From dfb191a16afe47d18b0dd9a4988582b0acf4d97a Mon Sep 17 00:00:00 2001 From: taesub kim Date: Mon, 12 Jun 2017 13:27:48 +0900 Subject: [PATCH] Imported Upstream version 765.50.9 Change-Id: I2003f412472c4565a91b05d44dad6dd35401d127 --- .gitignore | 7 + Clients/ClientCommon.c | 9 +- Clients/Java/nmakefile | 2 +- Clients/dns-sd.c | 658 ++-- Clients/dnsctl.c | 303 +- .../52D711AF-4055-4867-A494-7E31552BB9E1.png | Bin 0 -> 153759 bytes .../Screen Shot 2015-09-16 at 3.36.23 PM.png | Bin 0 -> 6231 bytes .../Screen Shot 2015-09-16 at 3.46.14 PM.png | Bin 0 -> 6910 bytes .../Attach mDNSResponder to Xcode.rtfd/TXT.rtf | 75 + .../Attach mDNSResponder to Xcode.rtfd/unknown.png | Bin 0 -> 55910 bytes .../A944EB40-AEFD-4CA1-BF10-E8F52835CA8C.png | Bin 0 -> 90282 bytes .../Screen Shot 2015-09-16 at 4.22.37 PM.png | Bin 0 -> 8498 bytes .../Start mDNSResponder in Xcode.rtfd/TXT.rtf | 57 + Makefile | 3 +- mDNSCore/CryptoAlg.c | 2 +- mDNSCore/CryptoAlg.h | 3 +- mDNSCore/DNSCommon.c | 180 +- mDNSCore/DNSCommon.h | 39 +- mDNSCore/DNSDigest.c | 3 +- mDNSCore/anonymous.c | 46 +- mDNSCore/anonymous.h | 2 +- mDNSCore/dnsproxy.c | 122 +- mDNSCore/dnsproxy.h | 7 +- mDNSCore/dnssec.c | 28 +- mDNSCore/dnssec.h | 3 +- mDNSCore/mDNS.c | 2340 +++++++------ mDNSCore/mDNSDebug.h | 10 +- mDNSCore/mDNSEmbeddedAPI.h | 303 +- mDNSCore/nsec.c | 14 +- mDNSCore/nsec.h | 3 +- mDNSCore/nsec3.c | 32 +- mDNSCore/nsec3.h | 2 +- mDNSCore/uDNS.c | 864 +++-- mDNSCore/uDNS.h | 14 +- mDNSMacOS9/Mac OS Test Responder.c | 227 -- mDNSMacOS9/Mac OS Test Searcher.c | 243 -- mDNSMacOS9/README.txt | 48 - mDNSMacOS9/Responder.c | 183 - mDNSMacOS9/Searcher.c | 240 -- mDNSMacOS9/ShowInitIcon.c | 160 - mDNSMacOS9/ShowInitIcon.h | 20 - mDNSMacOS9/SubTypeTester.c | 217 -- mDNSMacOS9/mDNS.mcp | Bin 1002330 -> 0 bytes mDNSMacOS9/mDNSLibrary.c | 63 - mDNSMacOS9/mDNSLibraryLoader.c | 68 - mDNSMacOS9/mDNSLibraryResources.r | 197 -- mDNSMacOS9/mDNSMacOS9.c | 687 ---- mDNSMacOS9/mDNSMacOS9.h | 58 - mDNSMacOS9/mDNSPrefix.h | 64 - mDNSMacOSX/BLE.c | 853 +++++ mDNSMacOSX/BLE.h | 54 + mDNSMacOSX/BonjourEvents.c | 7 +- mDNSMacOSX/CUPolicy.c | 95 - mDNSMacOSX/CryptoSupport.c | 106 +- mDNSMacOSX/CryptoSupport.h | 3 +- mDNSMacOSX/DNSProxySupport.c | 31 +- mDNSMacOSX/DNSSECSupport.c | 19 +- mDNSMacOSX/DNSSECSupport.h | 2 +- mDNSMacOSX/DNSServiceDiscovery.c | 598 +--- mDNSMacOSX/DNSServiceDiscovery.h | 182 +- mDNSMacOSX/DNSServiceDiscoveryDefines.h | 4 +- mDNSMacOSX/DNSServiceDiscoveryReply.defs | 60 - mDNSMacOSX/DNSServiceDiscoveryRequest.defs | 80 - mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist | 18 - mDNSMacOSX/LaunchDaemonInfo-Tiger.plist | 17 - mDNSMacOSX/LegacyNATTraversal.c | 16 +- mDNSMacOSX/Metrics.h | 38 + mDNSMacOSX/Metrics.m | 1967 +++++++++++ mDNSMacOSX/P2PPacketFilter.c | 11 +- mDNSMacOSX/P2PPacketFilter.h | 5 +- mDNSMacOSX/PreferencePane/ConfigurationAuthority.c | 4 +- .../PreferencePane/DNSServiceDiscoveryPref.m | 28 +- .../PreferencePane/English.lproj/InfoPlist.strings | Bin 484 -> 219 bytes mDNSMacOSX/PreferencePane/PrivilegedOperations.c | 43 +- mDNSMacOSX/PreferencePane/ddnswriteconfig.m | 42 +- mDNSMacOSX/Private/com.apple.mDNSResponder.plist | 19 + mDNSMacOSX/Private/dns_sd_private.h | 57 + mDNSMacOSX/Private/dns_services.c | 248 +- mDNSMacOSX/Private/dns_services.h | 56 +- mDNSMacOSX/Private/dns_xpc.h | 32 +- mDNSMacOSX/Private/dnsctl_server.c | 224 ++ mDNSMacOSX/Private/xpc_services.c | 45 +- mDNSMacOSX/Private/xpc_services.h | 6 + mDNSMacOSX/SymptomReporter.c | 186 ++ mDNSMacOSX/base.xcconfig | 2 - ...nInfo.dnsextd.plist => com.apple.dnsextd.plist} | 2 - ...monInfo.plist => com.apple.mDNSResponder.plist} | 16 +- ...r.plist => com.apple.mDNSResponderHelper.plist} | 6 +- mDNSMacOSX/com.apple.networking.mDNSResponder | 1 - .../CarbonResource.r => mDNSMacOSX/coreBLE.h | 22 +- mDNSMacOSX/coreBLE.m | 371 +++ mDNSMacOSX/daemon.c | 2083 +++--------- mDNSMacOSX/dnsctl-entitlements.plist | 2 + mDNSMacOSX/helper-error.h | 49 - mDNSMacOSX/helper-main.c | 599 +++- mDNSMacOSX/helper-server.h | 5 +- mDNSMacOSX/helper-stubs.c | 828 +++-- mDNSMacOSX/helper.c | 3485 +++++++++----------- mDNSMacOSX/helper.h | 99 +- mDNSMacOSX/helpermsg-types.h | 4 +- mDNSMacOSX/helpermsg.defs | 143 - mDNSMacOSX/ipsec_strerror.h | 6 +- mDNSMacOSX/libpfkey.h | 6 +- mDNSMacOSX/mDNSMacOSX.c | 2505 ++++++++------ mDNSMacOSX/mDNSMacOSX.h | 22 +- mDNSMacOSX/mDNSResponder-entitlements.plist | 22 +- mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj | 2338 ------------- mDNSMacOSX/mDNSResponder.sb | 33 +- mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj | 1829 +++++----- .../xcshareddata/xcschemes/Build All.xcscheme | 80 + .../xcshareddata/xcschemes/mDNSResponder.xcscheme | 101 + mDNSMacOSX/mDNSResponderHelper.8 | 2 +- mDNSMacOSX/mDNSResponderLogging.mobileconfig | 57 - mDNSMacOSX/pfkey.c | 6 +- mDNSMacOSX/uDNSPathEvalulation.c | 155 + mDNSPosix/Identify.c | 6 +- mDNSPosix/Makefile | 14 +- mDNSPosix/Responder.c | 4 +- mDNSPosix/mDNSPosix.c | 136 +- mDNSPosix/mDNSUNP.c | 25 +- mDNSPosix/mdnsd.sh | 4 +- mDNSResponder.sln | 21 + mDNSShared/CommonServices.h | 6 + mDNSShared/DebugServices.c | 4 +- mDNSShared/GenLinkedList.c | 9 +- mDNSShared/PlatformCommon.c | 24 +- mDNSShared/dns-sd.1 | 2 +- mDNSShared/dns_sd.h | 199 +- mDNSShared/dnsextd.8 | 2 +- mDNSShared/dnsextd.c | 34 +- mDNSShared/dnsextd_lexer.l | 7 +- mDNSShared/dnsextd_parser.y | 33 +- mDNSShared/dnssd_clientlib.c | 4 +- mDNSShared/dnssd_clientshim.c | 2 +- mDNSShared/dnssd_clientstub.c | 168 +- mDNSShared/dnssd_ipc.c | 4 +- mDNSShared/dnssd_ipc.h | 8 +- mDNSShared/mDNSDebug.c | 9 +- mDNSShared/mDNSResponder.8 | 27 +- mDNSShared/uds_daemon.c | 894 +++-- mDNSShared/uds_daemon.h | 30 +- mDNSWindows/ControlPanel/ConfigPropertySheet.cpp | 4 +- mDNSWindows/ControlPanel/ControlPanelExe.cpp | 9 +- mDNSWindows/ControlPanel/FourthPage.cpp | 2 +- mDNSWindows/DLLStub/DLLStub.cpp | 4 +- mDNSWindows/DLLX/DNSSD.cpp | 2 +- .../Windows/Sources/ChooserDialog.cpp | 12 +- .../WindowsCE/Sources/BrowserDialog.cpp | 2 +- mDNSWindows/Java/makefile | 2 +- mDNSWindows/Java/makefile64 | 2 +- mDNSWindows/NSPTool/NSPTool.c | 8 +- mDNSWindows/SystemService/Service.c | 2 +- mDNSWindows/mDNSWin32.c | 235 +- mDNSWindows/mdnsNSP/mdnsNSP.c | 13 +- unittests/DNSMessageTest.c | 50 + unittests/DNSMessageTest.h | 8 + unittests/DomainNameTest.c | 28 + unittests/DomainNameTest.h | 7 + unittests/InterfaceTest.c | 19 + unittests/InterfaceTest.h | 9 + unittests/ResourceRecordTest.c | 60 + unittests/ResourceRecordTest.h | 10 + mDNSMacOSX/VPNService.c => unittests/main.c | 32 +- unittests/unittest.c | 105 + unittests/unittest.h | 84 + 165 files changed, 14913 insertions(+), 15113 deletions(-) create mode 100644 .gitignore create mode 100644 Documents/Attach mDNSResponder to Xcode.rtfd/52D711AF-4055-4867-A494-7E31552BB9E1.png create mode 100644 Documents/Attach mDNSResponder to Xcode.rtfd/Screen Shot 2015-09-16 at 3.36.23 PM.png create mode 100644 Documents/Attach mDNSResponder to Xcode.rtfd/Screen Shot 2015-09-16 at 3.46.14 PM.png create mode 100644 Documents/Attach mDNSResponder to Xcode.rtfd/TXT.rtf create mode 100644 Documents/Attach mDNSResponder to Xcode.rtfd/unknown.png create mode 100644 Documents/Start mDNSResponder in Xcode.rtfd/A944EB40-AEFD-4CA1-BF10-E8F52835CA8C.png create mode 100644 Documents/Start mDNSResponder in Xcode.rtfd/Screen Shot 2015-09-16 at 4.22.37 PM.png create mode 100644 Documents/Start mDNSResponder in Xcode.rtfd/TXT.rtf delete mode 100644 mDNSMacOS9/Mac OS Test Responder.c delete mode 100644 mDNSMacOS9/Mac OS Test Searcher.c delete mode 100644 mDNSMacOS9/README.txt delete mode 100644 mDNSMacOS9/Responder.c delete mode 100644 mDNSMacOS9/Searcher.c delete mode 100755 mDNSMacOS9/ShowInitIcon.c delete mode 100755 mDNSMacOS9/ShowInitIcon.h delete mode 100644 mDNSMacOS9/SubTypeTester.c delete mode 100644 mDNSMacOS9/mDNS.mcp delete mode 100644 mDNSMacOS9/mDNSLibrary.c delete mode 100644 mDNSMacOS9/mDNSLibraryLoader.c delete mode 100644 mDNSMacOS9/mDNSLibraryResources.r delete mode 100644 mDNSMacOS9/mDNSMacOS9.c delete mode 100755 mDNSMacOS9/mDNSMacOS9.h delete mode 100644 mDNSMacOS9/mDNSPrefix.h create mode 100644 mDNSMacOSX/BLE.c create mode 100644 mDNSMacOSX/BLE.h delete mode 100644 mDNSMacOSX/CUPolicy.c delete mode 100644 mDNSMacOSX/DNSServiceDiscoveryReply.defs delete mode 100644 mDNSMacOSX/DNSServiceDiscoveryRequest.defs delete mode 100644 mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist delete mode 100644 mDNSMacOSX/LaunchDaemonInfo-Tiger.plist create mode 100644 mDNSMacOSX/Metrics.h create mode 100644 mDNSMacOSX/Metrics.m create mode 100644 mDNSMacOSX/Private/com.apple.mDNSResponder.plist create mode 100644 mDNSMacOSX/Private/dns_sd_private.h create mode 100644 mDNSMacOSX/Private/dnsctl_server.c create mode 100644 mDNSMacOSX/SymptomReporter.c delete mode 100644 mDNSMacOSX/base.xcconfig rename mDNSMacOSX/{LaunchDaemonInfo.dnsextd.plist => com.apple.dnsextd.plist} (92%) rename mDNSMacOSX/{LaunchDaemonInfo.plist => com.apple.mDNSResponder.plist} (69%) rename mDNSMacOSX/{LaunchDaemonInfo.helper.plist => com.apple.mDNSResponderHelper.plist} (79%) delete mode 100644 mDNSMacOSX/com.apple.networking.mDNSResponder rename mDNSMacOS9/CarbonResource.r => mDNSMacOSX/coreBLE.h (57%) create mode 100644 mDNSMacOSX/coreBLE.m delete mode 100644 mDNSMacOSX/helper-error.h delete mode 100644 mDNSMacOSX/helpermsg.defs delete mode 100644 mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj create mode 100644 mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme create mode 100644 mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/mDNSResponder.xcscheme delete mode 100644 mDNSMacOSX/mDNSResponderLogging.mobileconfig create mode 100644 mDNSMacOSX/uDNSPathEvalulation.c create mode 100644 unittests/DNSMessageTest.c create mode 100644 unittests/DNSMessageTest.h create mode 100644 unittests/DomainNameTest.c create mode 100644 unittests/DomainNameTest.h create mode 100644 unittests/InterfaceTest.c create mode 100644 unittests/InterfaceTest.h create mode 100644 unittests/ResourceRecordTest.c create mode 100644 unittests/ResourceRecordTest.h rename mDNSMacOSX/VPNService.c => unittests/main.c (52%) create mode 100644 unittests/unittest.c create mode 100644 unittests/unittest.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..119688d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +mDNSMacOSX/*.xcodeproj/project.xcworkspace +mDNSMacOSX/*.xcodeproj/xcuserdata +.svn +build +*~.m +*~.c +*~.h diff --git a/Clients/ClientCommon.c b/Clients/ClientCommon.c index 68f354c..f96319c 100644 --- a/Clients/ClientCommon.c +++ b/Clients/ClientCommon.c @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2008-2011 Apple Inc. All rights reserved. * - * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 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, @@ -16,7 +16,7 @@ * 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 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 @@ -49,8 +49,9 @@ const char *GetNextLabel(const char *cstr, char label[64]) while (*cstr && *cstr != '.') // While we have characters in the label... { char c = *cstr++; - if (c == '\\' && *cstr) // If we have a backslash, and it's not the last character of the string + if (c == '\\') // If escape character, check next character { + if (*cstr == '\0') break; // If this is the end of the string, then break c = *cstr++; if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1])) { diff --git a/Clients/Java/nmakefile b/Clients/Java/nmakefile index 89168e0..1758f7a 100644 --- a/Clients/Java/nmakefile +++ b/Clients/Java/nmakefile @@ -29,7 +29,7 @@ ############################################################################ -JDK = $(JAVA_HOME) +JDK = "$(JAVA_HOME)" CP = copy RM = del /Q diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 9acf41e..f36211e 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -1,8 +1,8 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Inc. All rights reserved. + * Copyright (c) 2002-2015 Apple Inc. All rights reserved. * - * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 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, @@ -16,7 +16,7 @@ * 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 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 @@ -57,14 +57,6 @@ // aren't in the system's /usr/lib/libSystem.dylib. //#define TEST_NEW_CLIENTSTUB 1 -// When building mDNSResponder for Mac OS X 10.4 and earlier, /usr/lib/libSystem.dylib is built using its own private -// copy of dnssd_clientstub.c, which is old and doesn't have all the entry points defined in the latest version, so -// when we're building dns-sd.c on Mac OS X 10.4 or earlier, we automatically set TEST_NEW_CLIENTSTUB so that we'll -// embed a copy of the latest dnssd_clientstub.c instead of trying to link to the incomplete version in libSystem.dylib -#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1040 -#define TEST_NEW_CLIENTSTUB 1 -#endif - #include #include // For stdout, stderr #include // For exit() @@ -185,14 +177,6 @@ static const char kFilePathSep = '/'; #include #endif -// The "+0" is to cope with the case where _DNS_SD_H is defined but empty (e.g. on Mac OS X 10.4 and earlier) -#if _DNS_SD_H+0 >= 116 -#define HAS_NAT_PMP_API 1 -#define HAS_ADDRINFO_API 1 -#else -#define kDNSServiceFlagsReturnIntermediates 0 -#endif - //************************************************************************************************************* // Globals @@ -336,18 +320,70 @@ static char *DNSTypeName(unsigned short rr_type) { switch (rr_type) { - case kDNSServiceType_A: return("Addr"); - case kDNSServiceType_NS: return("NS"); - case kDNSServiceType_MX: return("MX"); - case kDNSServiceType_CNAME: return("CNAME"); - case kDNSServiceType_SOA: return("SOA"); - case kDNSServiceType_PTR: return("PTR"); - case kDNSServiceType_AAAA: return("AAAA"); - case kDNSServiceType_NSEC: return("NSEC"); - case kDNSServiceType_TSIG: return("TSIG"); - case kDNSServiceType_RRSIG: return("RRSIG"); - case kDNSServiceType_DNSKEY: return("DNSKEY"); - case kDNSServiceType_DS: return("DS"); + case kDNSServiceType_A: return("Addr"); + case kDNSServiceType_NS: return("NS"); + case kDNSServiceType_MD: return("MD"); + case kDNSServiceType_MF: return("MF"); + case kDNSServiceType_CNAME: return("CNAME"); + case kDNSServiceType_SOA: return("SOA"); + case kDNSServiceType_MB: return("MB"); + case kDNSServiceType_MG: return("MG"); + case kDNSServiceType_MR: return("MR"); + case kDNSServiceType_NULL: return("NULL"); + case kDNSServiceType_WKS: return("WKS"); + case kDNSServiceType_PTR: return("PTR"); + case kDNSServiceType_HINFO: return("HINFO"); + case kDNSServiceType_MINFO: return("MINFO"); + case kDNSServiceType_MX: return("MX"); + case kDNSServiceType_TXT: return("TXT"); + case kDNSServiceType_RP: return("RP"); + case kDNSServiceType_AFSDB: return("AFSDB"); + case kDNSServiceType_X25: return("X25"); + case kDNSServiceType_ISDN: return("ISDN"); + case kDNSServiceType_RT: return("RT"); + case kDNSServiceType_NSAP: return("NSAP"); + case kDNSServiceType_NSAP_PTR: return("NSAP_PTR"); + case kDNSServiceType_SIG: return("SIG"); + case kDNSServiceType_KEY: return("KEY"); + case kDNSServiceType_PX: return("PX"); + case kDNSServiceType_GPOS: return("GPOS"); + case kDNSServiceType_AAAA: return("AAAA"); + case kDNSServiceType_LOC: return("LOC"); + case kDNSServiceType_NXT: return("NXT"); + case kDNSServiceType_EID: return("EID"); + case kDNSServiceType_NIMLOC: return("NIMLOC"); + case kDNSServiceType_SRV: return("SRV"); + case kDNSServiceType_ATMA: return("ATMA"); + case kDNSServiceType_NAPTR: return("NAPTR"); + case kDNSServiceType_KX: return("KX"); + case kDNSServiceType_CERT: return("CERT"); + case kDNSServiceType_A6: return("A6"); + case kDNSServiceType_DNAME: return("DNAME"); + case kDNSServiceType_SINK: return("SINK"); + case kDNSServiceType_OPT: return("OPT"); + case kDNSServiceType_APL: return("APL"); + case kDNSServiceType_DS: return("DS"); + case kDNSServiceType_SSHFP: return("SSHFP"); + case kDNSServiceType_IPSECKEY: return("IPSECKEY"); + case kDNSServiceType_RRSIG: return("RRSIG"); + case kDNSServiceType_NSEC: return("NSEC"); + case kDNSServiceType_DNSKEY: return("DNSKEY"); + case kDNSServiceType_DHCID: return("DHCID"); + case kDNSServiceType_NSEC3: return("NSEC3"); + case kDNSServiceType_NSEC3PARAM: return("NSEC3PARAM"); + case kDNSServiceType_HIP: return("HIP"); + case kDNSServiceType_SPF: return("SPF"); + case kDNSServiceType_UINFO: return("UINFO"); + case kDNSServiceType_UID: return("UID"); + case kDNSServiceType_GID: return("GID"); + case kDNSServiceType_UNSPEC: return("UNSPEC"); + case kDNSServiceType_TKEY: return("TKEY"); + case kDNSServiceType_TSIG: return("TSIG"); + case kDNSServiceType_IXFR: return("IXFR"); + case kDNSServiceType_AXFR: return("AXFR"); + case kDNSServiceType_MAILB: return("MAILB"); + case kDNSServiceType_MAILA: return("MAILA"); + case kDNSServiceType_ANY: return("ANY"); default: { static char buffer[RR_TYPE_SIZE]; @@ -421,7 +457,6 @@ done: #endif //_DNS_SD_LIBDISPATCH } -#if HAS_NAT_PMP_API | HAS_ADDRINFO_API static DNSServiceProtocol GetProtocol(const char *s) { if (!strcasecmp(s, "v4" )) return(kDNSServiceProtocol_IPv4); @@ -434,13 +469,14 @@ static DNSServiceProtocol GetProtocol(const char *s) else if (!strcasecmp(s, "tcpudp" )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP); else return(atoi(s)); } -#endif //************************************************************************************************************* // Sample callback functions for each of the operation types -static void printtimestamp(void) +#define printtimestamp() printtimestamp_F(stdout) + +static void printtimestamp_F(FILE *outstream) { struct tm tm; int ms; @@ -461,10 +497,10 @@ static void printtimestamp(void) strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm); if (strncmp(date, new_date, sizeof(new_date))) { - printf("DATE: ---%s---\n", new_date); //display date only if it has changed + fprintf(outstream, "DATE: ---%s---\n", new_date); //display date only if it has changed strncpy(date, new_date, sizeof(date)); } - printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); + fprintf(outstream, "%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); } // formating time to RFC 4034 format @@ -485,45 +521,50 @@ static void FormatTime(unsigned long te, unsigned char *buf, int bufsize) static void print_usage(const char *arg0, int print_all) { + // Print the commonly used command line options. These are listed in "the order they have been in historically". fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", arg0); fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", arg0); fprintf(stderr, "%s -R [...] (Register a service)\n", arg0); - fprintf(stderr, "%s -B (Browse for services instances)\n", arg0); - fprintf(stderr, "%s -L (Look up a service instance)\n", arg0); - fprintf(stderr, "%s -P [...] (Proxy)\n", arg0); - fprintf(stderr, "%s -q (Generic query for any record type)\n", arg0); - fprintf(stderr, "%s -D (Validate query for any record type with DNSSEC)\n", arg0); + fprintf(stderr, "%s -B (Browse for service instances)\n", arg0); + fprintf(stderr, "%s -L (Resolve a service instance)\n", arg0); + fprintf(stderr, "%s -Q (Generic query for any record type)\n", arg0); fprintf(stderr, "%s -Z (Output results in Zone File format)\n", arg0); -#if HAS_ADDRINFO_API fprintf(stderr, "%s -G v4/v6/v4v6 (Get address information for hostname)\n", arg0); - fprintf(stderr, "%s -g v4/v6/v4v6 (Validate address info for hostname with DNSSEC)\n", arg0); -#endif + fprintf(stderr, "%s -H (Print usage for complete command list)\n", arg0); fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", arg0); - if (print_all) //Print all available options for dns-sd tool + if (print_all) // Print all available options for dns-sd tool. Keep these in alphabetical order for easier maintenance. { - fprintf(stderr, "%s -C (Query; reconfirming each result)\n", arg0); -#if HAS_NAT_PMP_API - fprintf(stderr, "%s -X udp/tcp/udptcp (NAT Port Mapping)\n", arg0); -#endif + fprintf(stderr, "\n"); fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", arg0); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); + fprintf(stderr, "%s -C (Query; reconfirming each result)\n", arg0); + fprintf(stderr, "%s -D (Validate query for any record type with DNSSEC)\n", arg0); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); fprintf(stderr, "%s -N (Test adding a large NULL record)\n", arg0); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", arg0); - fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); + fprintf(stderr, "%s -P [...] (Proxy)\n", arg0); fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", arg0); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); + fprintf(stderr, "%s -X udp/tcp/udptcp (NAT Port Mapping)\n", arg0); + fprintf(stderr, "%s -ble (Use kDNSServiceInterfaceIndexBLE)\n", arg0); + fprintf(stderr, "%s -g v4/v6/v4v6 (Validate address info for hostname with DNSSEC)\n", arg0); fprintf(stderr, "%s -i (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0); - fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); - fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); fprintf(stderr, "%s -includep2p (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0); fprintf(stderr, "%s -includeAWDL (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0); + fprintf(stderr, "%s -intermediates (Set kDNSServiceFlagsReturnIntermediates flag)\n", arg0); + fprintf(stderr, "%s -ku (Set kDNSServiceFlagsKnownUnique flag)\n", arg0); + fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); fprintf(stderr, "%s -optional (Set kDNSServiceFlagsValidateOptional flag)\n", arg0); + fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); + fprintf(stderr, "%s -q (Equivalent to -Q with kDNSServiceFlagsSuppressUnusable set)\n", arg0); fprintf(stderr, "%s -tc (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0); - fprintf(stderr, "%s -unicastResponse (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0); + fprintf(stderr, "%s -test (Run basic API input range tests)\n", arg0); fprintf(stderr, "%s -t1 (Set kDNSServiceFlagsThresholdOne flag)\n", arg0); fprintf(stderr, "%s -tFinder (Set kDNSServiceFlagsThresholdFinder flag)\n", arg0); fprintf(stderr, "%s -timeout (Set kDNSServiceFlagsTimeout flag)\n", arg0); + fprintf(stderr, "%s -unicastResponse (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0); + fprintf(stderr, "%s -autoTrigger (Set kDNSServiceFlagsAutoTrigger flag)\n", arg0); } } @@ -753,17 +794,20 @@ static void DNSSD_API resolve_reply(DNSServiceRef sdref, const DNSServiceFlags f (void)context; // Unused EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - if (errorCode) - printf("Error code %d\n", errorCode); - else - { - printtimestamp(); - printf("%s can be reached at %s:%u (interface %d)", fullname, hosttarget, PortAsNumber, ifIndex); - if (flags) printf(" Flags: %X", flags); - // Don't show degenerate TXT records containing nothing but a single empty string - if (txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); } - printf("\n"); - } + printtimestamp(); + + printf("%s ", fullname); + + if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record"); + else if (errorCode) printf("error code %d\n", errorCode); + else printf("can be reached at %s:%u (interface %d)", hosttarget, PortAsNumber, ifIndex); + + if (flags) printf(" Flags: %X", flags); + + // Don't show degenerate TXT records containing nothing but a single empty string + if (!errorCode && txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); } + + printf("\n"); if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } @@ -798,6 +842,7 @@ static void myTimerCallBack(void) { if (updatetest[1] != 'Z') updatetest[1]++; else updatetest[1] = 'A'; + // The following line toggles the string length between 1 and 2 characters. updatetest[0] = 3 - updatetest[0]; updatetest[2] = updatetest[1]; printtimestamp(); @@ -1041,15 +1086,15 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, case kDNSServiceType_CNAME: case kDNSServiceType_PTR: case kDNSServiceType_DNAME: - p += snprintd(p, sizeof(rdb), &rd); + snprintd(p, sizeof(rdb), &rd); break; case kDNSServiceType_SOA: p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname p += snprintf(p, rdb + sizeof(rdb) - p, " "); p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname - p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", - ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); + snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", + ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); break; case kDNSServiceType_AAAA: @@ -1060,9 +1105,9 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, case kDNSServiceType_SRV: p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port - ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); + ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); rd += 6; - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host + snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host break; case kDNSServiceType_DS: @@ -1124,7 +1169,6 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, fflush(stdout); } -#if HAS_NAT_PMP_API static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, uint32_t publicAddress, uint32_t protocol, uint16_t privatePort, uint16_t publicPort, uint32_t ttl, void *context) { (void)sdref; // Unused @@ -1146,9 +1190,7 @@ static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceF if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } -#endif -#if HAS_ADDRINFO_API static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context) { char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; @@ -1218,7 +1260,6 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } -#endif //************************************************************************************************************* // The main test function @@ -1275,7 +1316,7 @@ static void HandleEvents(void) DNSServiceErrorType err = kDNSServiceErr_NoError; if (client && FD_ISSET(dns_sd_fd, &readfds)) err = DNSServiceProcessResult(client ); else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa); - if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } + if (err) { printtimestamp_F(stderr); fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; } } else if (result == 0) myTimerCallBack(); @@ -1332,18 +1373,6 @@ static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordR default: printf("Error %d\n", errorCode); break; } if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); - // DNSServiceRemoveRecord(service, rec, 0); to test record removal - -#if 0 // To test updating of individual records registered via DNSServiceRegisterRecord - if (!errorCode) - { - int x = 0x11111111; - printf("Updating\n"); - DNSServiceUpdateRecord(service, rec, 0, sizeof(x), &x, 0); - } -#endif - - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); } static void getip(const char *const name, struct sockaddr_storage *result) @@ -1362,6 +1391,7 @@ static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const // Any DNSService* call will initialize WinSock for us, so we make sure // DNSServiceCreateConnection() is called before getip() is. struct sockaddr_storage hostaddr; + memset(&hostaddr, 0, sizeof(hostaddr)); getip(ip, &hostaddr); flags |= kDNSServiceFlagsUnique; if (hostaddr.ss_family == AF_INET) @@ -1428,6 +1458,215 @@ static char *gettype(char *buffer, char *typ) return(typ); } +// Do some basic tests to verify API handles > 63 byte strings gracefully with +// a returned error code. + +#define STRING_64_BYTES "_123456789012345678901234567890123456789012345678901234567890123" + +static int API_string_limit_test() +{ + const char * regtype; + DNSServiceRef sdRef = NULL; + const char * longHost = STRING_64_BYTES ".local"; + const char * longDomain = "hostname." STRING_64_BYTES; + + printf("Testing for error returns when various strings are > 63 bytes.\n"); + + printf("DNSServiceGetAddrInfo(), hostname = %s\n", longHost); + if (DNSServiceGetAddrInfo(&sdRef, 0, 0, 0, longHost, addrinfo_reply, 0) == 0) + { + printf("DNSServiceGetAddrInfo(): expected error return\n"); + return 1; + }; + + printf("DNSServiceGetAddrInfo(), hostname = %s\n", longDomain); + if (DNSServiceGetAddrInfo(&sdRef, 0, 0, 0, longDomain, addrinfo_reply, 0) == 0) + { + printf("DNSServiceGetAddrInfo(): expected error return\n"); + return 1; + }; + + printf("DNSServiceResolve(), name = %s\n", STRING_64_BYTES); + if (DNSServiceResolve(&sdRef, 0, 0, STRING_64_BYTES, "_test._tcp", "local", resolve_reply, NULL) == 0) + { + printf("DNSServiceResolve(): expected error return\n"); + return 1; + }; + + regtype = STRING_64_BYTES "._tcp"; + printf("DNSServiceResolve(), regtype = %s\n", regtype); + if (DNSServiceResolve(&sdRef, 0, 0, "instanceName", regtype, "local", resolve_reply, NULL) == 0) + { + printf("DNSServiceResolve(): expected error return\n"); + return 1; + }; + + printf("DNSServiceResolve(), domain = %s\n", STRING_64_BYTES); + if (DNSServiceResolve(&sdRef, 0, 0, "instanceName", "_test._tcp", STRING_64_BYTES, resolve_reply, NULL) == 0) + { + printf("DNSServiceResolve(): expected error return\n"); + return 1; + }; + + printf("Testing for error returns when various strings are > 63 bytes: PASSED\n"); + return 0; +} + +// local prototypes for routines that don't have prototypes in dns_sd.h +#if APPLE_OSX_mDNSResponder +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain); +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid); +#endif + +static int API_NULL_input_test() +{ + printf("Running basic API input range tests with all parameters set to 0:\n"); + + // Test that API's handle NULL pointers by returning an error when appropriate. + printf("DNSServiceRefSockFD()\n"); + if (DNSServiceRefSockFD(0) != -1) + { + printf("DNSServiceRefSockFD(): expected dnssd_InvalidSocket return\n"); + return 1; + }; + + printf("DNSServiceProcessResult()\n"); + if (DNSServiceProcessResult(0) == 0) + { + printf("DNSServiceProcessResult(): expected error return\n"); + return 1; + }; + + // no return value, just verify it doesn't crash + printf("DNSServiceRefDeallocate()\n"); + DNSServiceRefDeallocate(0); + + printf("DNSServiceGetProperty()\n"); + if (DNSServiceGetProperty(0, 0, 0) == 0) + { + printf("DNSServiceGetProperty(): expected error return\n"); + return 1; + }; + + printf("DNSServiceResolve()\n"); + if (DNSServiceResolve(0, 0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceResolve(): expected error return\n"); + return 1; + }; + + printf("DNSServiceQueryRecord()\n"); + if (DNSServiceQueryRecord(0, 0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceQueryRecord(): expected error return\n"); + return 1; + }; + + printf("DNSServiceGetAddrInfo()\n"); + if (DNSServiceGetAddrInfo(0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceGetAddrInfo(): expected error return\n"); + return 1; + }; + + printf("DNSServiceBrowse()\n"); + if (DNSServiceBrowse(0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceBrowse(): expected error return\n"); + return 1; + }; + +#if APPLE_OSX_mDNSResponder + printf("DNSServiceSetDefaultDomainForUser()\n"); + if (DNSServiceSetDefaultDomainForUser(0, 0) == 0) + { + printf("DNSServiceSetDefaultDomainForUser(): expected error return\n"); + return 1; + }; +#endif + + printf("DNSServiceRegister()\n"); + if (DNSServiceRegister(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceRegister(): expected error return\n"); + return 1; + }; + + printf("DNSServiceEnumerateDomains()\n"); + if (DNSServiceEnumerateDomains(0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceEnumerateDomains(): expected error return\n"); + return 1; + }; + + printf("DNSServiceCreateConnection()\n"); + if (DNSServiceCreateConnection(0) == 0) + { + printf("DNSServiceCreateConnection(): expected error return\n"); + return 1; + }; + +#if APPLE_OSX_mDNSResponder + printf("DNSServiceCreateDelegateConnection()\n"); + if (DNSServiceCreateDelegateConnection(0, 0, 0) == 0) + { + printf("DNSServiceCreateDelegateConnection(): expected error return\n"); + return 1; + }; +#endif + + printf("DNSServiceRegisterRecord()\n"); + if (DNSServiceRegisterRecord(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceRegisterRecord(): expected error return\n"); + return 1; + }; + + printf("DNSServiceAddRecord()\n"); + if (DNSServiceAddRecord(0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceAddRecord(): expected error return\n"); + return 1; + }; + + printf("DNSServiceUpdateRecord()\n"); + if (DNSServiceUpdateRecord(0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceUpdateRecord(): expected error return\n"); + return 1; + }; + + printf("DNSServiceRemoveRecord()\n"); + if (DNSServiceRemoveRecord(0, 0, 0) == 0) + { + printf("DNSServiceRemoveRecord(): expected error return\n"); + return 1; + }; + + printf("DNSServiceReconfirmRecord()\n"); + if (DNSServiceReconfirmRecord(0, 0, 0, 0, 0, 0, 0) == 0) + { + printf("DNSServiceReconfirmRecord(): expected error return\n"); + return 1; + }; + + + printf("Basic API input range tests with all parameters set to 0: PASSED\n"); + return 0; +} + +static int API_input_range_test() +{ + + if (API_string_limit_test()) + return 1; + + if (API_NULL_input_test()) + return 1; + + return 0; +} + int main(int argc, char **argv) { DNSServiceErrorType err; @@ -1457,108 +1696,156 @@ int main(int argc, char **argv) //TXTRecordSetValue(&txtRecord, "aaa", 1, "b"); //printf("%d\n", TXTRecordContainsKey(TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), "Aaa")); - if (argc > 1 && !strcmp(argv[1], "-lo")) - { - argc--; - argv++; - opinterface = kDNSServiceInterfaceIndexLocalOnly; - printf("Using LocalOnly\n"); - } - - if (argc > 1 && (!strcmp(argv[1], "-p2p") || !strcmp(argv[1], "-P2P"))) - { - argc--; - argv++; - opinterface = kDNSServiceInterfaceIndexP2P; - } - - if (argc > 1 && !strcasecmp(argv[1], "-includep2p")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsIncludeP2P; - printf("Setting kDNSServiceFlagsIncludeP2P\n"); - } - - if (argc > 1 && !strcasecmp(argv[1], "-includeAWDL")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsIncludeAWDL; - printf("Setting kDNSServiceFlagsIncludeAWDL\n"); - } - - if (argc > 1 && !strcasecmp(argv[1], "-tc")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsBackgroundTrafficClass; - printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n"); - } - - if (argc > 1 && !strcasecmp(argv[1], "-t1")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsThresholdOne; - printf("Setting kDNSServiceFlagsThresholdOne\n"); - } - - if (argc > 1 && !strcasecmp(argv[1], "-tFinder")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsThresholdFinder; - printf("Setting kDNSServiceFlagsThresholdFinder\n"); - } - - if (argc > 1 && !strcasecmp(argv[1], "-wo")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsWakeOnlyService; - printf("Setting kDNSServiceFlagsWakeOnlyService\n"); - } - - if (argc > 1 && !strcasecmp(argv[1], "-unicastResponse")) + while (argc > 1) { - argc--; - argv++; - flags |= kDNSServiceFlagsUnicastResponse; - printf("Setting kDNSServiceFlagsUnicastResponse\n"); - } - if (argc > 1 && !strcasecmp(argv[1], "-timeout")) - { - argc--; - argv++; - flags |= kDNSServiceFlagsTimeout; - printf("Setting kDNSServiceFlagsTimeout\n"); - } - if (argc > 1 && !strcasecmp(argv[1], "-optional")) - { - argc--; - argv++; - optional = 1; - printf("Setting DNSSEC optional flag\n"); - } + int entryCount; + + // record current argc to see if we process an argument in this pass + entryCount = argc; + + if (argc > 1 && !strcmp(argv[1], "-test")) + { + argc--; + argv++; + return API_input_range_test(); + } + + if (argc > 1 && !strcmp(argv[1], "-lo")) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexLocalOnly; + printf("Using LocalOnly\n"); + } + + if (argc > 1 && (!strcasecmp(argv[1], "-p2p"))) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexP2P; + } + + if (argc > 1 && (!strcasecmp(argv[1], "-ble"))) + { + argc--; + argv++; + opinterface = kDNSServiceInterfaceIndexBLE; + } + + if (argc > 1 && !strcasecmp(argv[1], "-includep2p")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsIncludeP2P; + printf("Setting kDNSServiceFlagsIncludeP2P\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-includeAWDL")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsIncludeAWDL; + printf("Setting kDNSServiceFlagsIncludeAWDL\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-intermediates")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsReturnIntermediates; + printf("Setting kDNSServiceFlagsReturnIntermediates\n"); + } - if (argc > 2 && !strcmp(argv[1], "-i")) - { - opinterface = if_nametoindex(argv[2]); - if (!opinterface) opinterface = atoi(argv[2]); - if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; } - argc -= 2; - argv += 2; + if (argc > 1 && !strcasecmp(argv[1], "-tc")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsBackgroundTrafficClass; + printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-t1")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsThresholdOne; + printf("Setting kDNSServiceFlagsThresholdOne\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-tFinder")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsThresholdFinder; + printf("Setting kDNSServiceFlagsThresholdFinder\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-wo")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsWakeOnlyService; + printf("Setting kDNSServiceFlagsWakeOnlyService\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-ku")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsKnownUnique; + printf("Setting kDNSServiceFlagsKnownUnique\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-unicastResponse")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsUnicastResponse; + printf("Setting kDNSServiceFlagsUnicastResponse\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-timeout")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsTimeout; + printf("Setting kDNSServiceFlagsTimeout\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-autoTrigger")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsAutoTrigger; + printf("Setting kDNSServiceFlagsAutoTrigger\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-optional")) + { + argc--; + argv++; + optional = 1; + printf("Setting DNSSEC optional flag\n"); + } + + if (argc > 2 && !strcmp(argv[1], "-i")) + { + opinterface = if_nametoindex(argv[2]); + if (!opinterface) opinterface = atoi(argv[2]); + if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; } + argc -= 2; + argv += 2; + } + + // Exit loop if if we didn't match one of the multi character options. + if (argc == entryCount) + break; } if (argc < 2) goto Fail; // Minimum command line is the command name and one argument - operation = getfirstoption(argc, argv, "EFBZLlRPQqCAUNTMISVHhD" - #if HAS_NAT_PMP_API + operation = getfirstoption(argc, argv, "ABCDEFHILMNPQRSTUVZhlq" "X" - #endif - #if HAS_ADDRINFO_API "Gg" - #endif , &opi); if (operation == -1) goto Fail; @@ -1592,6 +1879,7 @@ int main(int argc, char **argv) if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom); err = DNSServiceCreateConnection(&client); + if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } sc1 = client; err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL); break; @@ -1694,7 +1982,6 @@ int main(int argc, char **argv) break; } -#if HAS_NAT_PMP_API case 'X': { if (argc == opi) // If no arguments, just fetch IP address err = DNSServiceNATPortMappingCreate(&client, 0, 0, 0, 0, 0, 0, port_mapping_create_reply, NULL); @@ -1711,9 +1998,7 @@ int main(int argc, char **argv) else goto Fail; break; } -#endif -#if HAS_ADDRINFO_API case 'g': case 'G': { flags |= kDNSServiceFlagsReturnIntermediates; @@ -1731,7 +2016,6 @@ int main(int argc, char **argv) err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); break; } -#endif case 'S': { Opaque16 registerPort = { { 0x23, 0x45 } }; // 9029 decimal diff --git a/Clients/dnsctl.c b/Clients/dnsctl.c index bb2a071..e01c8fe 100644 --- a/Clients/dnsctl.c +++ b/Clients/dnsctl.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Inc. All rights reserved. + * Copyright (c) 2012-2015 Apple Inc. All rights reserved. * * dnsctl.c * Command-line tool using libdns_services.dylib @@ -17,38 +17,42 @@ #include #include // if_nametoindex() -#include #include "dns_services.h" +#include +#include "dns_xpc.h" //************************************************************************************************************* // Globals: //************************************************************************************************************* static const char kFilePathSep = '/'; + static DNSXConnRef ClientRef = NULL; +static xpc_connection_t dnsctl_conn = NULL; + //************************************************************************************************************* // Utility Funcs: //************************************************************************************************************* -static void printtimestamp(void) +static void printtimestamp(void) { - struct tm tm; - int ms; + struct tm tm; + int ms; static char date[16]; static char new_date[16]; - struct timeval tv; + struct timeval tv; gettimeofday(&tv, NULL); localtime_r((time_t*)&tv.tv_sec, &tm); ms = tv.tv_usec/1000; strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm); //display date only if it has changed if (strncmp(date, new_date, sizeof(new_date))) - { + { printf("DATE: ---%s---\n", new_date); - strncpy(date, new_date, sizeof(date)); - } - printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); + strlcpy(date, new_date, sizeof(date)); + } + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); } static void print_usage(const char *arg0) @@ -56,12 +60,31 @@ static void print_usage(const char *arg0) fprintf(stderr, "%s USAGE: \n", arg0); fprintf(stderr, "%s -DP Enable DNS Proxy with Default Parameters \n", arg0); fprintf(stderr, "%s -DP [-o ] [-i ] Enable DNS Proxy \n", arg0); + fprintf(stderr, "%s -L [1/2/3/4] Change mDNSResponder Logging Level \n", arg0); + fprintf(stderr, "%s -I Print mDNSResponder STATE INFO \n", arg0); +} + + +static bool DebugEnabled() +{ + return true; // keep this true to debug the XPC msgs +} + +static void DebugLog(const char *prefix, xpc_object_t o) +{ + if (!DebugEnabled()) + return; + + char *desc = xpc_copy_description(o); + printf("%s: %s \n", prefix, desc); + free(desc); } //************************************************************************************************************* // CallBack Funcs: //************************************************************************************************************* + // DNSXEnableProxy Callback from the Daemon static void dnsproxy_reply(DNSXConnRef connRef, DNSXErrorType errCode) { @@ -69,59 +92,108 @@ static void dnsproxy_reply(DNSXConnRef connRef, DNSXErrorType errCode) printtimestamp(); switch (errCode) { - case kDNSX_NoError : printf(" SUCCESS \n"); break; - case kDNSX_DictError : printf(" DICT ERROR \n"); break; + case kDNSX_NoError : printf(" SUCCESS \n"); + break; case kDNSX_DaemonNotRunning : printf(" NO DAEMON \n"); - DNSXRefDeAlloc(ClientRef); break; - case kDNSX_Engaged : printf(" ENGAGED \n"); - DNSXRefDeAlloc(ClientRef); break; + DNSXRefDeAlloc(ClientRef); break; + case kDNSX_BadParam : printf(" BAD PARAMETER \n"); + DNSXRefDeAlloc(ClientRef); break; + case kDNSX_Busy : printf(" BUSY \n"); + DNSXRefDeAlloc(ClientRef); break; case kDNSX_UnknownErr : - default : printf("UNKNOWN ERR \n"); - DNSXRefDeAlloc(ClientRef); break; + default : printf(" UNKNOWN ERR \n"); + DNSXRefDeAlloc(ClientRef); break; } fflush(NULL); - + } //************************************************************************************************************* +// XPC Funcs: +//************************************************************************************************************* -int main(int argc, char **argv) +static void Init_Connection(const char *servname) +{ + dnsctl_conn = xpc_connection_create_mach_service(servname, dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + + xpc_connection_set_event_handler(dnsctl_conn, ^(xpc_object_t event) + { + printf("InitConnection: [%s] \n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + }); + + xpc_connection_resume(dnsctl_conn); +} + +static void SendDictToServer(xpc_object_t msg) { - DNSXErrorType err; + + DebugLog("SendDictToServer Sending msg to Daemon", msg); + + xpc_connection_send_message_with_reply(dnsctl_conn, msg, dispatch_get_main_queue(), ^(xpc_object_t recv_msg) + { + xpc_type_t type = xpc_get_type(recv_msg); + + if (type == XPC_TYPE_DICTIONARY) + { + DebugLog("SendDictToServer Received reply msg from Daemon", recv_msg); + /* + // If we ever want to do something based on the reply of the daemon + switch (daemon_status) + { + default: + break; + } + */ + } + else + { + printf("SendDictToServer Received unexpected reply from daemon [%s]", + xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION)); + DebugLog("SendDictToServer Unexpected Reply contents", recv_msg); + } + exit(1); + }); +} - // Default i/p intf is lo0 and o/p intf is primary interface - IfIndex Ipintfs[MaxInputIf] = {1, 0, 0, 0, 0}; - IfIndex Opintf = kDNSIfindexAny; +//************************************************************************************************************* +int main(int argc, char **argv) +{ // Extract program name from argv[0], which by convention contains the path to this executable - const char *a0 = strrchr(argv[0], kFilePathSep) + 1; + const char *a0 = strrchr(argv[0], kFilePathSep) + 1; if (a0 == (const char *)1) a0 = argv[0]; - + // Must run as root - if (0 != geteuid()) - { - fprintf(stderr, "%s MUST run as root!!\n", a0); - exit(-1); + if (0 != geteuid()) + { + fprintf(stderr, "%s MUST run as root!!\n", a0); + exit(-1); } if ((sizeof(argv) == 8)) printf("dnsctl running in 64-bit mode\n"); else if ((sizeof(argv) == 4)) printf("dnsctl running in 32-bit mode\n"); - + // expects atleast one argument if (argc < 2) goto Usage; - - if ( !strcmp(argv[1], "-DP") || !strcmp(argv[1], "-dp") ) + + printtimestamp(); + if (!strcasecmp(argv[1], "-DP")) { + DNSXErrorType err; + // Default i/p intf is lo0 and o/p intf is primary interface + IfIndex Ipintfs[MaxInputIf] = {1, 0, 0, 0, 0}; + IfIndex Opintf = kDNSIfindexAny; + if (argc == 2) { - printtimestamp(); - printf("Proceeding to Enable DNSProxy on mDNSResponder with Default Parameters\n"); dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL); err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply); - } + if (err) + fprintf(stderr, "DNSXEnableProxy returned %d\n", err); + } else if (argc > 2) { argc--; @@ -129,17 +201,17 @@ int main(int argc, char **argv) if (!strcmp(argv[1], "-o")) { Opintf = if_nametoindex(argv[2]); - if (!Opintf) + if (!Opintf) Opintf = atoi(argv[2]); - if (!Opintf) - { - fprintf(stderr, "Could not parse o/p interface [%s]: Passing default primary \n", argv[2]); + if (!Opintf) + { + fprintf(stderr, "Could not parse o/p interface [%s]: Passing default primary \n", argv[2]); Opintf = kDNSIfindexAny; } argc -= 2; argv += 2; } - if (argc > 2 && !strcmp(argv[1], "-i")) + if (argc > 2 && !strcmp(argv[1], "-i")) { int i; argc--; @@ -148,31 +220,164 @@ int main(int argc, char **argv) { Ipintfs[i] = if_nametoindex(argv[1]); if (!Ipintfs[i]) - Ipintfs[i] = atoi(argv[1]); + Ipintfs[i] = atoi(argv[1]); if (!Ipintfs[i]) { - fprintf(stderr, "Could not parse i/p interface [%s]: Passing default lo0 \n", argv[2]); + fprintf(stderr, "Could not parse i/p interface [%s]: Passing default lo0 \n", argv[2]); Ipintfs[i] = 1; } argc--; argv++; } - } - printtimestamp(); - printf("Proceeding to Enable DNSProxy on mDNSResponder \n"); + } + printf("Enabling DNSProxy on mDNSResponder \n"); dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL); - err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply); + err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply); + if (err) + fprintf(stderr, "DNSXEnableProxy returned %d\n", err); + } + } + else if (!strcasecmp(argv[1], "-l")) + { + printf("Changing loglevel of mDNSResponder \n"); + Init_Connection(kDNSCTLService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + + if (argc == 2) + { + xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level1); + + SendDictToServer(dict); + xpc_release(dict); + dict = NULL; + } + else if (argc > 2) + { + argc--; + argv++; + switch (atoi(argv[1])) + { + case log_level1: + xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level1); + break; + + case log_level2: + xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level2); + break; + + case log_level3: + xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level3); + break; + + case log_level4: + xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level4); + break; + + default: + xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level1); + break; + } + SendDictToServer(dict); + xpc_release(dict); + dict = NULL; } } + else if(!strcasecmp(argv[1], "-i")) + { + printf("Get STATE INFO of mDNSResponder \n"); + Init_Connection(kDNSCTLService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kDNSStateInfo, full_state); + SendDictToServer(dict); + xpc_release(dict); + dict = NULL; + } + else if(!strcasecmp(argv[1], "-th")) + { + printf("Sending Test message to mDNSResponder to forward to mDNSResponderHelper\n"); + Init_Connection(kDNSCTLService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kmDNSResponderTests, test_helper_ipc); + SendDictToServer(dict); + xpc_release(dict); + dict = NULL; + } + else if(!strcasecmp(argv[1], "-tl")) + { + printf("Testing mDNSResponder Logging\n"); + Init_Connection(kDNSCTLService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kmDNSResponderTests, test_mDNS_log); + SendDictToServer(dict); + xpc_release(dict); + dict = NULL; + } else { goto Usage; } - - dispatch_main(); - + + dispatch_main(); + Usage: print_usage(a0); return 0; } +/* + +#include + +static int operation; + +static int getfirstoption(int argc, char **argv, const char *optstr, int *pOptInd) +{ + // Return the recognized option in optstr and the option index of the next arg. + int o = getopt(argc, (char *const *)argv, optstr); + *pOptInd = optind; + return o; +} + +int opindex; +operation = getfirstoption(argc, argv, "lLDdPp", &opindex); +if (operation == -1) + goto Usage; + + + +switch (operation) +{ + case 'L': + case 'l': + { + printtimestamp(); + printf("Change Verbosity Level of mDNSResponder\n"); + + Init_Connection(kDNSCTLService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + if (dict == NULL) + printf("could not create the Msg Dict To Send! \n"); + xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level2); + + SendDictToServer(dict); + + xpc_release(dict); + dict = NULL; + break; + } + // exit(1); + +} + +*/ + diff --git a/Documents/Attach mDNSResponder to Xcode.rtfd/52D711AF-4055-4867-A494-7E31552BB9E1.png b/Documents/Attach mDNSResponder to Xcode.rtfd/52D711AF-4055-4867-A494-7E31552BB9E1.png new file mode 100644 index 0000000000000000000000000000000000000000..38eef1246253ac03e38aed7d700114b54785f699 GIT binary patch literal 153759 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 literal 0 HcmV?d00001 diff --git a/Documents/Attach mDNSResponder to Xcode.rtfd/Screen Shot 2015-09-16 at 3.36.23 PM.png b/Documents/Attach mDNSResponder to Xcode.rtfd/Screen Shot 2015-09-16 at 3.36.23 PM.png new file mode 100644 index 0000000000000000000000000000000000000000..40426447e2703a78ba5be2df5afa94e829d465fc GIT binary patch literal 6231 zcmZX1WmuG7_w_I!;Lu1YIWQn4-9rh|B`7g;$B+X<3>}gxB`KW)N=bu5cXt>_gLJ0? z{`tlGJkNE#_lNu1XYaMvK6`&!Ct6EWi3mgk0ssI+D$4RN006AuyOzeVyN)xVCGFw6wndNnmdY}imC&6e$PNB$+HT~$$7vWces$zaIz8b-gV#g_G13P z|6(3#dy5t9A2aZit&1E`cqo9=VOnw8Ut$m4_YTIg2S;4jrj>cLs%gbDUVgt6!_RHn zp)Y7M43gPzi(rx9$+`!i2tz!P$Vv`9gtHjI{AF>~M&);5PfQ4*b#z(U!u(v391 zF>WNC#Mlp+-Y{GYlRqOs^8Ek>0sy`V**sOJ!u#+{5#rQoh4jHU0`K;#nlqooC-}AV zv9fx`k}#Fg{^s=*`=ckQr;|9a7KOSs3B*~*${2layK$W}bkqi6HE86EguYwD3GqIh z_wP@iVJrIeI*po^w6Wdz-2XW;6MRZ+WUlww=oDDh z(2@VEc?}?=eIphTwK^#!MX}sudR7MMPzfJQeu@ zWD^JorP{%Xp2b6i$e0856phd4TY)YguIaE+6XRvQWi?gkvO26D(N2!2t8-(eNgz*h zPNnwx3_|Tisp@Jq0Zs&@2{k4B!T_<~bb#$X1&vkuYI-j$L~9N!h4rW-(uiJsNFGdF z7$T{KMe~&UA(o)|z1(1TeG2SAxgZ zt}>208ltwu*?9j~htwfH;Vb?_8uidPbKFocoqR}1yksE=E{hxg$QoN*jv=0rf>1%0 zpA>IJR?eQg_8w=LYA&b**C9ACWK=FScV`@M3ZMz;&GR0oet0j@0uFmL8Z?O zuMXBnLftnGa-O-qTHab+CHJT8gs3zWrY02a{c1&_gd=jrd26GPh-(3yDn5hd8nCQUQe<=UIN z9JkE7C4BlYqcHwXLhW}Y&pf~peSD1|$YN*EEl@kNrU5`5JC$`IiFh z5*$QHLrKL+&nlR$?(0qJgY*mwJYXO6;i?7-U)A1MHk(~lABTZ7dJI^#i*(jQ z2OBwcXS9<`4L|y7pHZ4b?8`kzr%qwE5*<_>798a2XzHZv9(mHA@tuuqk8D2|ilPyr zIie||F`-FJ|CoN8?kG$v%;-Q?>r_i#dk#Od*KpjiSF(4Va+wPJ04eSoADDqp`;@6o zcus0emCv-4M$Xhss7*@b&Q+4^7;Up{>TfR!1QV4JHsazD;u7*l)JI5m8Fn#2suSL5 zqqPerqWDL?jG=1WGLAA>)7A1*cTFd2)-cgUD%AsIiAv}fXlZnYhJ^a!`>l+sVa}2L z^q0d@Bcf>y8EtiZpQ38S?A;dMIc&K$jZ_aEXC&7pw|^XPm^Yk)_I@7p?N|KvxF6lW zkfTPvcfY8-VRL?(hl$d&(mVd20|r`|S}9w{u1{}p z0_kr*wZ;8m_|u1Fh}9fi|CF>JfBwNGi!vKK_vW0<7aQ3j&QvK95h7FC`3G`D2K4Sv z)~re#?cFvD_JKFuN*p$h#GMD{z}wo=_?%PVsxP zi1Bv{Uw?f~`HIU^&$hC;RnF|vgxsDvfX<;!#eU< ziAA({>80h1(c?mtO7p%>^7QUBRx6fFj!dR)YQ3oQizP0xbmF2)b=R?Y#bJ#~E&WDa zv3FZ(2Jf#pxFmOU>a9h1F``N`c1ojfjK}74=5psg&3csU8e!mj{m@iT;hns*svV4o zy;J{pzWNDAtDDCw=t7M)XOXYQ)mL39QQY)CgFdt;iVeQb&coD0)N3TpHH&&J^etJK z7T3tD*c*vf>Q4kN6zLQxl!4rqls_&9PK%FoY`uFAcDd>~r5gz_ZyFpC4O9ES$Oqod zznhS{^)po4D!Nz1cwVvK*D{=6=F8}ogIGi~nfd$<9J#@q2bC+AH0o~|el;%AsVi{! z1^C^*_i7X`<709uL1`P^3d3i!RoyAWQWGPuhkjxwhbNF_CEvjMtET0W&WX-~#ch;l z*ICEe529HjqQ0b9O3}`k&fcR{uGotlgv`W(^|a2Ex+9Mh7I$Zgep~mo0BZ8WdX6LG zrABd`rhZmlHivz^4mZI-FE zw7-=8eLI9VK(Zh`>@Rj=ziKtS8p=lgd@!3+s{F>};9|p~bom6b4`~^b%a)?CiD|us z+{9H{`HXRl4Vjj-+N0p-$McOpQGZI;D|<~(%*NE${%~B`&Cvc3E&k(vTk0*~?!H^T zw6}}Nzc@bFk%swiri0RbfAO!c4tsR4T|Ga`ahAUDkG?#%e7wW8?|3EJ;!3|5&?b|u z@jh!^@ZK<^9w3~Bj*+At0|l^aM|p4J1+$ABLgBqWR&~_?seWG0B9rzwBEP1vm+iJ4U`BK z`V9ykt@YuRriQICsuI}7`s&WU`tvpjki-q3%*dY$xHGSra2=QjOhX-NmGE_U7>x@;1v+y zzC&;$eOx>&P~0v^$Uh?gPe}_8nF7uPjsxj%?$f0h4s{2!*l zf0+V;|4aOz!oNgG-oNhuKlA*vtiQ8&+mZ%J^8RP@(jaHWY-#|2U{FO~RtJT(XF{T$ zXh-u$yggr6q-DzssVS7PzWW2=ZrAdIB?HBSM6)0bM(|5r#OPRb3g5l8-SJgp#S?zJ zQMR?~VhKS@#5qZM=#Ra-S1s{5Y{-W;!$zW;|0{09v(pT)^M3Y$GeHrBE#>se@A&iI z3qNm8B*zYRE+h|r`@0Uk{X-Tq#T^#Rc+*0?^o)5wGbc1GP58tLK^m`YdGM&Fq;VV3 z(r8**b?s}+<=(tF4dHu*7yJS5z1k8!0!IwkzzmFT$bZ3<_3PI!x#H`7vVYQvZvkdl z>H@j`VzB_FUxyM{LU$!m`pa4D z7`<1$qxjh2dKBm)Chpy26Z>a=@--6$CN}Wg2ftMgj(cH;uj%|qxrqs$#M-Jys3sJg z2q-O~Ax|%IHf)+w4*mQYy8ERH+tTMFdj&AK4RaqO ztk)@rRsMknnoM{ItHPkemjw~CM5Slg#+l~GQl0EKy${&TgG?D7pjD8-5lEXU>!NL6 z^ePcblZRd^J1D+2p0eIjIelRJAoXqKeC{Ptg*}C_56c*+|Dhlb(SUsqmX!HQW-D%$E#(r?biR9;g(6Y{XOrdnA?i!o zYH->rT^Vf#=CxVD-&C2V3yX{KB)s>#-?6G8*80f`(dg>x{v6QjQUKrh6c%q@E<2~( z0-7zs@e9ebR*<9ZZE&SA6~)dEogLY)g5f2_HXY`EQqyn;*scf^E$i&cx}fx>Wm#D0 zmQ5>1y2ExiFmk9kHkHiU0Lr; zpv)*?W{Yk2c-F36{h)%UTJ>GGI_9B(mVKmi1G&3f5jfG}qP41ZV?6H}tQo~WI5foX zckbjxF~l6iX@9-7siTY;Atg)DhF`DrWmIP zH-^(WAiyj^Ye}H){3`VLG8% zHd0=Red2mxBJ^_B^+#|xo91#=$VEayC6=}7X0}F;c7eFiV7JnM@vs$l#ixr_SA1;I z77EohY8&%~WWDCk3D(R6(5aVVfAXkoM{JkOA)vqs>}RprTH0W<28IBo7!S=3R(5T% zgYZ;@jiCRcjlV)N!>M)?V_i>%1-km17LkEI_n`WBx7&q_qvL1pG$R^&`+b8-DPZvm zX;0a%s}Klc1|Owe+ZBO^ym!(i#RuG!a0P&p*|u)S_j;;N80B1fnCG;y?UvJ*6TJ7n z^^DpLr**`VN8|Wi?9*4|>?SsTrhGtE(Z!}oupoYIclV@SdjTwDhX%_~IfX(oWW;g7 znfa1Wij8B&sY>+s-SSu7ua|*qZy;YB*Wzsu`7njEHj~h)IG3J_#T)NJft4o$bz~oj z@m}cOJU-6$2kfcjbQ)~P_ch$WDANerohSKPPT)528Pbe$J5{;#0f)E0k;W``+><{| zs6U-LK_uq(CLpOV)W34sT6)GtALX|+aV#a(4~NWc*>V+?osmGoX>jGami;lnA=WUp z(yW2uNq9Uade>Bnma&Oo>8cuUyD8`1j;eMh7v6n^zU@p4H(9}mpn|BPY)h*?lY?{J<_9w~@MET1<390qFjHYCBFZ`fRgqkZ$*eW|4`K zw?z{kJ5@X}iHI+PN@f$MgdP$_N5@ws9i6lfS~zJ0Lo`62T?d}@Zej$4G^Ywn-A%bt zZo|$lVnhjN4F7ziP!}GtcBX`rSRUnv!}7`$>juKi#z+V_WN*mHgStHzJV^r6e}Ngs zctf|A+|rO!O1oOt=GrBGhdwyK{1Ew+7CQLQXlR8rlRC+YVB)U|96i859)fg1xLd8) zgn(*XRh1`mIlRVk&9z5wy1M&U6$30&IRaEXv$ zFsiGUvTl@s7Xqf8vRNQRMwLn3BO~F*C`t!9G+v@5C-a%8YD%eBTCKepqyiUMGchhN z3Rr?=8B+OAB|T0-USOWc^Y!%6U;w>5u#6ymC{0Gjk_!o7jzLM?}I_J3rgRkC-Y=-ixylq z2|(k%Z6K%o&M?&@oe$B;j+^3&U)$s>H9ek_-^-woZ1}@BEx@auPUj>F&C`06J7lUz z^x}}qlZ;-94?ZH+MUBJkkcoZ-T!H$!Y;LHlDD{{PVp9p2g0cg4q)I4F)6|Z&tUcl@ zCzf5q9k(JEmF%&~)48~~j(Vug%!g9AcVXJvSP3$e6ge`uqU^c);Gb$730EA;#9|^* zVIp_}Nxly=i*pULQHeoHizKoSpITN^B*?7ux|E(KlqXz&DT|p+Z=F{Z2&nxQugbjL z<6PF-z4=E!cpIl9(z?@>&^bYf9bd>+Am&lINVPC8632xX2X`RoYIEUTY}1w^g0v0b zwflu%^5%33)`-Y_+pT=!C`~{jJtx^ubikfqDcw?$0~GWkj5eEr;L&d3RGK{{gs_a` zF@M&$U-)IZaS}&a*;c3K!DDm>|Lv1Hrx&z4m>w!6dfA9{?vD0w9l3D+HW2q=;%2&vlhyEW}_J^MU literal 0 HcmV?d00001 diff --git a/Documents/Attach mDNSResponder to Xcode.rtfd/Screen Shot 2015-09-16 at 3.46.14 PM.png b/Documents/Attach mDNSResponder to Xcode.rtfd/Screen Shot 2015-09-16 at 3.46.14 PM.png new file mode 100644 index 0000000000000000000000000000000000000000..31cd80700c1999bf5a26b4d4f93953b813775d4f GIT binary patch literal 6910 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 YIo{om)vxD^f7rg9l(J-%xN*?`0rwQg?f?J) literal 0 HcmV?d00001 diff --git a/Documents/Attach mDNSResponder to Xcode.rtfd/TXT.rtf b/Documents/Attach mDNSResponder to Xcode.rtfd/TXT.rtf new file mode 100644 index 0000000..ddcccfd --- /dev/null +++ b/Documents/Attach mDNSResponder to Xcode.rtfd/TXT.rtf @@ -0,0 +1,75 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf120 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset0 Menlo-Regular;} +{\colortbl;\red255\green255\blue255;} +\margl1440\margr1440\vieww25580\viewh24980\viewkind0 +\deftab720 +\pard\pardeftab720\partightenfactor0 + +\f0\fs24 \cf0 \expnd0\expndtw0\kerning0 +Three steps to attach, and debug in Xcode, a /usr/sbin/mDNSResponder that is already running. One caveat, mDNSResponder has to be built and deployed (using step 1 and 2 below) before it can be attached to Xcode.\ +\ +1.) First you have to build the Xcode project with symbols included and optimizations off.\ +\ + a.) From Terminal shell, open mDNSResponder Xcode project from top of tree: \'a0\ +\ + $ open mDNSMacOSX/mDNSResponder.xcodeproj/\ +\ +\pard\pardeftab720\partightenfactor0 +\cf0 You can also just double click on the project from Finder.\ +\ + b.) Add your diffs to mDNSResponder project. +\f1\fs22 \ +\pard\pardeftab720\partightenfactor0 + +\f0\fs24 \cf0 \ + c.) Set Strip Linked Product to No and compiler Optimization Level to None. Below shows changed settings.\ +\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0 +\cf0 \kerning1\expnd0\expndtw0 {{\NeXTGraphic Screen Shot 2015-09-16 at 3.36.23 PM.png \width7680 \height460 +}¬}\ +\ + {{\NeXTGraphic Screen Shot 2015-09-16 at 3.46.14 PM.png \width8700 \height460 +}¬}\expnd0\expndtw0\kerning0 +\ +\pard\pardeftab720\partightenfactor0 +\cf0 \ + d. ) Build mDNSResponder by setting the target to mDNSResponder.\ +\ + {{\NeXTGraphic unknown.png \width4300 \height800 \noorient +}¬}\ +\ + Then execute\'a0Command-B to build.\ +\ +\ +2.) Next, deploy newly-created Xcode version.\ +\ + a.) Go to Terminal shell and type the following: sudo mv \ +\ + b.) Then drag mDNSResponder from Products list on left side panel (shown below using red arrow) to Terminal shell.\ +\ +\pard\pardeftab720\partightenfactor0 +\cf0 {{\NeXTGraphic 52D711AF-4055-4867-A494-7E31552BB9E1.png \width4420 \height9700 +}¬}\pard\pardeftab720\partightenfactor0 +\cf0 \ +\ + c.) Execute command.\ +\ + For example:\ + \ + $ sudo mv\'a0/Volumes/iMac\\ HD/Users/llaier/Library/Developer/Xcode/DerivedData/mDNSResponder-fktewmdupxbxrrdlsdljyhmihboz/Build/Products/mDNSResponder\'a0/usr/sbin/mDNSResponder\ +\ + d.) Restart mDNSResponder by executing the following:\ +\ + $ sudo killall mDNSResponder\ +\pard\pardeftab720\partightenfactor0 + +\f1\fs22 \cf0 \ +\ +\pard\pardeftab720\partightenfactor0 + +\f0\fs24 \cf0 3.) In Xcode, select Debug > Attach to Process > mDNSResponder. +\f1\fs22 \ + +\f0\fs24 \ + Now set a breakpoint and try to trigger it.\ +} \ No newline at end of file diff --git a/Documents/Attach mDNSResponder to Xcode.rtfd/unknown.png b/Documents/Attach mDNSResponder to Xcode.rtfd/unknown.png new file mode 100644 index 0000000000000000000000000000000000000000..7e2364e0292fdc94315f9a2790904501374b5d30 GIT binary patch literal 55910 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 NOIO=at5U-$^l$c3g+TxS literal 0 HcmV?d00001 diff --git a/Documents/Start mDNSResponder in Xcode.rtfd/A944EB40-AEFD-4CA1-BF10-E8F52835CA8C.png b/Documents/Start mDNSResponder in Xcode.rtfd/A944EB40-AEFD-4CA1-BF10-E8F52835CA8C.png new file mode 100644 index 0000000000000000000000000000000000000000..a5410efed85ed0b3a8ace4fa2eb180f53bfc92ec GIT binary patch literal 90282 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 literal 0 HcmV?d00001 diff --git a/Documents/Start mDNSResponder in Xcode.rtfd/Screen Shot 2015-09-16 at 4.22.37 PM.png b/Documents/Start mDNSResponder in Xcode.rtfd/Screen Shot 2015-09-16 at 4.22.37 PM.png new file mode 100644 index 0000000000000000000000000000000000000000..1341df467f573e360efab59a2b8f0b45756efd95 GIT binary patch literal 8498 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 literal 0 HcmV?d00001 diff --git a/Documents/Start mDNSResponder in Xcode.rtfd/TXT.rtf b/Documents/Start mDNSResponder in Xcode.rtfd/TXT.rtf new file mode 100644 index 0000000..13e14d4 --- /dev/null +++ b/Documents/Start mDNSResponder in Xcode.rtfd/TXT.rtf @@ -0,0 +1,57 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1404\cocoasubrtf460 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\margl1440\margr1440\vieww51000\viewh25780\viewkind0 +\deftab720 +\pard\pardeftab720\partightenfactor0 + +\f0\fs24 \cf0 \expnd0\expndtw0\kerning0 +Instructions on how to run a secondary mDNSResponder in the Xcode debugger. (Two known issues, the secondary mDNSResponder will not send unicast mDNS packets and it does not support BTMM.)\ +\ +1.) From Terminal shell, open mDNSResponder Xcode project. \'a0\ +\ + $ open mDNSMacOSX/mDNSResponder.xcodeproj/\ +\ +You can also just double click on the project from Finder.\ +\ +2.) Configure Xcode Project Scheme by adding the following three arguments, -d -NoSandbox -UseDebugSocket.\ +\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardeftab720\pardirnatural\partightenfactor0 +\cf0 \kerning1\expnd0\expndtw0 {{\NeXTGraphic A944EB40-AEFD-4CA1-BF10-E8F52835CA8C.png \width18360 \height4420 +}¬}\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardeftab720\pardirnatural\partightenfactor0 +\cf0 \expnd0\expndtw0\kerning0 +\ +\pard\pardeftab720\partightenfactor0 +\cf0 \ +3.) Build and run mDNSResponder by setting the target to mDNSResponder.\ +\ +\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardeftab720\pardirnatural\partightenfactor0 +\cf0 \kerning1\expnd0\expndtw0 {{\NeXTGraphic Screen Shot 2015-09-16 at 4.22.37 PM.png \width5980 \height660 +}¬}\expnd0\expndtw0\kerning0 +\ +\pard\pardeftab720\partightenfactor0 +\cf0 \ + Then execute\'a0Command-R to build and run your code with the active scheme.\ +\ +\pard\pardeftab720\pardirnatural\partightenfactor0 +\cf0 \kerning1\expnd0\expndtw0 \CocoaLigature0 4.) Before using dns-sd with Xcode-version of mDNSResponder, make sure to export the socket UDS path by executing one of the following commands:\ +\ + From a Borne, bash or zsh, execute this command:\ +\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\pardirnatural\partightenfactor0 +\cf0 export DNSSD_UDS_PATH=/var/tmp/mDNSResponder\ +\ +\pard\tx729\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\pardirnatural\partightenfactor0 +\cf0 From csh or tcsh, execute this command:\ +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardeftab720\pardirnatural\partightenfactor0 +\cf0 \ + \expnd0\expndtw0\kerning0 +\CocoaLigature1 setenv DNSSD_UDS_PATH /var/tmp/mDNSResponder\ +\ + There may be other variants to setting the environment variable as well for other shells.\kerning1\expnd0\expndtw0 \CocoaLigature0 \ +\ + Now running dns-sd will interoperate with the secondary Xcode-version of mDNSResponder as long as you run dns-sd from this terminal shell.\ +\ +5.) \expnd0\expndtw0\kerning0 +\CocoaLigature1 Now set a breakpoint in Xcode and try to trigger it using dns-sd.\ +} \ No newline at end of file diff --git a/Makefile b/Makefile index 647dcf9..2b528d0 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include $(MAKEFILEPATH)/pb_makefiles/platform.make -MVERS = "mDNSResponder-576.30.4" +MVERS = "mDNSResponder-765.50.9" DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig" VER = @@ -41,6 +41,7 @@ installsrc: installhdrs:: cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries $(VER) + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target dns_services $(VER) java: cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target libjdns_sd.jnilib $(VER) diff --git a/mDNSCore/CryptoAlg.c b/mDNSCore/CryptoAlg.c index 38533fc..0008405 100644 --- a/mDNSCore/CryptoAlg.c +++ b/mDNSCore/CryptoAlg.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-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. diff --git a/mDNSCore/CryptoAlg.h b/mDNSCore/CryptoAlg.h index 6cb3dc9..c21507e 100644 --- a/mDNSCore/CryptoAlg.h +++ b/mDNSCore/CryptoAlg.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-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. @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef __CRYPTO_ALG_H #define __CRYPTO_ALG_H diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index 249e3b2..3ea9a30 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -43,6 +43,7 @@ mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2; mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3; mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4; mDNSexport const mDNSInterfaceID uDNSInterfaceMark = (mDNSInterfaceID)-5; +mDNSexport const mDNSInterfaceID mDNSInterface_BLE = (mDNSInterfaceID)-6; // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP @@ -107,6 +108,8 @@ mDNSexport const mDNSOpaque16 DNSSecQFlags = { { kDNSFlag0_QR_Query | kDNS mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; +mDNSexport const mDNSOpaque16 SubscribeFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Subscribe, 0 } }; +mDNSexport const mDNSOpaque16 UnSubscribeFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_UnSubscribe, 0 } }; mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } }; @@ -510,10 +513,10 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s ", DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL), - expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c); + expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), rrsig->signerName); len = DomainNameLength((domainname *)&rrsig->signerName); - length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE), + baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE), rr->rdlength - (len + RRSIG_FIXED_SIZE), ENC_BASE64); } break; @@ -521,7 +524,7 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD rdataDNSKey *rrkey = (rdataDNSKey *)rd->data; length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u ", swap16(rrkey->flags), rrkey->proto, DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength)); - length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE), + baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE), rr->rdlength - DNSKEY_FIXED_SIZE, ENC_BASE64); } break; @@ -541,7 +544,7 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD } break; - default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data); + default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %.*s", rr->rdlength, rr->rdlength, rd->data); // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.'; break; @@ -803,6 +806,7 @@ mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstri mDNSu8 c = (mDNSu8)*cstr++; // Read the character if (c == '\\') // If escape character, check next character { + if (*cstr == '\0') break; // If this is the end of the string, then break c = (mDNSu8)*cstr++; // Assume we'll just take the next character if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1])) { // If three decimal digits, @@ -815,7 +819,7 @@ mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstri } *ptr++ = c; // Write the character } - if (*cstr) cstr++; // Skip over the trailing dot (if present) + if (*cstr == '.') cstr++; // Skip over the trailing dot (if present) if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort return(mDNSNULL); *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte @@ -1165,7 +1169,8 @@ mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3 const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen) { AlgContext *ctx; - int i; + unsigned int i; + unsigned int iterations; domainname lname; mDNSu8 *p = (mDNSu8 *)&nsec3->salt; const mDNSu8 *digest; @@ -1183,7 +1188,8 @@ mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3 // Note that it is "i <=". The first iteration is for digesting the name and salt. // The iteration count does not include that. - for (i = 0; i <= swap16(nsec3->iterations); i++) + iterations = swap16(nsec3->iterations); + for (i = 0; i <= iterations; i++) { ctx = AlgCreate(DIGEST_ALG, nsec3->alg); if (!ctx) @@ -1367,17 +1373,14 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly) { LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype); - return; } else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P) { LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype); - return; } else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly)) { LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype); - return; } // Don't try to store a TTL bigger than we can represent in platform time units @@ -1467,8 +1470,6 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNSfalse; q->SuppressUnusable = mDNSfalse; - q->DenyOnCellInterface = mDNSfalse; - q->DenyOnExpInterface = mDNSfalse; q->SearchListIndex = 0; q->AppendSearchDomains = 0; q->RetryWithSearchDomains = mDNSfalse; @@ -1481,6 +1482,7 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I q->qnameOrig = mDNSNULL; q->AnonInfo = mDNSNULL; q->pid = mDNSPlatformGetPID(); + q->euid = 0; q->DisallowPID = mDNSfalse; q->ServiceID = -1; q->QuestionCallback = callback; @@ -1778,7 +1780,7 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records // are handled in LocalOnlyRecordAnswersQuestion - if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + if (LocalOnlyOrP2PInterface(rr->InterfaceID)) { LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); return mDNSfalse; @@ -1913,7 +1915,7 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, { // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records // are handled in LocalOnlyRecordAnswersQuestion - if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + if (LocalOnlyOrP2PInterface(rr->InterfaceID)) { LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); return mDNSfalse; @@ -2141,7 +2143,6 @@ mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const return(mDNSNULL); } -// Put a string of dot-separated labels as length-prefixed labels // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't) // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) // end points to the end of the message so far @@ -2357,13 +2358,9 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS case kDNSType_NSEC: { // For NSEC records, rdlength represents the exact number of bytes // of in memory storage. - int len = rr->rdlength; mDNSu8 *nsec = (mDNSu8 *)rdb->data; domainname *name = (domainname *)nsec; - int dlen; - - dlen = DomainNameLength(name); - len -= dlen; + const int dlen = DomainNameLength(name); nsec += dlen; // This function is called when we are sending a NSEC record as part of mDNS, // or to copy the data to any other buffer needed which could be a mDNS or uDNS @@ -2376,7 +2373,6 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS int i, j, wlen; wlen = *(nsec + 1); nsec += 2; // Skip the window number and len - len -= 2; // For our simplified use of NSEC synthetic records: // @@ -2406,6 +2402,7 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS else { int win, wlen; + int len = rr->rdlength - dlen; // Sanity check whether the bitmap is good while (len) @@ -2607,7 +2604,7 @@ mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domain } // for dynamic updates -mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) +mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease) { AuthRecord rr; mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); @@ -2616,13 +2613,13 @@ mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease) rr.resrec.rdestimate = sizeof(rdataOPT); rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; rr.resrec.rdata->u.opt[0].u.updatelease = lease; - end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0); - if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } - return end; + ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, &rr.resrec, 0); + if (!ptr) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; } + return ptr; } // for dynamic updates -mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit) +mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit) { AuthRecord rr; mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); @@ -2631,9 +2628,9 @@ mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 rr.resrec.rdestimate = sizeof(rdataOPT); rr.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; rr.resrec.rdata->u.opt[0].u.updatelease = lease; - end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit); - if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } - return end; + ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.numAdditionals, &rr.resrec, 0, limit); + if (!ptr) { LogMsg("ERROR: putUpdateLeaseWithLimit - PutResourceRecordTTLWithLimit"); return mDNSNULL; } + return ptr; } mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit) @@ -2650,7 +2647,7 @@ mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit) // set the DO bit ttl |= 0x8000; end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, ttl, limit); - if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; } + if (!end) { LogMsg("ERROR: putDNSSECOption - PutResourceRecordTTLWithLimit"); return mDNSNULL; } return end; } @@ -2846,7 +2843,7 @@ mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int l // (domainnames are expanded to 255 bytes) when stored in memory. // // This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr. -// The caller can do this only if the names in the resource records are compressed and validity of the +// The caller can do this only if the names in the resource records are not compressed and validity of the // resource record has already been done before. DNSSEC currently uses it this way. mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, LargeCacheRecord *const largecr, mDNSu16 rdlength) @@ -3351,6 +3348,12 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con dlen = DomainNameLength(&name); rlen = end - ptr; rr->resrec.rdlength = dlen + rlen; + if (rr->resrec.rdlength > MaximumRDSize) + { + LogInfo("SetRData: Malformed TSIG/TKEY rdlength %d, rr->resrec.rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + goto fail; + } AssignDomainName((domainname *)rdb->data, &name); mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen); break; @@ -3441,12 +3444,7 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage mDNSu16 pktrdlength; if (largecr == &m->rec && m->rec.r.resrec.RecordType) - { - LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); -#if ForceAlerts - *(long*)0 = 0; -#endif - } + LogFatalError("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); rr->next = mDNSNULL; rr->resrec.name = &largecr->namestorage; @@ -3634,7 +3632,7 @@ mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end) mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label) { int i; - LogMsg("%2d %s", count, label); + LogInfo("%2d %s", count, label); for (i = 0; i < count && ptr; i++) { // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage, @@ -3642,9 +3640,11 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, // embedded systems) putting a 9kB object on the stack isn't a big problem. LargeCacheRecord largecr; ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr); - if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); + if (ptr) + LogInfo("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); } - if (!ptr) LogMsg("DumpRecords: ERROR: Premature end of packet data"); + if (!ptr) + LogInfo("DumpRecords: ERROR: Premature end of packet data"); return(ptr); } @@ -3654,7 +3654,9 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, (X) == kDNSFlag0_OP_Status ? "Status " : \ (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \ (X) == kDNSFlag0_OP_Notify ? "Notify " : \ - (X) == kDNSFlag0_OP_Update ? "Update " : "?? " ) + (X) == kDNSFlag0_OP_Update ? "Update " : \ + (X) == kDNSFlag0_OP_Subscribe? "Subscribe": \ + (X) == kDNSFlag0_OP_UnSubscribe? "UnSubscribe" : "?? " ) #define DNS_RC_Name(X) ( \ (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ @@ -3686,7 +3688,7 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t if (dstaddr || !mDNSIPPortIsZero(dstport)) dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; - LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --", + LogInfo("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --", tbuffer, transport, DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", @@ -3705,16 +3707,16 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" ); - LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); + LogInfo("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); for (i = 0; i < msg->h.numQuestions && ptr; i++) { ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); - if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); + if (ptr) LogInfo("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); } ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers"); ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities"); - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); - LogMsg("--------------"); + DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); + LogInfo("--------------"); } // *************************************************************************** @@ -3725,11 +3727,8 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t // Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.) struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ }; - -struct UDPSocket_struct -{ - mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port -}; +// Stub definition of UDPSocket_struct so we can access port field. (Rest of UDPSocket_struct is platform-dependent.) +struct UDPSocket_struct { mDNSIPPort port; /* ... */ }; // Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which // is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible. @@ -3844,12 +3843,7 @@ mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname) // If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one // If mDNS_busy != mDNS_reentrancy that's a bad sign if (m->mDNS_busy != m->mDNS_reentrancy) - { - LogMsg("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); -#if ForceAlerts - *(long*)0 = 0; -#endif - } + LogFatalError("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); // If this is an initial entry into the mDNSCore code, set m->timenow // else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set @@ -3912,6 +3906,10 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA; +#if BONJOUR_ON_DEMAND + if (m->NextBonjourDisableTime && (e - m->NextBonjourDisableTime > 0)) e = m->NextBonjourDisableTime; +#endif // BONJOUR_ON_DEMAND + // NextScheduledSPRetry only valid when DelaySleep not set if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; @@ -3927,85 +3925,95 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; } if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime; + + if (m->NextBLEServiceTime && (e - m->NextBLEServiceTime > 0)) e = m->NextBLEServiceTime; + return(e); } +#define LogTSE TSE++,LogMsg + mDNSexport void ShowTaskSchedulingError(mDNS *const m) { + int TSE = 0; AuthRecord *rr; mDNS_Lock(m); - LogMsg("Task Scheduling Error: Continuously busy for more than a second"); + LogMsg("Task Scheduling Error: *** Continuously busy for more than a second"); // Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0)) - LogMsg("Task Scheduling Error: NewQuestion %##s (%s)", + LogTSE("Task Scheduling Error: NewQuestion %##s (%s)", m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype)); if (m->NewLocalOnlyQuestions) - LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)", + LogTSE("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)", m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype)); if (m->NewLocalRecords) { rr = AnyLocalRecordReady(m); - if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr)); + if (rr) LogTSE("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr)); } - if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords"); + if (m->NewLocalOnlyRecords) LogTSE("Task Scheduling Error: NewLocalOnlyRecords"); - if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged"); - if (m->LocalRemoveEvents) LogMsg("Task Scheduling Error: LocalRemoveEvents"); + if (m->SPSProxyListChanged) LogTSE("Task Scheduling Error: SPSProxyListChanged"); - if (m->timenow - m->NextScheduledEvent >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent); + if (m->LocalRemoveEvents) LogTSE("Task Scheduling Error: LocalRemoveEvents"); #ifndef UNICAST_DISABLED if (m->timenow - m->NextuDNSEvent >= 0) - LogMsg("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); + LogTSE("Task Scheduling Error: m->NextuDNSEvent %d", m->timenow - m->NextuDNSEvent); if (m->timenow - m->NextScheduledNATOp >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); + LogTSE("Task Scheduling Error: m->NextScheduledNATOp %d", m->timenow - m->NextScheduledNATOp); if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) - LogMsg("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate); + LogTSE("Task Scheduling Error: m->NextSRVUpdate %d", m->timenow - m->NextSRVUpdate); #endif if (m->timenow - m->NextCacheCheck >= 0) - LogMsg("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck); + LogTSE("Task Scheduling Error: m->NextCacheCheck %d", m->timenow - m->NextCacheCheck); if (m->timenow - m->NextScheduledSPS >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); + LogTSE("Task Scheduling Error: m->NextScheduledSPS %d", m->timenow - m->NextScheduledSPS); if (m->timenow - m->NextScheduledKA >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledKA %d", m->timenow - m->NextScheduledKA); + LogTSE("Task Scheduling Error: m->NextScheduledKA %d", m->timenow - m->NextScheduledKA); if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); + LogTSE("Task Scheduling Error: m->NextScheduledSPRetry %d", m->timenow - m->NextScheduledSPRetry); if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) - LogMsg("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); + LogTSE("Task Scheduling Error: m->DelaySleep %d", m->timenow - m->DelaySleep); if (m->SuppressSending && m->timenow - m->SuppressSending >= 0) - LogMsg("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); + LogTSE("Task Scheduling Error: m->SuppressSending %d", m->timenow - m->SuppressSending); if (m->timenow - m->NextScheduledQuery >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery); + LogTSE("Task Scheduling Error: m->NextScheduledQuery %d", m->timenow - m->NextScheduledQuery); if (m->timenow - m->NextScheduledProbe >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); + LogTSE("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); if (m->timenow - m->NextScheduledResponse >= 0) - LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); + LogTSE("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); + if (m->timenow - m->NextScheduledStopTime >= 0) + LogTSE("Task Scheduling Error: m->NextScheduledStopTime %d", m->timenow - m->NextScheduledStopTime); + + if (m->timenow - m->NextScheduledEvent >= 0) + LogTSE("Task Scheduling Error: m->NextScheduledEvent %d", m->timenow - m->NextScheduledEvent); + + if (m->NetworkChanged && m->timenow - m->NetworkChanged >= 0) + LogTSE("Task Scheduling Error: NetworkChanged %d", m->timenow - m->NetworkChanged); + + if (!TSE) LogMsg("Task Scheduling Error: *** No likely causes identified"); + else LogMsg("Task Scheduling Error: *** %d potential cause%s identified (significant only if the same cause consistently appears)", TSE, TSE > 1 ? "s" : ""); mDNS_Unlock(m); } -mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname) +mDNSexport void mDNS_Unlock_(mDNS *const m, const char *const functionname) { // Decrement mDNS_busy m->mDNS_busy--; // Check for locking failures if (m->mDNS_busy != m->mDNS_reentrancy) - { - LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); -#if ForceAlerts - *(long*)0 = 0; -#endif - } + LogFatalError("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy); // If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow if (m->mDNS_busy == 0) diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h index b92f5a9..2da1970 100644 --- a/mDNSCore/DNSCommon.h +++ b/mDNSCore/DNSCommon.h @@ -40,17 +40,19 @@ extern "C" { typedef enum { - kDNSFlag0_QR_Mask = 0x80, // Query or response? - kDNSFlag0_QR_Query = 0x00, - kDNSFlag0_QR_Response = 0x80, - - kDNSFlag0_OP_Mask = 0x78, // Operation type - kDNSFlag0_OP_StdQuery = 0x00, - kDNSFlag0_OP_Iquery = 0x08, - kDNSFlag0_OP_Status = 0x10, - kDNSFlag0_OP_Unused3 = 0x18, - kDNSFlag0_OP_Notify = 0x20, - kDNSFlag0_OP_Update = 0x28, + kDNSFlag0_QR_Mask = 0x80, // Query or response? + kDNSFlag0_QR_Query = 0x00, + kDNSFlag0_QR_Response = 0x80, + + kDNSFlag0_OP_Mask = 0x78, // Operation type + kDNSFlag0_OP_StdQuery = 0x00, + kDNSFlag0_OP_Subscribe = 0x06, + kDNSFlag0_OP_UnSubscribe = 0x07, + kDNSFlag0_OP_Iquery = 0x08, + kDNSFlag0_OP_Status = 0x10, + kDNSFlag0_OP_Unused3 = 0x18, + kDNSFlag0_OP_Notify = 0x20, + kDNSFlag0_OP_Update = 0x28, kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, @@ -84,6 +86,7 @@ typedef enum TSIG_ErrBadTime = 18 } TSIG_ErrorCode; + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -220,7 +223,7 @@ extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *r extern mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit); extern mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit); extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name); -extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); +extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease); extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit); extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit); @@ -297,16 +300,16 @@ extern void mDNS_Unlock_(mDNS *const m, const char * const functionname); #define mDNS_Unlock(X) mDNS_Unlock_((X), __func__) -#define mDNS_CheckLock(X) { if ((X)->mDNS_busy != (X)->mDNS_reentrancy+1) \ - LogMsg("%s: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", __func__, (X)->mDNS_busy, (X)->mDNS_reentrancy); } +#define mDNS_CheckLock(X) \ + if ((X)->mDNS_busy != (X)->mDNS_reentrancy+1) LogMsg("%s: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", __func__, (X)->mDNS_busy, (X)->mDNS_reentrancy) #define mDNS_DropLockBeforeCallback() do { m->mDNS_reentrancy++; \ - if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Locking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ -} while (0) + if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Locking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ + } while (0) #define mDNS_ReclaimLockAfterCallback() do { \ - if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Unlocking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ - m->mDNS_reentrancy--; } while (0) + if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Unlocking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \ + m->mDNS_reentrancy--; } while (0) #ifdef __cplusplus } diff --git a/mDNSCore/DNSDigest.c b/mDNSCore/DNSDigest.c index 33798d3..57a4012 100644 --- a/mDNSCore/DNSDigest.c +++ b/mDNSCore/DNSDigest.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-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. @@ -15,7 +15,6 @@ * limitations under the License. */ - #ifdef __cplusplus extern "C" { #endif diff --git a/mDNSCore/anonymous.c b/mDNSCore/anonymous.c index 94b102e..d2b80e7 100644 --- a/mDNSCore/anonymous.c +++ b/mDNSCore/anonymous.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2012-2013 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. @@ -27,6 +27,14 @@ #define ANON_NSEC3_ITERATIONS 1 +struct AnonInfoResourceRecord_struct +{ + ResourceRecord resrec; + RData rdatastorage; +}; + +typedef struct AnonInfoResourceRecord_struct AnonInfoResourceRecord; + mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt) { const mDNSu8 *ptr; @@ -63,7 +71,7 @@ mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonD // Hash the base service name + salt + AnonData if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen)) { - LogMsg("InitializeNSEC3Record: NSEC3HashName failed for ##s", rr->name->c); + LogMsg("InitializeNSEC3Record: NSEC3HashName failed for %##s", rr->name->c); return mDNSfalse; } if (hlen != SHA1_HASH_LENGTH) @@ -118,9 +126,10 @@ mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr) { - int len; + AnonInfoResourceRecord *anonRR; domainname *name; - ResourceRecord *nsec3rr; + mDNSu32 neededLen; + mDNSu32 extraLen; if (rr->rdlength < MCAST_NSEC3_RDLENGTH) { @@ -128,22 +137,26 @@ mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const Resou return mDNSNULL; } // Allocate space for the name and the rdata along with the ResourceRecord - len = DomainNameLength(rr->name); - nsec3rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + len + sizeof(RData)); - if (!nsec3rr) + neededLen = rr->rdlength + DomainNameLength(rr->name); + extraLen = (neededLen > sizeof(RDataBody)) ? (neededLen - sizeof(RDataBody)) : 0; + anonRR = (AnonInfoResourceRecord *)mDNSPlatformMemAllocate(sizeof(AnonInfoResourceRecord) + extraLen); + if (!anonRR) return mDNSNULL; - *nsec3rr = *rr; - name = (domainname *)((mDNSu8 *)nsec3rr + sizeof(ResourceRecord)); - nsec3rr->name = (const domainname *)name; + anonRR->resrec = *rr; + + anonRR->rdatastorage.MaxRDLength = rr->rdlength; + mDNSPlatformMemCopy(anonRR->rdatastorage.u.data, rr->rdata->u.data, rr->rdlength); + + name = (domainname *)(anonRR->rdatastorage.u.data + rr->rdlength); AssignDomainName(name, rr->name); - nsec3rr->rdata = (RData *)((mDNSu8 *)nsec3rr->name + len); - mDNSPlatformMemCopy(nsec3rr->rdata->u.data, rr->rdata->u.data, rr->rdlength); + anonRR->resrec.name = name; + anonRR->resrec.rdata = &anonRR->rdatastorage; - si->nsec3RR = nsec3rr; + si->nsec3RR = (ResourceRecord *)anonRR; - return nsec3rr; + return si->nsec3RR; } // When a service is started or a browse is started with the Anonymous data, we allocate a new random @@ -262,9 +275,10 @@ mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNS int AnonDataLen; rdataNSEC3 *nsec3; int hlen; - const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; int nxtLength; mDNSu8 *nxtName; + mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + mDNSPlatformMemZero(hashName, sizeof(hashName)); debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c); @@ -385,7 +399,7 @@ mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNS if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen)) { - LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for ##s", nsec3RR->name->c); + LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for %##s", nsec3RR->name->c); return mDNSfalse; } if (hlen != SHA1_HASH_LENGTH) diff --git a/mDNSCore/anonymous.h b/mDNSCore/anonymous.h index 2f2b4f8..b60812e 100644 --- a/mDNSCore/anonymous.h +++ b/mDNSCore/anonymous.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 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. diff --git a/mDNSCore/dnsproxy.c b/mDNSCore/dnsproxy.c index 11bacc1..2afb59d 100644 --- a/mDNSCore/dnsproxy.c +++ b/mDNSCore/dnsproxy.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -83,10 +83,6 @@ mDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc) mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit) { - mDNSu16 rrtype, rrclass; - mDNSu8 rcode, version; - mDNSu16 flag; - if (ptr + length > limit) { LogInfo("ParseEDNS0: Not enough space in the packet"); @@ -94,18 +90,19 @@ mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, } // Skip the root label ptr++; - rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); + mDNSu16 rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); if (rrtype != kDNSType_OPT) { LogInfo("ParseEDNS0: Not the right type %d", rrtype); return mDNSfalse; } - rrclass = (mDNSu16) ((mDNSu16)ptr[2] << 8 | ptr[3]); - rcode = ptr[4]; - version = ptr[5]; - flag = (mDNSu16) ((mDNSu16)ptr[6] << 8 | ptr[7]); - + mDNSu16 rrclass = (mDNSu16) ((mDNSu16)ptr[2] << 8 | ptr[3]); +#if MDNS_DEBUGMSGS + mDNSu8 rcode = ptr[4]; + mDNSu8 version = ptr[5]; + mDNSu16 flag = (mDNSu16) ((mDNSu16)ptr[6] << 8 | ptr[7]); debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype), rrclass, rcode, version, flag); +#endif pc->rcvBufSize = rrclass; pc->DNSSECOK = ptr[6] & 0x80; @@ -198,6 +195,8 @@ mDNSlocal mDNSu8 *AddResourceRecords(mDNS *const m, DNSProxyClient *pc, mDNSu8 * CacheRecord *soa = mDNSNULL; CacheRecord *cname = mDNSNULL; mDNSu8 *limit; + domainname tempQName; + mDNSu32 tempQNameHash; *error = mStatus_NoError; *prevptr = mDNSNULL; @@ -225,20 +224,17 @@ mDNSlocal mDNSu8 *AddResourceRecords(mDNS *const m, DNSProxyClient *pc, mDNSu8 * } LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data); - if (!SameDomainName(&pc->qname, &pc->q.qname)) - { - AssignDomainName(&pc->q.qname, &pc->qname); - pc->q.qnamehash = DomainNameHashValue(&pc->q.qname); - } + AssignDomainName(&tempQName, &pc->qname); + tempQNameHash = DomainNameHashValue(&tempQName); again: nsec = soa = cname = mDNSNULL; - slot = HashSlot(&pc->q.qname); - - cg = CacheGroupForName(m, slot, pc->q.qnamehash, &pc->q.qname); + slot = HashSlot(&tempQName); + + cg = CacheGroupForName(m, slot, tempQNameHash, &tempQName); if (!cg) { - LogInfo("AddResourceRecords: CacheGroup not found"); + LogInfo("AddResourceRecords: CacheGroup not found for %##s", tempQName.c); *error = mStatus_NoSuchRecord; return mDNSNULL; } @@ -347,8 +343,8 @@ again: } if (cname) { - AssignDomainName(&pc->q.qname, &cname->resrec.rdata->u.name); - pc->q.qnamehash = DomainNameHashValue(&pc->q.qname); + AssignDomainName(&tempQName, &cname->resrec.rdata->u.name); + tempQNameHash = DomainNameHashValue(&tempQName); goto again; } if (!ptr) @@ -366,7 +362,7 @@ again: return mDNSNULL; } len += (ptr - orig); - orig = ptr; + // orig = ptr; Commented out to avoid ‘value never read’ error message } LogInfo("AddResourceRecord: Added %d bytes to the packet", len); return ptr; @@ -471,14 +467,16 @@ mDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const R } } } + + debugf("ProxyClientCallback: InterfaceID is %p for response to client", pc->interfaceID); if (!pc->tcp) { - mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse); + mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse); } else { - mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse); + mDNSSendDNSMessage(m, &m->omsg, ptr, pc->interfaceID, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse); } done: @@ -496,13 +494,10 @@ done: FreeDNSProxyClient(pc); } -mDNSlocal void SendError(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *dstaddr, +mDNSlocal void SendError(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context, mDNSu8 rcode) { - int pktlen = (int)(end - (mDNSu8 *)pkt); - DNSMessage *msg = (DNSMessage *)pkt; - - (void) InterfaceID; + int pktlen = (int)(end - (mDNSu8 *)msg); // RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing // in the body or send back whatever we get for updates. It is easy to return whatever we get @@ -517,12 +512,12 @@ mDNSlocal void SendError(mDNS *const m, void *socket, void *const pkt, const mDN mDNSPlatformMemCopy(m->omsg.data, (mDNSu8 *)&msg->h.numQuestions, pktlen); if (!tcp) { - mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, socket, dstaddr, dstport, mDNSNULL, mDNSNULL, + mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, socket, dstaddr, dstport, mDNSNULL, mDNSNULL, mDNSfalse); } else { - mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket, + mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, InterfaceID, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket, mDNSNULL, mDNSfalse); } mDNSPlatformDisposeProxyContext(context); @@ -556,27 +551,33 @@ mDNSlocal mDNSBool CheckDNSProxyIpIntf(const mDNS *const m, mDNSInterfaceID Inte int i; mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID; - LogInfo("CheckDNSProxyIpIntf: Stored Input Interface List: [%d] [%d] [%d] [%d] [%d]", m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], - m->dp_ipintf[3], m->dp_ipintf[4]); + LogInfo("CheckDNSProxyIpIntf: Check for ifindex[%d] in stored input interface list: [%d] [%d] [%d] [%d] [%d]", + ip_ifindex, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4]); - for (i = 0; i < MaxIp; i++) + if (ip_ifindex > 0) { - if (ip_ifindex == m->dp_ipintf[i]) - return mDNStrue; + for (i = 0; i < MaxIp; i++) + { + if (ip_ifindex == m->dp_ipintf[i]) + return mDNStrue; + } } + + LogMsg("CheckDNSProxyIpIntf: ifindex[%d] not in stored input interface list: [%d] [%d] [%d] [%d] [%d]", + ip_ifindex, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4]); + return mDNSfalse; } -mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, +mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context) { - DNSMessage *msg = (DNSMessage *)pkt; mDNSu8 QR_OP; const mDNSu8 *ptr; DNSQuestion q, *qptr; DNSProxyClient *pc; - const mDNSu8 *optRR; + const mDNSu8 *optRR = mDNSNULL; int optLen = 0; DNSProxyClient **ppc = &DNSProxyClients; @@ -586,11 +587,14 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID); // Ignore if the DNS Query is not from a Valid Input InterfaceID if (!CheckDNSProxyIpIntf(m, InterfaceID)) + { + LogMsg("ProxyCallbackCommon: Rejecting DNS Query coming from InterfaceID %p", InterfaceID); return; + } - if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + if ((unsigned)(end - (mDNSu8 *)msg) < sizeof(DNSMessageHeader)) { - debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg)); return; } @@ -598,7 +602,7 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, if (QR_OP != kDNSFlag0_QR_Query) { LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP, srcaddr, mDNSVal16(srcport)); - SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_NotImpl); + SendError(m, socket, msg, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_NotImpl); return; } @@ -613,7 +617,7 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, { LogInfo("ProxyCallbackCommon: Malformed pkt from %#a:%d, Q:%d, An:%d, Au:%d", srcaddr, mDNSVal16(srcport), msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities); - SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); + SendError(m, socket, msg, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); return; } ptr = msg->data; @@ -621,7 +625,7 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, if (!ptr) { LogInfo("ProxyCallbackCommon: Question cannot be parsed for pkt from %#a:%d", srcaddr, mDNSVal16(srcport)); - SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); + SendError(m, socket, msg, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); return; } else @@ -694,8 +698,7 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, debugf("ProxyCallbackCommon: DNS Query forwarding to interface index %d", m->dp_opintf); mDNS_SetupQuestion(&pc->q, (mDNSInterfaceID)(unsigned long)m->dp_opintf, &q.qname, q.qtype, ProxyClientCallback, pc); pc->q.TimeoutQuestion = 1; - // Even though we don't care about intermediate responses, set ReturnIntermed so that - // we get the negative responses + // Set ReturnIntermed so that we get the negative responses pc->q.ReturnIntermed = mDNStrue; pc->q.ProxyQuestion = mDNStrue; pc->q.ProxyDNSSECOK = pc->DNSSECOK; @@ -727,20 +730,21 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, mDNS_StartQuery(m, &pc->q); } -mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, +mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) { - LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); - ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNSfalse, context); + LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg)); + ProxyCallbackCommon(m, socket, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNSfalse, context); } -mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, +mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) { - LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); - // If the connection was closed from the other side, locate the client + LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - (mDNSu8 *)msg)); + + // If the connection was closed from the other side or incoming packet does not match stored input interface list, locate the client // state and free it. - if ((end - (mDNSu8 *)pkt) == 0) + if (((end - (mDNSu8 *)msg) == 0) || (!CheckDNSProxyIpIntf(m, InterfaceID))) { DNSProxyClient **ppc = &DNSProxyClients; DNSProxyClient **prevpc; @@ -763,7 +767,7 @@ mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, c FreeDNSProxyClient(*ppc); return; } - ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context); + ProxyCallbackCommon(m, socket, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context); } mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf) @@ -793,11 +797,11 @@ mDNSexport void DNSProxyTerminate(mDNS *const m) } #else // UNICAST_DISABLED -mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) { (void) m; (void) socket; - (void) pkt; + (void) msg; (void) end; (void) srcaddr; (void) srcport; @@ -807,11 +811,11 @@ mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, c (void) context; } -mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) { (void) m; (void) socket; - (void) pkt; + (void) msg; (void) end; (void) srcaddr; (void) srcport; diff --git a/mDNSCore/dnsproxy.h b/mDNSCore/dnsproxy.h index ed46a12..a2abdfb 100644 --- a/mDNSCore/dnsproxy.h +++ b/mDNSCore/dnsproxy.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -14,15 +14,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef __DNS_PROXY_H #define __DNS_PROXY_H #include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" -extern void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, +extern void ProxyUDPCallback(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); -extern void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, +extern void ProxyTCPCallback(mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); extern void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf); extern void DNSProxyTerminate(mDNS *const m); diff --git a/mDNSCore/dnssec.c b/mDNSCore/dnssec.c index c83b841..514a488 100644 --- a/mDNSCore/dnssec.c +++ b/mDNSCore/dnssec.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "mDNSEmbeddedAPI.h" #include "DNSSECSupport.h" #include "DNSCommon.h" @@ -86,6 +87,7 @@ mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv); mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv); mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status); mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from); +mDNSlocal void FreeDNSSECAuthChainInfo(AuthChain *ac); // Currently we use this to convert a RRVerifier to resource record so that we can // use the standard DNS utility functions @@ -299,6 +301,8 @@ mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae) if (!ac) { LogMsg("AuthChainCopy: AuthChain alloc failure"); + if (retac) + FreeDNSSECAuthChainInfo(retac); return mDNSfalse; } @@ -309,7 +313,7 @@ mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae) rvfrom = ae->rrset; rvto = &ac->rrset; - while (rvfrom) + while (rvfrom && rvto) { *rvto = CopyRRVerifier(rvfrom); rvfrom = rvfrom->next; @@ -318,7 +322,7 @@ mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae) rvfrom = ae->rrsig; rvto = &ac->rrsig; - while (rvfrom) + while (rvfrom && rvto) { *rvto = CopyRRVerifier(rvfrom); rvfrom = rvfrom->next; @@ -327,7 +331,7 @@ mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae) rvfrom = ae->key; rvto = &ac->key; - while (rvfrom) + while (rvfrom && rvto) { *rvto = CopyRRVerifier(rvfrom); rvfrom = rvfrom->next; @@ -1065,7 +1069,6 @@ mDNSlocal mStatus CheckDSForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecord * return mStatus_NoError; else return mStatus_NoSuchRecord; - return (dv->ds ? mStatus_NoError : mStatus_NoSuchRecord); } // It returns mDNStrue if we have all the rrsets for verification and mDNSfalse otherwise. @@ -2335,8 +2338,6 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu rdataRRSig *rrsig; mDNSu32 slot; CacheGroup *cg; - int sigNameLen, len; - mDNSu8 *ptr; mDNSu32 rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL; domainname *qname; mDNSu16 qtype; @@ -2400,17 +2401,11 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu { rrsigv = dv->ac->rrsig; rrsig = (rdataRRSig *)rrsigv->rdata; - sigNameLen = DomainNameLength((domainname *)&rrsig->signerName); - // pointer to signature and the length - ptr = (mDNSu8 *)(rrsigv->rdata + sigNameLen + RRSIG_FIXED_SIZE); - len = rrsigv->rdlength - RRSIG_FIXED_SIZE - sigNameLen; } else { rrsigv = mDNSNULL; rrsig = mDNSNULL; - ptr = mDNSNULL; - sigNameLen = len = 0; } rrsigRR = mDNSNULL; @@ -2531,7 +2526,9 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) LogDNSSEC("FinishDNSSECVerification: all rdata sets available for sig verification for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); - mDNS_StopQuery(m, &dv->q); + // Stop outstanding query if one exists + if (dv->q.ThisQInterval != -1) + mDNS_StopQuery(m, &dv->q); if (ValidateSignature(dv, &resultKey, &resultRRSig) == mStatus_NoError) { rdataDNSKey *key; @@ -3187,14 +3184,11 @@ mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion * mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv) { - rdataRRSig *rrsig; rdataDS *ds; rdataDNSKey *key; TrustAnchor *ta; RRVerifier *keyv; - rrsig = (rdataRRSig *)dv->rrsig->rdata; - // Walk all our trusted DS Records to see if we have a matching DNS KEY record that verifies // the hash. If we find one, verify that this key was used to sign the KEY rrsets in // this zone. Loop till we find one. diff --git a/mDNSCore/dnssec.h b/mDNSCore/dnssec.h index 1a8d953..b770af8 100644 --- a/mDNSCore/dnssec.h +++ b/mDNSCore/dnssec.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef __DNSSEC_H #define __DNSSEC_H diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index e0af1f1..a58a6c1 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -47,9 +47,11 @@ #include "dns_sd.h" // for kDNSServiceFlags* definitions #if APPLE_OSX_mDNSResponder - #include +// Delay in seconds before disabling multicast after there are no active queries or registrations. +#define BONJOUR_DISABLE_DELAY 60 + #if !NO_WCF WCFConnection *WCFConnectionNew(void) __attribute__((weak_import)); void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); @@ -63,10 +65,14 @@ void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); #define NO_WCF 1 #endif // APPLE_OSX_mDNSResponder +#if TARGET_OS_EMBEDDED +#include "Metrics.h" +#endif + // Forward declarations mDNSlocal void BeginSleepProcessing(mDNS *const m); mDNSlocal void RetrySPSRegistrations(mDNS *const m); -mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password); +mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly); mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q); @@ -80,6 +86,7 @@ mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m); mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords); mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records); +mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth); // *************************************************************************** @@ -102,16 +109,39 @@ mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const resp #define NR_AnswerMulticast (mDNSu8*)~0 #define NR_AnswerUnicast (mDNSu8*)~1 -// Defined to set the kDNSQClass_UnicastResponse bit in the first four query packets. -// else, it's just set it the first query. -#define mDNS_REQUEST_UNICAST_RESPONSE 0 - // The code (see SendQueries() and BuildQuestion()) needs to have the // RequestUnicast value set to a value one greater than the number of times you want the query // sent with the "request unicast response" (QU) bit set. #define SET_QU_IN_FIRST_QUERY 2 -#define SET_QU_IN_FIRST_FOUR_QUERIES 5 +#define kDefaultRequestUnicastCount SET_QU_IN_FIRST_QUERY + +// The time needed to offload records to a sleep proxy after powerd sends the kIOMessageSystemWillSleep notification +#define DARK_WAKE_DELAY_SLEEP 5 +#define kDarkWakeDelaySleep (mDNSPlatformOneSecond * DARK_WAKE_DELAY_SLEEP) + +// The maximum number of times we delay probing to prevent spurious conflicts due to stale packets +#define MAX_CONFLICT_PROCESSING_DELAYS 3 +// RFC 6762 defines Passive Observation Of Failures (POOF) +// +// A host observes the multicast queries issued by the other hosts on +// the network. One of the major benefits of also sending responses +// using multicast is that it allows all hosts to see the responses +// (or lack thereof) to those queries. +// +// If a host sees queries, for which a record in its cache would be +// expected to be given as an answer in a multicast response, but no +// such answer is seen, then the host may take this as an indication +// that the record may no longer be valid. +// +// After seeing two or more of these queries, and seeing no multicast +// response containing the expected answer within ten seconds, then even +// though its TTL may indicate that it is not yet due to expire, that +// record SHOULD be flushed from the cache. +// +// + +#define POOF_ENABLED 1 mDNSexport const char *const mDNS_DomainTypeNames[] = { @@ -146,10 +176,6 @@ mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q) { mDNS_CheckLock(m); -#if ForceAlerts - if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; -#endif - if (m->NextScheduledStopTime - q->StopTime > 0) m->NextScheduledStopTime = q->StopTime; } @@ -158,10 +184,6 @@ mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) { mDNS_CheckLock(m); -#if ForceAlerts - if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; -#endif - if (ActiveQuestion(q)) { // Depending on whether this is a multicast or unicast question we want to set either: @@ -298,13 +320,13 @@ mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const Resourc // Returns the AuthGroup in which the AuthRecord was inserted mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) { + (void)m; AuthGroup *ag; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); ag = AuthGroupForRecord(r, slot, &rr->resrec); if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec); // If we don't have a AuthGroup for this name, make one now if (ag) { - LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr)); *(ag->rrauth_tail) = rr; // Append this record to tail of cache slot list ag->rrauth_tail = &(rr->next); // Advance tail pointer } @@ -314,13 +336,12 @@ mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *r mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) { AuthGroup *a; - AuthGroup **ag = &a; AuthRecord **rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; } - rp = &(*ag)->members; + rp = &a->members; while (*rp) { if (*rp != rr) @@ -334,7 +355,7 @@ mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *r } } // TBD: If there are no more members, release authgroup ? - (*ag)->rrauth_tail = rp; + a->rrauth_tail = rp; return a; } @@ -352,7 +373,7 @@ mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slo return(CacheGroupForName(m, slot, rr->namehash, rr->name)); } -mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself) +mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) { NetworkInterfaceInfo *intf; @@ -363,44 +384,19 @@ mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterface for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) - { - if (myself) - { - if (mDNSSameIPv4Address(intf->ip.ip.v4, addr->ip.v4)) - *myself = mDNStrue; - else - *myself = mDNSfalse; - if (*myself) - debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning true", addr); - else - debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning false", addr); - } return(mDNStrue); - } } if (addr->type == mDNSAddrType_IPv6) { + if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue); for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) - { - if (myself) - { - if (mDNSSameIPv6Address(intf->ip.ip.v6, addr->ip.v6)) - *myself = mDNStrue; - else - *myself = mDNSfalse; - if (*myself) - debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning true", addr); - else - debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning false", addr); - } return(mDNStrue); - } } return(mDNSfalse); @@ -441,14 +437,15 @@ mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID Interfa } // Caller should hold the lock -mDNSlocal void GenerateNegativeResponse(mDNS *const m, QC_result qc) +mDNSlocal void GenerateNegativeResponse(mDNS *const m, mDNSInterfaceID InterfaceID, QC_result qc) { DNSQuestion *q; if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; } q = m->CurrentQuestion; LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, InterfaceID, mDNSNULL); + // We need to force the response through in the following cases // // a) SuppressUnusable questions that are suppressed @@ -475,20 +472,11 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value UDPSocket *sock = q->LocalSocket; mDNSOpaque16 id = q->TargetQID; +#if TARGET_OS_EMBEDDED + uDNSMetrics metrics; +#endif - // if there is a message waiting at the socket, we want to process that instead - // of throwing it away. If we have a CNAME response that answers - // both A and AAAA question and while answering it we don't want to throw - // away the response where the actual addresses are present. - if (mDNSPlatformPeekUDP(m, q->LocalSocket)) - { - LogInfo("AnswerQuestionByFollowingCNAME: Preserving UDP socket for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - q->LocalSocket = mDNSNULL; - } - else - { - sock = mDNSNULL; - } + q->LocalSocket = mDNSNULL; // The SameDomainName check above is to ignore bogus CNAME records that point right back at // themselves. Without that check we can get into a case where we have two duplicate questions, @@ -508,6 +496,26 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s", q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr)); +#if TARGET_OS_EMBEDDED + if ((q->CNAMEReferrals == 0) && !q->metrics.originalQName) + { + domainname * qName; + mDNSu16 qNameLen; + + qNameLen = DomainNameLength(&q->qname); + if ((qNameLen > 0) && (qNameLen <= MAX_DOMAIN_NAME)) + { + qName = mDNSPlatformMemAllocate(qNameLen); + if (qName) + { + mDNSPlatformMemCopy(qName->c, q->qname.c, qNameLen); + q->metrics.originalQName = qName; + } + } + } + metrics = q->metrics; + mDNSPlatformMemZero(&q->metrics, sizeof(q->metrics)); +#endif mDNS_StopQuery_internal(m, q); // Stop old query AssignDomainName(&q->qname, &rr->rdata->u.name); // Update qname q->qnamehash = DomainNameHashValue(&q->qname); // and namehash @@ -524,17 +532,97 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal, // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero q->CNAMEReferrals = c; +#if TARGET_OS_EMBEDDED + q->metrics = metrics; +#endif if (sock) { - // We have a message waiting and that should answer this question. - if (q->LocalSocket) - mDNSPlatformUDPClose(q->LocalSocket); - q->LocalSocket = sock; - q->TargetQID = id; + // If our new query is a duplicate, then it can't have a socket of its own, so we have to close the one we saved. + if (q->DuplicateOf) mDNSPlatformUDPClose(sock); + else + { + // Transplant the old socket into the new question, and copy the query ID across too. + // No need to close the old q->LocalSocket value because it won't have been created yet (they're made lazily on-demand). + q->LocalSocket = sock; + q->TargetQID = id; + } } } } +#ifdef USE_LIBIDN + +#include + +// #define DEBUG_PUNYCODE 1 + +mDNSlocal mDNSu8 *PunycodeConvert(const mDNSu8 *const src, mDNSu8 *const dst, const mDNSu8 *const end) +{ + UErrorCode errorCode = U_ZERO_ERROR; + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + UIDNA *uts46 = uidna_openUTS46(UIDNA_USE_STD3_RULES|UIDNA_NONTRANSITIONAL_TO_UNICODE, &errorCode); + int32_t len = uidna_nameToASCII_UTF8(uts46, (const char *)src+1, src[0], (char *)dst+1, end-(dst+1), &info, &errorCode); + uidna_close(uts46); + #if DEBUG_PUNYCODE + if (errorCode) LogMsg("uidna_nameToASCII_UTF8(%##s) failed errorCode %d", src, errorCode); + if (info.errors) LogMsg("uidna_nameToASCII_UTF8(%##s) failed info.errors 0x%08X", src, info.errors); + if (len > MAX_DOMAIN_LABEL) LogMsg("uidna_nameToASCII_UTF8(%##s) result too long %d", src, len); + #endif + if (errorCode || info.errors || len > MAX_DOMAIN_LABEL) return mDNSNULL; + *dst = len; + return(dst + 1 + len); +} + +mDNSlocal mDNSBool IsHighASCIILabel(const mDNSu8 *d) +{ + int i; + for (i=1; i<=d[0]; i++) if (d[i] & 0x80) return mDNStrue; + return mDNSfalse; +} + +mDNSlocal const mDNSu8 *FindLastHighASCIILabel(const domainname *const d) +{ + const mDNSu8 *ptr = d->c; + const mDNSu8 *ans = mDNSNULL; + while (ptr[0]) + { + const mDNSu8 *const next = ptr + 1 + ptr[0]; + if (ptr[0] > MAX_DOMAIN_LABEL || next >= d->c + MAX_DOMAIN_NAME) return mDNSNULL; + if (IsHighASCIILabel(ptr)) ans = ptr; + ptr = next; + } + return ans; +} + +mDNSlocal mDNSBool PerformNextPunycodeConversion(const DNSQuestion *const q, domainname *const newname) +{ + const mDNSu8 *h = FindLastHighASCIILabel(&q->qname); + #if DEBUG_PUNYCODE + LogMsg("PerformNextPunycodeConversion: %##s (%s) Last High-ASCII Label %##s", q->qname.c, DNSTypeName(q->qtype), h); + #endif + if (!h) return mDNSfalse; // There are no high-ascii labels to convert + + mDNSu8 *const dst = PunycodeConvert(h, newname->c + (h - q->qname.c), newname->c + MAX_DOMAIN_NAME); + if (!dst) + return mDNSfalse; // The label was not convertible to Punycode + else + { + // If Punycode conversion of final eligible label was successful, copy the rest of the domainname + const mDNSu8 *const src = h + 1 + h[0]; + const mDNSu8 remainder = DomainNameLength((domainname*)src); + if (dst + remainder > newname->c + MAX_DOMAIN_NAME) return mDNSfalse; // Name too long -- cannot be converted to Punycode + + mDNSPlatformMemCopy(newname->c, q->qname.c, h - q->qname.c); // Fill in the leading part + mDNSPlatformMemCopy(dst, src, remainder); // Fill in the trailing part + #if DEBUG_PUNYCODE + LogMsg("PerformNextPunycodeConversion: %##s converted to %##s", q->qname.c, newname->c); + #endif + return mDNStrue; + } +} + +#endif // USE_LIBIDN + // For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord // Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) @@ -758,6 +846,8 @@ mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, cons pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) || authrr->WakeUp.HMAC.l[0]) if (pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); + if ((authrr->resrec.InterfaceID == mDNSInterface_Any) && + !mDNSPlatformValidRecordForInterface(authrr, pktrr->resrec.InterfaceID)) return(mDNSfalse); return (mDNSBool)( pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && @@ -835,11 +925,12 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) { // To allow us to aggregate probes when a group of services are registered together, - // the first probe is delayed 1/4 second. This means the common-case behaviour is: - // 1/4 second wait; probe + // the first probe is delayed by a random delay in the range 1/8 to 1/4 second. + // This means the common-case behaviour is: + // randomized wait; probe // 1/4 second wait; probe // 1/4 second wait; probe - // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) + // 1/4 second wait; announce (i.e. service is normally announced 7/8 to 1 second after being registered) m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); // If we already have a *probe* scheduled to go out sooner, then use that time to get better aggregation @@ -871,7 +962,9 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) } rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; } - else if (m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) + // Skip kDNSRecordTypeKnownUnique records here and set their LastAPTime in the "else" block below so that they get announced immediately, + // otherwise, their announcement would be delayed until all other record probes complete. + else if ((rr->resrec.RecordType != kDNSRecordTypeKnownUnique) && m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; else rr->LastAPTime = m->timenow - rr->ThisAPInterval; @@ -882,7 +975,7 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) // and we can begin broadcasting our announcements to take over ownership of that IP address. // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address. - if (rr->AddressProxy.type) + if (rr->AddressProxy.type) rr->LastAPTime = m->timenow; // Set LastMCTime to now, to inhibit multicast responses @@ -933,7 +1026,7 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) { const domainname *const n = SetUnicastTargetToHostName(m, rr); if (n) newname = n; - else { target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } + else { if (target) target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; } } if (target && SameDomainName(target, newname)) @@ -1060,26 +1153,25 @@ mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr) { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; + const AuthGroup *a; + AuthRecord *rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) return mDNSNULL; - rp = &(*ag)->members; - while (*rp) + rp = a->members; + while (rp) { - if (!RecordIsLocalDuplicate(*rp, rr)) - rp=&(*rp)->next; + if (!RecordIsLocalDuplicate(rp, rr)) + rp = rp->next; else { - if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering) + if (rp->resrec.RecordType == kDNSRecordTypeDeregistering) { - (*rp)->AnnounceCount = 0; - rp=&(*rp)->next; + rp->AnnounceCount = 0; + rp = rp->next; } - else return *rp; + else return rp; } } return (mDNSNULL); @@ -1087,22 +1179,21 @@ mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr) mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr) { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; + const AuthGroup *a; + const AuthRecord *rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) return mDNSfalse; - rp = &(*ag)->members; - while (*rp) + rp = a->members; + while (rp) { const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; - const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp; - if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec)) + const AuthRecord *s2 = rp->RRSet ? rp->RRSet : rp; + if (s1 != s2 && SameResourceRecordSignature(rp, rr) && !IdenticalSameNameRecord(&rp->resrec, &rr->resrec)) return mDNStrue; else - rp=&(*rp)->next; + rp = rp->next; } return (mDNSfalse); } @@ -1110,21 +1201,20 @@ mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr) // checks to see if "rr" is already present mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; + const AuthGroup *a; + AuthRecord *rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) return mDNSNULL; - rp = &(*ag)->members; - while (*rp) + rp = a->members; + while (rp) { - if (*rp != rr) - rp=&(*rp)->next; + if (rp != rr) + rp = rp->next; else { - return *rp; + return rp; } } return (mDNSNULL); @@ -1133,37 +1223,91 @@ mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) { + if (RRLocalOnly(rr)) + { + // A sanity check, this should be prevented in calling code. + LogInfo("DecrementAutoTargetServices: called for RRLocalOnly() record: %s", ARDisplayString(m, rr)); + return; + } + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) { - m->AutoTargetServices--; - LogInfo("DecrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); - if (!m->AutoTargetServices) + // If about to get rid of the last advertised service + if (m->AutoTargetServices == 1) DeadvertiseAllInterfaceRecords(m); + + m->AutoTargetServices--; + LogInfo("DecrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); + } + +#if BONJOUR_ON_DEMAND + if (!AuthRecord_uDNS(rr)) + { + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + m->NextBonjourDisableTime = NonZeroTime(m->timenow + (BONJOUR_DISABLE_DELAY * mDNSPlatformOneSecond)); + m->NumAllInterfaceRecords--; + LogInfo("DecrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); } +#endif // BONJOUR_ON_DEMAND } mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) { - if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + mDNSBool enablingBonjour = 0; + + if (RRLocalOnly(rr)) { - int count = m->AutoTargetServices; + // A sanity check, this should be prevented in calling code. + LogInfo("IncrementAutoTargetServices: called for RRLocalOnly() record: %s", ARDisplayString(m, rr)); + return; + } + +#if BONJOUR_ON_DEMAND + if (!AuthRecord_uDNS(rr)) + { + m->NumAllInterfaceRecords++; + LogInfo("IncrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + { + m->NextBonjourDisableTime = 0; + if (m->BonjourEnabled == 0) + { + // Enable Bonjour immediately by scheduling network changed processing where + // we will join the multicast group on each active interface. + m->BonjourEnabled = 1; + enablingBonjour = 1; + m->NetworkChanged = m->timenow; + } + } + } +#endif // BONJOUR_ON_DEMAND - // Bump up before calling AdvertiseAllInterfaceRecords. AdvertiseInterface - // returns without doing anything if the count is zero. + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + { m->AutoTargetServices++; - LogInfo("IncrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); - if (!count) + LogInfo("IncrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); + + // If this is the first advertised service and we did not just enable Bonjour above, then + // advertise all the interface records. If we did enable Bonjour above, the interface records will + // be advertised during the network changed processing scheduled above, so no need + // to do it here. + if ((m->AutoTargetServices == 1) && (enablingBonjour == 0)) AdvertiseAllInterfaceRecords(m); } } mDNSlocal void getKeepaliveRaddr(mDNS *const m, AuthRecord *rr, mDNSAddr *raddr) { - mDNSAddr laddr; - mDNSEthAddr eth; - mDNSIPPort lport, rport; - mDNSu32 timeout, seq, ack; - mDNSu16 win; + mDNSAddr laddr = zeroAddr; + mDNSEthAddr eth = zeroEthAddr; + mDNSIPPort lport = zeroIPPort; + mDNSIPPort rport = zeroIPPort; + mDNSu32 timeout = 0; + mDNSu32 seq = 0; + mDNSu32 ack = 0; + mDNSu16 win = 0; if (mDNS_KeepaliveRecord(&rr->resrec)) { @@ -1262,15 +1406,15 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // Set up by client prior to call // Field Group 2: Persistent metadata for Authoritative Records -// rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->Callback = already set in mDNS_SetupResourceRecord -// rr->Context = already set in mDNS_SetupResourceRecord -// rr->RecordType = already set in mDNS_SetupResourceRecord -// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client -// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client +// rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Callback = already set in mDNS_SetupResourceRecord +// rr->Context = already set in mDNS_SetupResourceRecord +// rr->RecordType = already set in mDNS_SetupResourceRecord +// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client +// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client // Make sure target is not uninitialized data, or we may crash writing debugging log messages if (rr->AutoTarget && target) target->c[0] = 0; @@ -1293,9 +1437,9 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; if (!rr->AutoTarget) InitializeLastAPTime(m, rr); -// rr->LastAPTime = Set for us in InitializeLastAPTime() -// rr->LastMCTime = Set for us in InitializeLastAPTime() -// rr->LastMCInterface = Set for us in InitializeLastAPTime() +// rr->LastAPTime = Set for us in InitializeLastAPTime() +// rr->LastMCTime = Set for us in InitializeLastAPTime() +// rr->LastMCInterface = Set for us in InitializeLastAPTime() rr->NewRData = mDNSNULL; rr->newrdlength = 0; rr->UpdateCallback = mDNSNULL; @@ -1328,12 +1472,12 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // times with different values if the external NAT port changes during the lifetime of the service registration. //if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; -// rr->resrec.interface = already set in mDNS_SetupResourceRecord -// rr->resrec.name->c = MUST be set by client -// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord -// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord -// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord -// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set +// rr->resrec.interface = already set in mDNS_SetupResourceRecord +// rr->resrec.name->c = MUST be set by client +// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord +// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord +// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord +// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set // 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 s", not "Zero or more s". @@ -1378,6 +1522,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more // complications and not clear whether there are any benefits. See rdar:9304275 for details. // Hence, just bail out. + // This comment is doesn’t make any sense. -- SC if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) { if (CheckAuthRecordConflict(&m->rrauth, rr)) @@ -1427,7 +1572,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) if (r) { - debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr)); + LogInfo("mDNS_Register_internal: Adding to duplicate list %s", ARDisplayString(m,rr)); *d = rr; // If the previous copy of this record is already verified unique, // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. @@ -1438,16 +1583,17 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } else { - debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); + LogInfo("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); if (RRLocalOnly(rr)) { AuthGroup *ag; ag = InsertAuthRecord(m, &m->rrauth, rr); - if (ag && !ag->NewLocalOnlyRecords) { + if (ag && !ag->NewLocalOnlyRecords) + { m->NewLocalOnlyRecords = mDNStrue; ag->NewLocalOnlyRecords = rr; } - // No probing for LocalOnly records, Acknowledge them right away + // No probing for LocalOnly records; acknowledge them right away if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; AcknowledgeRecord(m, rr); return(mStatus_NoError); @@ -1459,33 +1605,34 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } } - // If this is a keepalive record, fetch the MAC address of the remote host. + if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above + { + // We have inserted the record in the list. See if we have to advertise the A/AAAA, HINFO, PTR records. + IncrementAutoTargetServices(m, rr); + + // For records that are not going to probe, acknowledge them right away + if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) + AcknowledgeRecord(m, rr); + + // Adding a record may affect whether or not we should sleep + mDNS_UpdateAllowSleep(m); + } + + // If this is a non-sleep proxy keepalive record, fetch the MAC address of the remote host. // This is used by the in-NIC proxy to send the keepalive packets. - if (mDNS_KeepaliveRecord(&rr->resrec)) + if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec)) { + mDNSAddr raddr; // Set the record type to known unique to prevent probing keep alive records. // Also make sure we do not announce the keepalive records. rr->resrec.RecordType = kDNSRecordTypeKnownUnique; rr->AnnounceCount = 0; - mDNSAddr raddr; getKeepaliveRaddr(m, rr, &raddr); // This is an asynchronous call. Once the remote MAC address is available, helper will schedule an // asynchronous task to update the resource record mDNSPlatformGetRemoteMacAddr(m, &raddr); } - if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above - { - // We have inserted the record in the list. See if we have to advertise the A/AAAA,HINFO,PTR records. - IncrementAutoTargetServices(m, rr); - // For records that are not going to probe, acknowledge them right away - if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) - AcknowledgeRecord(m, rr); - - // Adding a record may affect whether or not we should sleep - mDNS_UpdateAllowSleep(m); - } - return(mStatus_NoError); } @@ -1531,13 +1678,12 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (RRLocalOnly(rr)) { AuthGroup *a; - AuthGroup **ag = &a; AuthRecord **rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); if (!a) return mDNSfalse; - rp = &(*ag)->members; + rp = &a->members; while (*rp && *rp != rr) rp=&(*rp)->next; p = rp; } @@ -1747,7 +1893,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (drt != mDNS_Dereg_conflict) { mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - LogInfo("mDNS_Deregister_internal: mStatus_MemFree for %s", ARDisplayString(m, rr)); + LogInfo("mDNS_Deregister_internal: callback with mStatus_MemFree for %s", ARDisplayString(m, rr)); if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again @@ -1771,6 +1917,10 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, } else { +#if APPLE_OSX_mDNSResponder + // See if this record was also registered with any D2D plugins. + D2D_stop_advertising_record(r2); +#endif mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); // As this is a duplicate record, it will be unlinked from the list // immediately @@ -1881,9 +2031,8 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d rr->v6Requester = zerov6Addr; // Only sent records registered for P2P over P2P interfaces - if (intf && !mDNSPlatformValidRecordForInterface(rr, intf)) + if (intf && !mDNSPlatformValidRecordForInterface(rr, intf->InterfaceID)) { - LogInfo("SendDelayedUnicastResponse: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, InterfaceID)); continue; } @@ -2297,6 +2446,22 @@ mDNSlocal mDNSBool ShouldSendGoodbyesBeforeSleep(mDNS *const m, const NetworkInt } } +mDNSlocal mDNSBool IsInterfaceValidForAuthRecord(const AuthRecord *ar, mDNSInterfaceID InterfaceID) +{ + mDNSBool result; + + if (ar->resrec.InterfaceID == mDNSInterface_Any) + { + result = mDNSPlatformValidRecordForInterface(ar, InterfaceID); + } + else + { + result = (ar->resrec.InterfaceID == InterfaceID); + } + + return(result); +} + // Note about acceleration of announcements to facilitate automatic coalescing of // multiple independent threads of announcements into a single synchronized thread: // The announcements in the packet may be at different stages of maturity; @@ -2358,11 +2523,13 @@ mDNSlocal void SendResponses(mDNS *const m) } else { + mDNSBool unicastOnly; LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); + unicastOnly = ((rr->AnnounceCount == WakeupCount) || (rr->AnnounceCount == WakeupCount - 1)) ? mDNStrue : mDNSfalse; + SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password, unicastOnly); for (r2 = rr; r2; r2=r2->next) - if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) && - !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC)) + if ((r2->resrec.RecordType == kDNSRecordTypeDeregistering) && r2->AnnounceCount && (r2->resrec.InterfaceID == rr->resrec.InterfaceID) && + mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) && !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC)) { // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict. @@ -2460,17 +2627,28 @@ mDNSlocal void SendResponses(mDNS *const m) if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked { for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (ResourceRecordIsValidAnswer(r2)) - if (r2->ImmedAnswer != mDNSInterfaceMark && - r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(r2, rr)) - r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark; + { + if ((r2->resrec.RecordType & kDNSRecordTypeUniqueMask) && ResourceRecordIsValidAnswer(r2) && + (r2->ImmedAnswer != mDNSInterfaceMark) && (r2->ImmedAnswer != rr->ImmedAnswer) && + SameResourceRecordSignature(r2, rr) && + ((rr->ImmedAnswer == mDNSInterfaceMark) || IsInterfaceValidForAuthRecord(r2, rr->ImmedAnswer))) + { + r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark; + } + } } else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked { for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (ResourceRecordIsValidAnswer(r2)) - if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(r2, rr)) - r2->ImmedAdditional = rr->ImmedAdditional; + { + if ((r2->resrec.RecordType & kDNSRecordTypeUniqueMask) && ResourceRecordIsValidAnswer(r2) && + (r2->ImmedAdditional != rr->ImmedAdditional) && + SameResourceRecordSignature(r2, rr) && + IsInterfaceValidForAuthRecord(r2, rr->ImmedAdditional)) + { + r2->ImmedAdditional = rr->ImmedAdditional; + } + } } } @@ -2483,6 +2661,7 @@ mDNSlocal void SendResponses(mDNS *const m) rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer rr->LastMCTime = m->timenow; rr->LastMCInterface = rr->ImmedAnswer; + rr->ProbeRestartCount = 0; // Reset the probe restart count // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) { @@ -2530,9 +2709,8 @@ mDNSlocal void SendResponses(mDNS *const m) // Skip this interface if the record InterfaceID is *Any and the record is not // appropriate for the interface type. if ((rr->SendRNow == intf->InterfaceID) && - ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf))) + ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf->InterfaceID))) { - LogInfo("SendResponses: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow)); rr->SendRNow = GetNextActiveInterfaceID(intf); } else if (rr->SendRNow == intf->InterfaceID) @@ -2605,7 +2783,7 @@ mDNSlocal void SendResponses(mDNS *const m) // Get the reserved space back OwnerRecordSpace -= AnoninfoSpace; - TraceRecordSpace -= AnoninfoSpace; + TraceRecordSpace -= AnoninfoSpace; newptr = responseptr; for (rr = m->ResourceRecords; rr; rr=rr->next) { @@ -2754,10 +2932,9 @@ mDNSlocal void SendResponses(mDNS *const m) SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]); } newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); - if (newptr) - { - responseptr = newptr; - LogInfo("SendResponses put %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt)); + if (newptr) + { + responseptr = newptr; } else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1) { @@ -2770,7 +2947,7 @@ mDNSlocal void SendResponses(mDNS *const m) m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); } } - + debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", numDereg, numDereg == 1 ? "" : "s", numAnnounce, numAnnounce == 1 ? "" : "s", @@ -2917,10 +3094,10 @@ mDNSexport mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. // It also appends to the list of known answer records that need to be included, // and updates the forcast for the size of the known answer section. -mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q, - CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) +mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf, DNSMessage *query, mDNSu8 **queryptr, + DNSQuestion *q, CacheRecord ***kalistptrptr, mDNSu32 *answerforecast) { - mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; + mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353 && intf->SupportsUnicastMDNSResponse; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; mDNSu8 anoninfo_space = q->AnonInfo ? AnonInfoSpace(q->AnonInfo) : 0; @@ -3051,36 +3228,36 @@ mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *c } return(bestcr); #else // SPC_DISABLED - (void) m; - (void) q; - (void) c0; - (void) c1; - (void) c1; + (void) m; + (void) q; + (void) c0; + (void) c1; + (void) c1; return mDNSNULL; #endif // SPC_DISABLED } -mDNSlocal void CheckAndSwapSPS(const CacheRecord *sps1, const CacheRecord *sps2) +mDNSlocal void CheckAndSwapSPS(const CacheRecord **sps1, const CacheRecord **sps2) { const CacheRecord *swap_sps; mDNSu32 metric1, metric2; - if (!sps1 || !sps2) return; - metric1 = SPSMetric(sps1->resrec.rdata->u.name.c); - metric2 = SPSMetric(sps2->resrec.rdata->u.name.c); - if (!SPSFeatures(sps1->resrec.rdata->u.name.c) && SPSFeatures(sps2->resrec.rdata->u.name.c) && (metric2 >= metric1)) + if (!(*sps1) || !(*sps2)) return; + metric1 = SPSMetric((*sps1)->resrec.rdata->u.name.c); + metric2 = SPSMetric((*sps2)->resrec.rdata->u.name.c); + if (!SPSFeatures((*sps1)->resrec.rdata->u.name.c) && SPSFeatures((*sps2)->resrec.rdata->u.name.c) && (metric2 >= metric1)) { - swap_sps = sps1; - sps1 = sps2; - sps2 = swap_sps; + swap_sps = *sps1; + *sps1 = *sps2; + *sps2 = swap_sps; } } mDNSlocal void ReorderSPSByFeature(const CacheRecord *sps[3]) { - CheckAndSwapSPS(sps[0], sps[1]); - CheckAndSwapSPS(sps[0], sps[2]); - CheckAndSwapSPS(sps[1], sps[2]); + CheckAndSwapSPS(&sps[0], &sps[1]); + CheckAndSwapSPS(&sps[0], &sps[2]); + CheckAndSwapSPS(&sps[1], &sps[2]); } @@ -3124,7 +3301,7 @@ mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressI return(mDNSfalse); } -mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) +mDNSlocal void RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type) { int i, j; @@ -3144,8 +3321,6 @@ mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDN ds[i].Time = Time; ds[i].InterfaceID = InterfaceID; ds[i].Type = Type; - - return(i); } mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) @@ -3155,7 +3330,7 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) domainname *d = &q->qname; // We can't send magic packets without knowing which interface to send it on. - if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P) + if (InterfaceID == mDNSInterface_Any || LocalOnlyOrP2PInterface(InterfaceID)) { LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c); return; @@ -3163,7 +3338,6 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) // Split MAC@IPAddress and pass them separately len = d->c[0]; - i = 1; cnt = 0; for (i = 1; i < len; i++) { @@ -3372,7 +3546,7 @@ mDNSlocal void SendQueries(mDNS *const m) // don't send it again until MaxQuestionInterval unless: // one of its cached answers needs to be refreshed, // or it's the initial query for a kDNSServiceFlagsThresholdFinder mode browse. - if (q->BrowseThreshold + if (q->BrowseThreshold && (q->CurrentAnswers >= q->BrowseThreshold) && (q->CachedAnswerNeedsUpdate == mDNSfalse) && !((q->flags & kDNSServiceFlagsThresholdFinder) && (q->ThisQInterval == InitialQuestionInterval))) @@ -3394,7 +3568,7 @@ mDNSlocal void SendQueries(mDNS *const m) debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); - if (q->ThisQInterval >= QuestionIntervalThreshold) + if (q->ThisQInterval > MaxQuestionInterval) { q->ThisQInterval = MaxQuestionInterval; } @@ -3457,11 +3631,11 @@ mDNSlocal void SendQueries(mDNS *const m) { if (ar->AddressProxy.type == mDNSAddrType_IPv4) { - // There's a problem here. If a host is waking up, and we probe to see if it responds, then - // it will see those ARP probes as signalling intent to use the address, so it picks a different one. - // A more benign way to find out if a host is responding to ARPs might be send a standard ARP *request* - // (using our sender IP address) instead of an ARP *probe* (using all-zero sender IP address). - // A similar concern may apply to the NDP Probe too. -- SC + // There's a problem here. If a host is waking up, and we probe to see if it responds, then + // it will see those ARP probes as signalling intent to use the address, so it picks a different one. + // A more benign way to find out if a host is responding to ARPs might be send a standard ARP *request* + // (using our sender IP address) instead of an ARP *probe* (using all-zero sender IP address). + // A similar concern may apply to the NDP Probe too. -- SC LogSPS("SendQueries ARP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); SendARP(m, 1, ar, &zerov4Addr, &zeroEthAddr, &ar->AddressProxy.ip.v4, &ar->WakeUp.IMAC); } @@ -3553,12 +3727,11 @@ mDNSlocal void SendQueries(mDNS *const m) // If interface is P2P type, verify that query should be sent over it. if (!mDNSPlatformValidQuestionForInterface(q, intf)) { - LogInfo("SendQueries: Not sending (%s) %##s on %s", DNSTypeName(q->qtype), q->qname.c, InterfaceNameForID(m, intf->InterfaceID)); q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); } // If we're suppressing this question, or we successfully put it, update its SendQNow state else if ((Suppress = SuppressOnThisInterface(q->DupSuppress, intf)) || - BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) + BuildQuestion(m, intf, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) { // We successfully added the question to the packet. Make sure that // we also send the NSEC3 record if required. BuildQuestion accounted for @@ -3582,7 +3755,7 @@ mDNSlocal void SendQueries(mDNS *const m) q->WakeOnResolveCount--; } - // use brackground traffic class if any included question requires it + // use background traffic class if any included question requires it if (q->UseBackgroundTrafficClass) { useBackgroundTrafficClass = mDNStrue; @@ -3593,24 +3766,62 @@ mDNSlocal void SendQueries(mDNS *const m) // Put probe questions in this packet for (ar = m->ResourceRecords; ar; ar=ar->next) - if (ar->SendRNow == intf->InterfaceID) + { + if (ar->SendRNow != intf->InterfaceID) + continue; + + // If interface is a P2P variant, verify that the probe should be sent over it. + if (!mDNSPlatformValidRecordForInterface(ar, intf->InterfaceID)) { - mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353; + ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + ar->IncludeInProbe = mDNSfalse; + } + else + { + mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353 && intf->SupportsUnicastMDNSResponse; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) mDNSu32 forecast = answerforecast + 12 + ar->resrec.rdestimate; - mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, (mDNSu16)(ar->resrec.rrclass | ucbit)); - if (newptr) + mDNSBool putProbe = mDNStrue; + mDNSu16 qclass = ar->resrec.rrclass | ucbit; + + {// Determine if this probe question is already in packet's dns message + const mDNSu8 *questionptr = m->omsg.data; + DNSQuestion question; + mDNSu16 n; + for (n = 0; n < m->omsg.h.numQuestions && questionptr; n++) + { + questionptr = getQuestion(&m->omsg, questionptr, limit, mDNSInterface_Any, &question); + if (questionptr && (question.qtype == kDNSQType_ANY) && (question.qclass == qclass) && + (question.qnamehash == ar->resrec.namehash) && SameDomainName(&question.qname, ar->resrec.name)) + { + putProbe = mDNSfalse; // set to false if already in message + break; + } + } + } + + if (putProbe) + { + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, qclass); + if (newptr) + { + queryptr = newptr; + answerforecast = forecast; + ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + ar->IncludeInProbe = mDNStrue; + verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d InterfaceID= %d %d %d", + ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount, ar->resrec.InterfaceID, ar->resrec.rdestimate, answerforecast); + } + } + else { - queryptr = newptr; - answerforecast = forecast; ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); ar->IncludeInProbe = mDNStrue; - verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", - ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount); } } + } } // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) @@ -3676,7 +3887,7 @@ mDNSlocal void SendQueries(mDNS *const m) AuthRecord opt; mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); + opt.resrec.rdlength = sizeof(rdataOPT); opt.resrec.rdestimate = sizeof(rdataOPT); if (OwnerRecordSpace && TraceRecordSpace) { @@ -3693,19 +3904,18 @@ mDNSlocal void SendQueries(mDNS *const m) { SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]); } - LogInfo("SendQueries putting %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt)); queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); if (!queryptr) - { + { LogMsg("SendQueries: How did we fail to have space for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); } if (queryptr > m->omsg.data + NormalMaxDNSMessageData) { if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1) - LogMsg("SendQueries: Why did we generate oversized packet with %s %s OPT record %p %p %p (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", - TraceRecordSpace ? "TRACER" : "", m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, m->omsg.h.numQuestions, m->omsg.h.numAnswers, + LogMsg("SendQueries: Why did we generate oversized packet with %s %s OPT record %p %p %p (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", + TraceRecordSpace ? "TRACER" : "", m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); } } @@ -3774,17 +3984,21 @@ mDNSlocal void SendQueries(mDNS *const m) { DNSQuestion *x; for (x = m->NewQuestions; x; x=x->next) if (x == q) break; // Check if this question is a NewQuestion - LogInfo("SendQueries: No active interface %d to send %s question: %d %##s (%s)", - (uint32_t)q->SendQNow, x ? "new" : "old", (uint32_t)q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + // There will not be an active interface for questions applied to mDNSInterface_BLE + // so don't log the warning in that case. + if (q->InterfaceID != mDNSInterface_BLE) + LogInfo("SendQueries: No active interface %d to send %s question: %d %##s (%s)", + (uint32_t)q->SendQNow, x ? "new" : "old", (uint32_t)q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); q->SendQNow = mDNSNULL; } q->CachedAnswerNeedsUpdate = mDNSfalse; } } -mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password) +mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly) { int i, j; + mDNSu8 *ptr = m->omsg.data; NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found", InterfaceID); return; } @@ -3810,13 +4024,16 @@ mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAdd mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); - // For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses, - // broadcast is the only reliable way to get a wakeup packet to the intended target machine. - // For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast - // key rotation, unicast is the only way to get a wakeup packet to the intended target machine. - // So, we send one of each, unicast first, then broadcast second. - for (i=0; i<6; i++) m->omsg.data[i] = 0xFF; - mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); + if (!unicastOnly) + { + // For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses, + // broadcast is the only reliable way to get a wakeup packet to the intended target machine. + // For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast + // key rotation, unicast is the only way to get a wakeup packet to the intended target machine. + // So, we send one of each, unicast first, then broadcast second. + for (i=0; i<6; i++) m->omsg.data[i] = 0xFF; + mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); + } } // *************************************************************************** @@ -3848,7 +4065,7 @@ mDNSlocal void ResetQuestionState(mDNS *const m, DNSQuestion *q) mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord) { DNSQuestion *const q = m->CurrentQuestion; - mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord); + const mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord); verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr)); @@ -3893,6 +4110,35 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco return; } +#if TARGET_OS_EMBEDDED + if ((AddRecord == QC_add) && Question_uDNS(q) && !followcname) + { + const domainname * queryName; + mDNSu32 responseLatencyMs; + mDNSBool isForCellular; + + queryName = q->metrics.originalQName ? q->metrics.originalQName : &q->qname; + isForCellular = (q->qDNSServer && q->qDNSServer->cellIntf); + if (!q->metrics.answered) + { + if (q->metrics.querySendCount > 0) + { + responseLatencyMs = ((m->timenow - q->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; + } + else + { + responseLatencyMs = 0; + } + + MetricsUpdateUDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, responseLatencyMs, isForCellular); + q->metrics.answered = mDNStrue; + } + if (q->metrics.querySendCount > 0) + { + MetricsUpdateUDNSResolveStats(queryName, &rr->resrec, isForCellular); + } + } +#endif // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue) // may be called twice, once when the record is received, and again when it's time to notify local clients. // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this. @@ -3923,6 +4169,30 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us +#ifdef USE_LIBIDN + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) // If negative answer, check if we need to try Punycode conversion + { + domainname newname; + if (PerformNextPunycodeConversion(q, &newname)) // Itertative Punycode conversion succeeded, so reissue question with new name + { + UDPSocket *const sock = q->LocalSocket; // Save old socket and transaction ID + const mDNSOpaque16 id = q->TargetQID; + q->LocalSocket = mDNSNULL; + mDNS_StopQuery_internal(m, q); // Stop old query + AssignDomainName(&q->qname, &newname); // Update qname + q->qnamehash = DomainNameHashValue(&q->qname); // and namehash + mDNS_StartQuery_internal(m, q); // Start new query + + if (sock) // Transplant saved socket, if appropriate + { + if (q->DuplicateOf) mDNSPlatformUDPClose(sock); + else { q->LocalSocket = sock; q->TargetQID = id; } + } + return; // All done for now; wait until we get the next answer + } + } +#endif // USE_LIBIDN + // Only deliver negative answers if client has explicitly requested them except when we are forcing a negative response // for the purpose of retrying search domains/timeout OR the question is suppressed if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype))) @@ -3973,7 +4243,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG), // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated // first before following it - if (!ValidatingQuestion(q) && followcname && m->CurrentQuestion == q) + if ((m->CurrentQuestion == q) && followcname && !ValidatingQuestion(q)) AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); } @@ -4014,7 +4284,7 @@ mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *c // deliver a RMV (for the current old entry) followed by ADD (for the new entry). // It needs to schedule the timer for the next cache expiry (ScheduleNextCacheCheckTime), // so that the cache entry can be purged (purging causes the RMV followed by ADD) - // + // // 2) A new question is about to be answered and the caller needs to know whether it's // scheduling should be delayed so that the question is not answered with this record. // Instead of delivering an ADD (old entry) followed by RMV (old entry) and another ADD @@ -4249,7 +4519,7 @@ mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp) if ((*cp)->rrcache_tail != &(*cp)->members) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)"); //if ((*cp)->name != (domainname*)((*cp)->namestorage)) - // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); + // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); (*cp)->name = mDNSNULL; *cp = (*cp)->next; // Cut record from list @@ -4296,7 +4566,7 @@ mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) r->resrec.rdata = mDNSNULL; cg = CacheGroupForRecord(m, slot, &r->resrec); - + if (!cg) { // It is okay to have this printed for NSEC/NSEC3s @@ -4349,7 +4619,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou if (m->timenow - event >= 0) // If expired, delete it { *rp = rr->next; // Cut it from the list - + verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s", m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away @@ -4441,7 +4711,7 @@ mDNSlocal mDNSBool AnswerQuestionWithLORecord(mDNS *const m, DNSQuestion *q, mDN // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly // we handle both mDNSInterface_Any and scoped questions. - if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any)) + if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && (q->InterfaceID == mDNSInterface_Any || q->InterfaceID == mDNSInterface_BLE))) if (LocalOnlyRecordAnswersQuestion(rr, q)) { if (checkOnly) @@ -4509,7 +4779,7 @@ mDNSlocal void AnswerSuppressedQuestion(mDNS *const m, DNSQuestion *q) q->SuppressQuery = mDNSfalse; q->DisallowPID = mDNSfalse; - GenerateNegativeResponse(m, QC_suppressed); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_suppressed); q->SuppressQuery = SuppressQuery; q->DisallowPID = DisallowPID; @@ -4614,7 +4884,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } - else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) + else if (mDNSOpaque16IsZero(q->TargetQID) && RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) ShouldQueryImmediately = mDNSfalse; } // We don't use LogInfo for this "Question deleted" message because it happens so routinely that @@ -4628,7 +4898,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains) { LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - GenerateNegativeResponse(m, QC_forceresponse); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); } if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } @@ -4669,6 +4939,7 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) AuthGroup *ag; DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any) + mDNSBool retEv = mDNSfalse; debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -4694,6 +4965,7 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) m->CurrentRecord = rr->next; if (LocalOnlyRecordAnswersQuestion(rr, q)) { + retEv = mDNStrue; AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } @@ -4710,12 +4982,18 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) m->CurrentRecord = rr->next; if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { + retEv = mDNStrue; AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } } } + // The local host is the authoritative source for LocalOnly questions + // so if no records exist and client requested intermediates, then generate a negative response + if (!retEv && (m->CurrentQuestion == q) && q->ReturnIntermed) + GenerateNegativeResponse(m, mDNSInterface_LocalOnly, QC_forceresponse); + m->CurrentQuestion = mDNSNULL; m->CurrentRecord = mDNSNULL; } @@ -4830,7 +5108,7 @@ mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const Res cg->namehash = rr->namehash; cg->members = mDNSNULL; cg->rrcache_tail = &cg->members; - if (namelen > sizeof(cg->namestorage)) + if (namelen > sizeof(cg->namestorage)) cg->name = mDNSPlatformMemAllocate(namelen); else cg->name = (domainname*)cg->namestorage; @@ -4884,7 +5162,7 @@ mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m) // had its Sleep Proxy client list change, and defer to actual BPF reconfiguration to mDNS_Execute(). // (GetNextScheduledEvent() returns "now" when m->SPSProxyListChanged is set) #define SetSPSProxyListChanged(X) do { \ - if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m, m->SPSProxyListChanged); \ + if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m, m->SPSProxyListChanged); \ m->SPSProxyListChanged = (X); } while(0) // Called from mDNS_Execute() to expire stale proxy records @@ -4961,7 +5239,7 @@ mDNSlocal void TimeoutQuestions(mDNS *const m) if (m->timenow - q->StopTime >= 0) { LogInfo("TimeoutQuestions: question %p %##s timed out, time %d", q, q->qname.c, m->timenow - q->StopTime); - GenerateNegativeResponse(m, QC_forceresponse); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); if (m->CurrentQuestion == q) q->StopTime = 0; } else @@ -5067,10 +5345,26 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) mDNS_SendKeepalives(m); } +#if BONJOUR_ON_DEMAND + if (m->NextBonjourDisableTime && (m->timenow - m->NextBonjourDisableTime >= 0)) + { + // Schedule immediate network change processing to leave the multicast group + // since the delay time has expired since the previous active registration or query. + m->NetworkChanged = m->timenow; + m->NextBonjourDisableTime = 0; + m->BonjourEnabled = 0; + + LogInfo("mDNS_Execute: Scheduled network changed processing to leave multicast group."); + } +#endif // BONJOUR_ON_DEMAND + // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) { m->AnnounceOwner = 0; + + // This is a good time to reset the delay counter used to prevent spurious conflicts + m->DelayConflictProcessing = 0; } if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) @@ -5237,6 +5531,10 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m); if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m); #endif +#if APPLE_OSX_mDNSResponder + extern void serviceBLE(); + if (m->NextBLEServiceTime && (m->timenow - m->NextBLEServiceTime >= 0)) serviceBLE(); +#endif // APPLE_OSX_mDNSResponder } // Note about multi-threaded systems: @@ -5482,11 +5780,7 @@ mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q) if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) { q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question -#if mDNS_REQUEST_UNICAST_RESPONSE - q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; -#else // mDNS_REQUEST_UNICAST_RESPONSE - q->RequestUnicast = SET_QU_IN_FIRST_QUERY; -#endif // mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = kDefaultRequestUnicastCount; q->LastQTime = m->timenow - q->ThisQInterval; q->RecentAnswerPkts = 0; ExpireDupSuppressInfo(q->DupSuppress, m->timenow); @@ -5495,23 +5789,26 @@ mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q) } // restart the probe/announce cycle for multicast record -mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount) +mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount) { if (!AuthRecord_uDNS(rr)) { if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - // announceCount < 0 indicates default announce count should be used - if (announceCount < 0) - announceCount = InitialAnnounceCount; - if (rr->AnnounceCount < announceCount) - rr->AnnounceCount = announceCount; - if (mDNS_KeepaliveRecord(&rr->resrec)) - rr->AnnounceCount = 0; // Do not announce keepalive records + { + rr->AnnounceCount = 0; // Do not announce keepalive records + } else - rr->AnnounceCount = InitialAnnounceCount; + { + // announceCount < 0 indicates default announce count should be used + if (announceCount < 0) + announceCount = InitialAnnounceCount; + if (rr->AnnounceCount < (mDNSu8)announceCount) + rr->AnnounceCount = (mDNSu8)announceCount; + } + rr->SendNSECNow = mDNSNULL; InitializeLastAPTime(m, rr); } @@ -5558,26 +5855,34 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) break; } - // Disallow sleep if there is no sleep proxy server - const CacheRecord *cr = FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL); - if ( cr == mDNSNULL) - { - allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server on %s", intf->ifname); - LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server", intf->ifname); - break; - } - else if (m->SPSType != 0) + // If the interface can be an in-NIC Proxy, we should check if it can accomodate all the records + // that will be offloaded. If not, we should prevent sleep. + // This check will be possible once the lower layers provide an API to query the space available for offloads on the NIC. +#if APPLE_OSX_mDNSResponder + if (!SupportsInNICProxy(intf)) +#endif { - mDNSu32 mymetric = LocalSPSMetric(m); - mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); - if (metric >= mymetric) + // Disallow sleep if there is no sleep proxy server + const CacheRecord *cr = FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL); + if ( cr == mDNSNULL) { allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server with better metric on %s", intf->ifname); - LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server with a better metric", intf->ifname); + mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server on %s", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server", intf->ifname); break; } + else if (m->SPSType != 0) + { + mDNSu32 mymetric = LocalSPSMetric(m); + mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c); + if (metric >= mymetric) + { + allowSleep = mDNSfalse; + mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server with better metric on %s", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server with a better metric", intf->ifname); + break; + } + } } } } @@ -5596,7 +5901,7 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte // If it is not a uDNS record, check to see if the updateid is zero. "updateid" is cleared when we have // sent the resource record on all the interfaces. If the update id is not zero, check to see if it is time // to send. - if (AuthRecord_uDNS(rr) || (rr->AuthFlags & AuthFlagsWakeOnly) || mDNSOpaque16IsZero(rr->updateid) || + if (AuthRecord_uDNS(rr) || (rr->AuthFlags & AuthFlagsWakeOnly) || mDNSOpaque16IsZero(rr->updateid) || m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) { return mDNSfalse; @@ -5608,7 +5913,7 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte // updateid and we should have returned from above. // // Note: scopeid is the same as intf->InterfaceID. It is passed in so that we don't have to call the - // platform function to extract the value from "intf" everytime. + // platform function to extract the value from "intf" every time. if ((scopeid >= (sizeof(rr->updateIntID) * mDNSNBBY) || bit_get_opaque64(rr->updateIntID, scopeid)) && (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID)) @@ -5617,47 +5922,56 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte return mDNSfalse; } -mDNSexport void UpdateRMACCallback(mDNS *const m, void *context) +mDNSexport void UpdateRMAC(mDNS *const m, void *context) { IPAddressMACMapping *addrmap = (IPAddressMACMapping *)context ; m->CurrentRecord = m->ResourceRecords; if (!addrmap) { - LogMsg("UpdateRMACCallback: Address mapping is NULL"); + LogMsg("UpdateRMAC: Address mapping is NULL"); return; } while (m->CurrentRecord) { AuthRecord *rr = m->CurrentRecord; - // If this is a keepalive record and the remote IP address matches, update the RData - if (mDNS_KeepaliveRecord(&rr->resrec)) + // If this is a non-sleep proxy keepalive record and the remote IP address matches, update the RData + if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec)) { mDNSAddr raddr; getKeepaliveRaddr(m, rr, &raddr); if (mDNSSameAddress(&raddr, &addrmap->ipaddr)) { - UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr)); + // Update the MAC address only if it is not a zero MAC address + mDNSEthAddr macAddr; + mDNSu8 *ptr = GetValueForMACAddr((mDNSu8 *)(addrmap->ethaddr), (mDNSu8 *) (addrmap->ethaddr + sizeof(addrmap->ethaddr)), &macAddr); + if (ptr != mDNSNULL && !mDNSEthAddressIsZero(macAddr)) + { + UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr)); + } } } m->CurrentRecord = rr->next; } if (addrmap) - { mDNSPlatformMemFree(addrmap); - } + } mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr) { mDNSu16 newrdlength; - mDNSAddr laddr, raddr; - mDNSEthAddr eth; - mDNSIPPort lport, rport; - mDNSu32 timeout, seq, ack; - mDNSu16 win; + mDNSAddr laddr = zeroAddr; + mDNSAddr raddr = zeroAddr; + mDNSEthAddr eth = zeroEthAddr; + mDNSIPPort lport = zeroIPPort; + mDNSIPPort rport = zeroIPPort; + mDNSu32 timeout = 0; + mDNSu32 seq = 0; + mDNSu32 ack = 0; + mDNSu16 win = 0; UTF8str255 txt; int rdsize; RData *newrd; @@ -5667,8 +5981,7 @@ mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkIn // Note: If we fail to update the DNS NULL record with additional information in this function, it will be registered // with the SPS like any other record. SPS will not send keepalives if it does not have additional information. mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); - if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) || - mDNSIPPortIsZero(rport)) + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport)) { LogMsg("UpdateKeepaliveRData: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, &raddr, rport.NotAnInteger); return mStatus_UnknownErr; @@ -5731,8 +6044,8 @@ mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkIn // free that memory here before copying in the new data. if ( rr->resrec.rdata != &rr->rdatastorage) { - mDNSPlatformMemFree(rr->resrec.rdata); LogSPS("UpdateKeepaliveRData: Freed allocated memory for keep alive packet: %s ", ARDisplayString(m, rr)); + mDNSPlatformMemFree(rr->resrec.rdata); } SetNewRData(&rr->resrec, newrd, newrdlength); // Update our rdata @@ -5872,7 +6185,7 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo * LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); - // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss + // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSNULL, mDNSfalse); if (err) LogSPS("SendSPSRegistration: mDNSSendDNSMessage err %d", err); if (err && intf->SPSAddr[sps].type == mDNSAddrType_IPv4 && intf->NetWakeResolve[sps].ThisQInterval == -1) @@ -5945,7 +6258,7 @@ mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntI { AuthRecord *ar; LogSPS("SPSInitRecordsBeforeUpdate: UpdateIntID 0x%x 0x%x", updateIntID.l[1], updateIntID.l[0]); - + *WakeOnlyService = mDNSfalse; // Before we store the A and AAAA records that we are going to register with the sleep proxy, @@ -6062,7 +6375,7 @@ mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const Resour if (!AddRecord) return; // Don't care about REMOVE events if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs - // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address + // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address if (answer->rrtype == kDNSType_SRV) { @@ -6114,28 +6427,42 @@ mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m) return mDNSfalse; } +#define WAKE_ONLY_SERVICE 1 +#define AC_ONLY_SERVICE 2 + #ifdef APPLE_OSX_mDNSResponder -// This function is used only in the case of local NIC proxy. For external -// sleep proxy server, we do this in SPSInitRecordsBeforeUpdate when we -// walk the resource records. -mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyService) +mDNSlocal void SendGoodbyesForSelectServices(mDNS *const m, mDNSBool *servicePresent, mDNSu32 serviceType) { AuthRecord *rr; - - *WakeOnlyService = mDNSfalse; + *servicePresent = mDNSfalse; // Mark all the records we need to deregister and send them for (rr = m->ResourceRecords; rr; rr=rr->next) { - if ((rr->AuthFlags & AuthFlagsWakeOnly) && - rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + // If the service type is wake only service and the auth flags match and requires a goodbye + // OR if the service type is AC only and it is not a keepalive record, + // mark the records we need to deregister and send them + if ((serviceType == WAKE_ONLY_SERVICE && (rr->AuthFlags & AuthFlagsWakeOnly) && + rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) || + (serviceType == AC_ONLY_SERVICE && !mDNS_KeepaliveRecord(&rr->resrec))) { rr->ImmedAnswer = mDNSInterfaceMark; - *WakeOnlyService = mDNStrue; + *servicePresent = mDNStrue; } } } -#endif // APPLE_OSx_mDNSResponder +#endif + +#ifdef APPLE_OSX_mDNSResponder +// This function is used only in the case of local NIC proxy. For external +// sleep proxy server, we do this in SPSInitRecordsBeforeUpdate when we +// walk the resource records. +mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyService) +{ + return SendGoodbyesForSelectServices(m, WakeOnlyService, WAKE_ONLY_SERVICE); +} +#endif // APPLE_OSX_mDNSResponder + mDNSlocal void SendSleepGoodbyes(mDNS *const m, mDNSBool AllInterfaces, mDNSBool unicast) { @@ -6235,7 +6562,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) mDNSBool invokeKACallback = mDNStrue; const CacheRecord *sps[3] = { mDNSNULL }; mDNSOpaque64 updateIntID = zeroOpaque64; - mDNSInterfaceID registeredIntfIDS[128]; + mDNSInterfaceID registeredIntfIDS[128] = { 0 }; mDNSu32 registeredCount = 0; int skippedRegistrations = 0; @@ -6248,7 +6575,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) NetworkInterfaceInfo *intf; // Clear out the SCDynamic entry that stores the external SPS information - mDNSPlatformClearSPSMACAddr(); + mDNSPlatformClearSPSData(); for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) { @@ -6288,16 +6615,22 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) #if APPLE_OSX_mDNSResponder else if (SupportsInNICProxy(intf)) { - if (ActivateLocalProxy(m, intf) == mStatus_NoError) + mDNSBool keepaliveOnly = mDNSfalse; + if (ActivateLocalProxy(m, intf, &keepaliveOnly) == mStatus_NoError) { SendGoodbyesForWakeOnlyService(m, &WakeOnlyService); - SendGoodbyes = mDNSfalse; - invokeKACallback = mDNSfalse; + + // Send goodbyes for all advertised services if the only record offloaded was the keepalive record. + SendGoodbyes = (keepaliveOnly) ? mDNStrue: mDNSfalse; + invokeKACallback = mDNSfalse; LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); // This will leave m->SleepState set to SleepState_Transferring, // which is okay because with no outstanding resolves, or updates in flight, // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed + // Setting this flag activates the SleepLimit which delays sleep by 5 seconds and + // will allow the system to deregister any BTMM records. + m->NextScheduledSPRetry = m->timenow + (5 * mDNSPlatformOneSecond); registeredIntfIDS[registeredCount] = intf->InterfaceID; registeredCount++; } @@ -6334,9 +6667,9 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) { #if ForceAlerts if (intf->SPSAddr[i].type) - { LogMsg("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); *(long*)0 = 0; } + LogFatalError("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); if (intf->NetWakeResolve[i].ThisQInterval >= 0) - { LogMsg("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); *(long*)0 = 0; } + LogFatalError("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); #endif intf->SPSAddr[i].type = mDNSAddrType_None; if (intf->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery(m, &intf->NetWakeResolve[i]); @@ -6376,7 +6709,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) // // - If there are no sleep proxy servers, then send goodbyes on all interfaces // for both multicast and unicast. - // + // // - If we skipped registrations on some interfaces, then we have already marked // them appropriately above. We don't need to send goodbyes for unicast as // we have registered with at least one sleep proxy. @@ -6399,7 +6732,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) else if (WakeOnlyService) { // If we saw WakeOnly service above, send the goodbyes now. - LogSPS("BeginSleepProcessing: Sending goodbyes for WakeOnlyServices"); + LogSPS("BeginSleepProcessing: Sending goodbyes for WakeOnlyService"); SendResponses(m); } } @@ -6425,7 +6758,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) #ifndef SPC_DISABLED if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords); #else - (void)oldstate; + (void)oldstate; #endif mDNS_ReclaimLockAfterCallback(); } @@ -6474,10 +6807,10 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) m->SleepState = SleepState_Awake; m->SleepSeqNum++; // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) - // then we enforce a minimum delay of 16 seconds before we begin sleep processing. + // then we enforce a minimum delay of five seconds before we begin sleep processing. // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., // before we make our determination of whether there's a Sleep Proxy out there we should register with. - m->DelaySleep = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 16); + m->DelaySleep = NonZeroTime(m->timenow + kDarkWakeDelaySleep); } if (m->SPSState == 3) @@ -6486,7 +6819,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags); } m->mDNSStats.Wakes++; - + m->DelayConflictProcessing = MAX_CONFLICT_PROCESSING_DELAYS; // ... and the same for NextSPSAttempt for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1; @@ -6527,7 +6860,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) mDNSu32 uTTL = RRUnadjustedTTL(cr->resrec.rroriginalttl); const mDNSs32 remain = uTTL - (m->timenow - cr->TimeRcvd) / mDNSPlatformOneSecond; - // -if we have slept longer than the remaining TTL, purge and start fresh. + // -if we have slept longer than the remaining TTL, purge and start fresh. // -if we have been sleeping for a long time, we could reduce TimeRcvd below by // a sufficiently big value which could cause the value to go into the future // because of the signed comparison of time. For this to happen, we should have been @@ -7011,7 +7344,7 @@ mDNSlocal void DeregisterProxyRecord(mDNS *const m, AuthRecord *const rr) mDNSlocal void ClearKeepaliveProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist, const mDNSInterfaceID InterfaceID) { if (m->CurrentRecord) - LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + LogMsg("ClearKeepaliveProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); m->CurrentRecord = thelist; // Normally, the RDATA of the keepalive record will be different each time and hence we always @@ -7103,7 +7436,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, DNSMessage *const response) { - mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); + mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; @@ -7145,9 +7478,9 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } - // + // // Look in Authority Section for NSEC3 record - // + // mDNSParseNSEC3Records(m, query, end, InterfaceID, &McastNSEC3Records); @@ -7481,7 +7814,6 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { SendLegacyResponse = mDNStrue; } - if (SendMulticastResponse || SendUnicastResponse) { @@ -7588,15 +7920,17 @@ exit: // For non-truncated queries, we can definitively say that we should expect // to be seeing a response for any records still left in the ExpectedAnswers list if (!(query->h.flags.b[0] & kDNSFlag0_TC)) - if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond) + if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond * 3/4) { cr->UnansweredQueries++; cr->LastUnansweredTime = m->timenow; -#if ENABLE_MULTI_PACKET_QUERY_SNOOPING if (cr->UnansweredQueries > 1) - debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", + #if ENABLE_MULTI_PACKET_QUERY_SNOOPING + debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); -#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + #else + debugf("ProcessQuery: UnansweredQueries %lu %s", cr->UnansweredQueries, CRDisplayString(m, cr)); + #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING SetNextCacheCheckTimeForRecord(m, cr); } @@ -7604,12 +7938,16 @@ exit: // then mark it to expire in five seconds if we don't get a response by then. if (cr->UnansweredQueries >= MaxUnansweredQueries) { -#if ENABLE_MULTI_PACKET_QUERY_SNOOPING // Only show debugging message if this record was not about to expire anyway - if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) - debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + if (RRExpireTime(cr) - m->timenow > (mDNSs32) kDefaultReconfirmTimeForNoAnswer * 4 / 3 + mDNSPlatformOneSecond) + #if ENABLE_MULTI_PACKET_QUERY_SNOOPING + debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); -#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + #else + LogInfo("ProcessQuery: UnansweredQueries %lu TTL %lu mDNS_Reconfirm() for %s", + cr->UnansweredQueries, (RRExpireTime(cr) - m->timenow + mDNSPlatformOneSecond-1) / mDNSPlatformOneSecond, CRDisplayString(m, cr)); + #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + m->mDNSStats.PoofCacheDeletions++; mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); } @@ -7653,9 +7991,9 @@ exit: DNSQuestion *q = DupQuestions; DupQuestions = q->NextInDQList; q->NextInDQList = mDNSNULL; - i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); - debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID, - srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); + RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type); + debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s", q->qname.c, DNSTypeName(q->qtype), InterfaceID, + srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6"); } if (McastNSEC3Records) @@ -7673,17 +8011,18 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, { mDNSu8 *responseend = mDNSNULL; mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && - !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); + !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); - if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) + if (!dstaddr || (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr))) { + const char *const reason = !dstaddr ? "Received over TCP connection" : "Multicast, but no InterfaceID"; LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes (Multicast, but no InterfaceID)", + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes (%s)", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data, reason); return; } @@ -7773,9 +8112,9 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, } if (mDNSSameIPPort(srcp, port)) return(q); - // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); - // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking - // if (TrustedSource(m, srcaddr)) return(mDNStrue); + // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); + // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking + // if (TrustedSource(m, srcaddr)) return(mDNStrue); LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s", q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); return(mDNSNULL); @@ -7791,111 +8130,6 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, return(mDNSNULL); } -// Return a pointer to the primary service name, skipping subtype name if present. -mDNSlocal const domainname *getPrimaryServiceName(const domainname *domainName) -{ - const domainname *primaryName = domainName; - const domainname *subName = SkipLeadingLabels(domainName, 1); - - if (SameDomainLabel(subName->c, (const mDNSu8 *)mDNSSubTypeLabel)) - { - // skip "._sub" portion of name - primaryName = SkipLeadingLabels(domainName, 2); - debugf("getPrimaryServiceName: returning %##s for _sub type", primaryName); - } - - return primaryName; -} - -// This function is not called if the packet is from us, which implies that we accept all multicast packets coming from us. -mDNSlocal mDNSBool ExpectingMulticastResponseForRecord(mDNS *const m, CacheRecord *rr, const mDNSAddr *srcaddr, mDNSBool recordAccepted, - CacheRecord **McastNSEC3Records) -{ - DNSQuestion *q; - - // Accept A and AAAA if we accepted something before in the same packet as most likely related to the - // service records that we may have accepted. - if (recordAccepted && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA)) - { - LogInfo("ExpectingMulticastResponseForRecord:A:AAAA: accepting %s, from %#a due to same packet %d", CRDisplayString(m, rr), srcaddr, m->PktNum); - return mDNStrue; - } - for (q = m->Questions; q; q=q->next) - { - if (!q->DuplicateOf && mDNSOpaque16IsZero(q->TargetQID)) - { - mDNSBool ret; - // 1. If a resource record answers question, cache it. This also will cache NSECs if it asserts - // non-existence of q->qtype. If we have any matching NSEC3 Records for the question, send - // it along with the resource record. Do it only for questions that are expecting to - // discover only its peers (q->AnonInfo not NULL) - if (q->AnonInfo && McastNSEC3Records && !rr->resrec.AnonInfo) - { - InitializeAnonInfoForCR(m, McastNSEC3Records, rr); - } - ret = ResourceRecordAnswersQuestion(&rr->resrec, q); - if (ret) - { - // The record and the question belong to the same subset. Set the - // anonymous data in the cache record. - if (q->AnonInfo && rr->resrec.AnonInfo) - { - SetAnonData(q, &rr->resrec, mDNSfalse); - } - LogInfo("ExpectingMulticastResponseForRecord: Name and Type match, accepting %s, from %#a", CRDisplayString(m, rr), srcaddr); - if (rr->resrec.rrtype == kDNSType_NSEC) - LogInfo("ExpectingMulticastResponseForRecord: record %s, question %##s (%s)", CRDisplayString(m, rr), q->qname.c, DNSTypeName(q->qtype)); - return mDNStrue; - } - if (rr->resrec.rrtype == kDNSType_SRV || rr->resrec.rrtype == kDNSType_TXT) - { - // Point to the service type in the record name - const domainname *name = SkipLeadingLabels(rr->resrec.name, 1); - - // If question is for a sub type, just compare against the primary service type - const domainname *primaryName = getPrimaryServiceName(&q->qname); - - // 2. If the SRV or TXT record matches the service name, then cache it. If the TXT or SRV record is - // before the PTR record in the packet, PTR record may not be in the cache yet and hence the logic - // in (3) below will fail to cache it. - if (q->qtype == kDNSType_PTR && name && SameDomainName(primaryName, name)) - { - LogInfo("ExpectingMulticastResponseForRecord: Accepting %s due to PTR match, question %##s from %#a, pktnum %d", - CRDisplayString(m, rr), q->qname.c, srcaddr, m->PktNum); - return mDNStrue; - } - - if (name) - { - const mDNSu32 slot = HashSlot(name); - const mDNSu32 namehash = DomainNameHashValue(name); - CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); - CacheRecord *cr; - - // 3. Same as in (2), but look in the cache in case we don't have the PTR question. - - for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) - { - if (cr->resrec.rrtype == kDNSType_PTR) - { - primaryName = getPrimaryServiceName(cr->resrec.name); - - if (SameDomainName(primaryName, name)) - { - LogInfo("ExpectingMulticastResponseForRecord: accepting %s, from %#a, pktnum %d", - CRDisplayString(m, rr), srcaddr, m->PktNum); - return mDNStrue; - } - } - } - } - } - } - } - debugf("ExpectingMulticastResponseForRecord: discarding %s, from %#a, pktnum %d", CRDisplayString(m, rr), srcaddr, m->PktNum); - return(mDNSfalse); -} - // Certain data types need more space for in-memory storage than their in-packet rdlength would imply // Currently this applies only to rdata types containing more than one domainname, // or types where the domainname is not the last item in the structure. @@ -7918,7 +8152,7 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C if (!m->rec.r.resrec.InterfaceID) debugf("CreateNewCacheEntry %s", CRDisplayString(m, &m->rec.r)); //if (RDLength > InlineCacheRDSize) - // LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r)); + // LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r)); if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); // If we don't have a CacheGroup for this name, make one now if (cg) rr = GetCacheRecord(m, cg, RDLength); // Make a cache record, being careful not to recycle cg @@ -8257,7 +8491,7 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * // the application if (qptr->ProxyQuestion) qptr->responseFlags = response->h.flags; - GenerateNegativeResponse(m, QC_forceresponse); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); m->CurrentQuestion = mDNSNULL; } else @@ -8441,7 +8675,8 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * name = (const domainname *)(name->c + 1 + name->c[0]); hash = DomainNameHashValue(name); slot = HashSlot(name); - cg = CacheGroupForName(m, slot, hash, name); + // For now, we don't need to update cg here, because we'll do it again immediately, back up at the start of this loop + //cg = CacheGroupForName(m, slot, hash, name); } } } @@ -8616,7 +8851,6 @@ mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage } else { - // If the packet TTL is zero, that means we're deleting this record. // To give other hosts on the network a chance to protest, we push the deletion // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. @@ -8643,7 +8877,7 @@ mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records) { - const mDNSu8 *ptr = response->data; + const mDNSu8 *ptr; CacheRecord *rr; int i; @@ -8687,29 +8921,11 @@ mDNSlocal void mDNSCoreResetRecord(mDNS *const m) m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it if (m->rec.r.resrec.AnonInfo) { - FreeAnonInfo(m->rec.r.resrec.AnonInfo); + FreeAnonInfo(m->rec.r.resrec.AnonInfo); m->rec.r.resrec.AnonInfo = mDNSNULL; } } -#define DEVICE_INFO_RECORD_LABELS 4 - -// Determine if the record is an instance of _device-info._tcp.local. -mDNSlocal mDNSBool IsDeviceInfoRecord(const domainname *d) -{ - const domainname *afterInstance; - - if (CountLabels(d) != DEVICE_INFO_RECORD_LABELS) - return mDNSfalse; - - // skip the instance name - afterInstance = SkipLeadingLabels(d, 1); - if (SameDomainName(afterInstance, &LocalDeviceInfoName)) - return mDNStrue; - - return mDNSfalse; -} - // Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change // the record list and/or question list. // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. @@ -8722,9 +8938,8 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, const mDNSInterfaceID InterfaceID) { int i; - mDNSBool myself; mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); - mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, &myself); + mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); DNSQuestion *llqMatch = mDNSNULL; DNSQuestion *unicastQuestion = mDNSNULL; uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); @@ -8742,12 +8957,12 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, mDNSu8 rcode = '\0'; mDNSBool rrsigsCreated = mDNSfalse; mDNSBool DNSSECQuestion = mDNSfalse; - mDNSBool recordAccepted = mDNSfalse; NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID); + mDNSBool recordAcceptedInResponse = mDNSfalse; // Set if a record is accepted from a unicast mDNS response that answers an existing question. // All records in a DNS response packet are treated as equally valid statements of truth. If we want // to guard against spoof responses, then the only credible protection against that is cryptographic - // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record + // security, e.g. DNSSEC., not worrying about which section in the spoof packet contained the record. int firstauthority = response->h.numAnswers; int firstadditional = firstauthority + response->h.numAuthorities; int totalrecords = firstadditional + response->h.numAdditionals; @@ -8854,14 +9069,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // and hence retransmit without the EDNS0/DOK option. if (DNSSECOptionalQuestion(qptr) && qptr->qDNSServer && !qptr->qDNSServer->DNSSECAware) { - LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to DNSSEC Query %##s (%s), clear DO flag", + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to DNSSEC Query %##s (%s), clear DO flag", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); - qptr->qDNSServer->req_DO = mDNSfalse; + qptr->qDNSServer->req_DO = mDNSfalse; } // For Unicast DNS Queries, penalize the DNSServer else { - LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); PenalizeDNSServer(m, qptr, response->h.flags); } @@ -8896,7 +9111,9 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { // All responses sent via LL multicast are acceptable for caching // All responses received over our outbound TCP connections are acceptable for caching - mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType; + // We accept all records in a unicast response to a multicast query once we find one that + // answers an active question. + mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType || recordAcceptedInResponse; // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer // to any specific question -- any code reading records from the cache needs to make that determination for itself.) @@ -8983,64 +9200,73 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that // we create. - if (!mDNSOpaque16IsZero(response->h.id)) - { - DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); - // Initialize the DNS server on the resource record which will now filter what questions we answer with - // this record. - // - // We could potentially lookup the DNS server based on the source address, but that may not work always - // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came - // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based - // on the "id" and "source port", then this response answers the question and assume the response - // came from the same DNS server that we sent the query to. + // Initialize the DNS server on the resource record which will now filter what questions we answer with + // this record. + // + // We could potentially lookup the DNS server based on the source address, but that may not work always + // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came + // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based + // on the "id" and "source port", then this response answers the question and assume the response + // came from the same DNS server that we sent the query to. - if (q != mDNSNULL) + if (q != mDNSNULL) + { + AcceptableResponse = mDNStrue; + if (!InterfaceID) { - AcceptableResponse = mDNStrue; - if (!InterfaceID) - { - debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); - m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; - } - else - LogInfo("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; } else { - // If we can't find a matching question, we need to see whether we have seen records earlier that matched - // the question. The code below does that. So, make this record unacceptable for now - if (!InterfaceID) - { - debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); - AcceptableResponse = mDNSfalse; - } + // Accept all remaining records in this unicast response to an mDNS query. + recordAcceptedInResponse = mDNStrue; + LogInfo("mDNSCoreReceiveResponse: Accepting response for query: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); } } - else if (ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) - { - recordAccepted = mDNStrue; - AcceptableResponse = mDNStrue; - LogInfo("mDNSCoreReceiveResponse: Accepting record in response to QU question %s, InterfaceID %p", CRDisplayString(m, &m->rec.r), - InterfaceID); - } - else if (IsDeviceInfoRecord(m->rec.r.resrec.name)) + else { - recordAccepted = mDNStrue; - AcceptableResponse = mDNStrue; - LogInfo("mDNSCoreReceiveResponse: Accepting _device-info record %s, InterfaceID %p", - CRDisplayString(m, &m->rec.r), InterfaceID); + // If we can't find a matching question, we need to see whether we have seen records earlier that matched + // the question. The code below does that. So, make this record unacceptable for now + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } } } } else if (llintf && llintf->IgnoreIPv4LL && m->rec.r.resrec.rrtype == kDNSType_A) { - CacheRecord *const rr = &m->rec.r; - RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; - - // If we are supposed to ignore link-local addresses on this interface, drop - // all "A" records that have link-local address in them. + // There are some routers (rare, thankfully) that generate bogus ARP responses for + // any IPv4 address they don’t recognize, including RFC 3927 IPv4 link-local addresses. + // To work with these broken routers, client devices need to blacklist these broken + // routers and ignore their bogus ARP responses. Some devices implement a technique + // such as the one described in US Patent 7436783, which lets clients detect and + // ignore these broken routers: + + // OS X and iOS do not implement this defensive mechanism, instead taking a simpler + // approach of just detecting these broken routers and completely disabling IPv4 + // link-local communication on interfaces where a broken router is detected. + // OS X and iOS set the IFEF_ARPLL interface flag on interfaces + // that are deemed “safe” for IPv4 link-local communication; + // the flag is cleared on interfaces where a broken router is detected. + + // OS X and iOS will not even try to communicate with an IPv4 + // link-local destination on an interface without the IFEF_ARPLL flag set. + // This can cause some badly written applications to freeze for a long time if they + // attempt to connect to an IPv4 link-local destination address and then wait for + // that connection attempt to time out before trying other candidate addresses. + + // To mask this client bug, we suppress acceptance of IPv4 link-local address + // records on interfaces where we know the OS will be unwilling even to attempt + // communication with those IPv4 link-local destination addresses. + // kSuppress IPv4LL answers on interfaces without IFEF_ARPLL + + const CacheRecord *const rr = &m->rec.r; + const RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; if (mDNSv4AddressIsLinkLocal(&rdb->ipv4)) { LogInfo("mDNSResponder: Dropping LinkLocal packet %s", CRDisplayString(m, &m->rec.r)); @@ -9099,7 +9325,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, if (rr->ProbeCount > DefaultProbeCountForTypeUnique) LogInfo("mDNSCoreReceiveResponse: Already reset to Probing: %s", ARDisplayString(m, rr)); else if (rr->ProbeCount == DefaultProbeCountForTypeUnique) - LogMsg("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr)); + LogInfo("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr)); else { LogMsg("mDNSCoreReceiveResponse: Received from %#a:%d %s", srcaddr, mDNSVal16(srcport), CRDisplayString(m, &m->rec.r)); @@ -9120,13 +9346,36 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // If we're probing for this record, we just failed else if (rr->resrec.RecordType == kDNSRecordTypeUnique) { + // At this point in the code, we're probing for uniqueness. + // We've sent at least one probe (rr->ProbeCount < DefaultProbeCountForTypeUnique) + // but we haven't completed probing yet (rr->resrec.RecordType == kDNSRecordTypeUnique). // Before we call deregister, check if this is a packet we registered with the sleep proxy. if (!mDNSCoreRegisteredProxyRecord(m, rr)) { - LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); - - m->mDNSStats.NameConflicts++; - mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + // This may be a conflict due to stale packets on the network. Delay probing by a second. + // If there are conflicts after 3 such attempts, then it is a true conflict. + if (m->DelayConflictProcessing) + { + m->DelayConflictProcessing--; + LogMsg("Possible spurious conflict for %s. Attempt %d at suppressing probes for one second", + ARDisplayString(m, rr), (MAX_CONFLICT_PROCESSING_DELAYS - m->DelayConflictProcessing)); + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->AnnounceCount = InitialAnnounceCount; + m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + InitializeLastAPTime(m, rr); + RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate + } + else + { + LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); + m->mDNSStats.NameConflicts++; +#if APPLE_OSX_mDNSResponder + // See if this record was also registered with any D2D plugins. + D2D_stop_advertising_record(rr); +#endif + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + } } // We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the @@ -9138,6 +9387,9 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); m->mDNSStats.KnownUniqueNameConflicts++; +#if APPLE_OSX_mDNSResponder + D2D_stop_advertising_record(rr); +#endif mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); } else @@ -9185,27 +9437,6 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, unicastQuestion, &cfp, &NSECCachePtr, InterfaceID); } - // If mDNSOppCaching is set (which affects only multicast), enable opportunistic caching in which case we cache - // everything that was received over multicast. Otherwise, we are selective about the caching. - // - // Cache everything that is from ourselves (that's how we answer any questions looking for them). Otherwise call - // ExpectingMulticastResponseForRecord which decides whether to cache this record or not. - // - if (!m->mDNSOppCaching && !rr && !myself && mDNSOpaque16IsZero(response->h.id)) - { - if (!ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) - { - //LogMsg("mDNSCoreReceiveResponse: discarding %s", CRDisplayString(m, &m->rec.r)); - mDNSCoreResetRecord(m); - continue; - } - else - { - recordAccepted = mDNStrue; - } - } - - // If packet resource record not in our cache, add it now // (unless it is just a deletion of a record we never had, in which case we don't care) if (!rr && m->rec.r.resrec.rroriginalttl > 0) @@ -9327,7 +9558,7 @@ exit: continue; } } - + // For Unicast (null InterfaceID) the resolver IDs should also match if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) && (r1->resrec.InterfaceID || (id1 == id2)) && @@ -9343,15 +9574,22 @@ exit: // goodbye announcement with the cache flush bit set (or a case-change on record rdata, // which we treat as a goodbye followed by an addition) and in that case it would be // inappropriate to synchronize all the other records to a TTL of 0 (or 1). + // We suppress the message for the specific case of correcting from 240 to 60 for type TXT, // because certain early Bonjour devices are known to have this specific mismatch, and // there's no point filling syslog with messages about something we already know about. // We also don't log this for uDNS responses, since a caching name server is obliged // to give us an aged TTL to correct for how long it has held the record, // so our received TTLs are expected to vary in that case + + // We also suppress log message in the case of SRV records that are received + // with a TTL of 4500 that are already cached with a TTL of 120 seconds, since + // this behavior was observed for a number of discoveryd based AppleTV's in iOS 8 + // GM builds. if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1) { if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) && + !(r2->resrec.rroriginalttl == 120 && r1->resrec.rroriginalttl == 4500 && r2->resrec.rrtype == kDNSType_SRV) && mDNSOpaque16IsZero(response->h.id)) LogInfo("Correcting TTL from %4d to %4d for %s", r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2)); @@ -9419,7 +9657,7 @@ exit: // Note: We need to do this before we call CacheRecordDeferredAdd as this // might start the verification process which needs these NSEC records if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) - { + { LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); FreeNSECRecords(m, NSECRecords); } @@ -9439,7 +9677,7 @@ exit: { LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr)); if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) - { + { LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); FreeNSECRecords(m, NSECRecords); } @@ -9519,8 +9757,8 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); if (ar->WakeUp.HMAC.l[0]) { - SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); // Send one wakeup magic packet - ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken + SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password, mDNSfalse); // Send one wakeup magic packet + ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken } mDNS_Unlock(m); } @@ -9550,6 +9788,11 @@ mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *et } else if (*ptr == ':') { + if (colons >=5) + { + LogMsg("GetValueForMACAddr: Address malformed colons %d val %d", colons, val); + return mDNSNULL; + } eth->b[colons] = val; colons++; val = 0; @@ -9681,6 +9924,11 @@ mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4 val = val * 10 + *ptr - '0'; else if (*ptr == '.') { + if (val > 255 || dots >= 3) + { + LogMsg("GetValueForIPv4Addr: something wrong ptr(%p) %c, limit %p, dots %d", ptr, *ptr, limit, dots); + return mDNSNULL; + } v4->b[dots++] = val; val = 0; } @@ -9723,6 +9971,38 @@ mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *valu return ptr; } +mDNSexport mDNSBool mDNSValidKeepAliveRecord(AuthRecord *rr) +{ + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + + if (!mDNS_KeepaliveRecord(&rr->resrec)) + { + return mDNSfalse; + } + + timeout = seq = ack = 0; + win = 0; + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + eth = zeroEthAddr; + + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + + if (mDNSAddressIsZero(&laddr) || mDNSIPPortIsZero(lport) || + mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(rport) || + mDNSEthAddressIsZero(eth)) + { + return mDNSfalse; + } + + return mDNStrue; +} + + mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win) { @@ -9750,7 +10030,7 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA raddr->type = mDNSAddrType_IPv4; ptr = GetValueForIPv4Addr(ptr, limit, &raddr->ip.v4); } - if (param == 'H') + else if (param == 'H') { laddr->type = mDNSAddrType_IPv6; ptr = GetValueForIPv6Addr(ptr, limit, &laddr->ip.v6); @@ -9899,16 +10179,18 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m) mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar) { - if (ar != mDNSNULL) - { - LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL"); - return; - } - mDNSu32 timeout, seq, ack; + mDNSu32 timeout, seq, ack, seqInc; mDNSu16 win; mDNSAddr laddr, raddr; mDNSEthAddr eth; mDNSIPPort lport, rport; + mDNSu8 *ptr; + + if (ar == mDNSNULL) + { + LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL"); + return; + } timeout = seq = ack = 0; win = 0; @@ -9923,6 +10205,17 @@ mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar) LogInfo("mDNS_SendKeepaliveACK: not a valid record %s for keepalive", ARDisplayString(m, ar)); return; } + + // To send a keepalive ACK, we need to add one to the sequence number from the keepalive + // record, which is the TCP connection's "next" sequence number minus one. Otherwise, the + // keepalive ACK also ends up being a keepalive probe. Also, seq is in network byte order, so + // it's converted to host byte order before incrementing it by one. + ptr = (mDNSu8 *)&seq; + seqInc = (mDNSu32)((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]) + 1; + ptr[0] = (mDNSu8)((seqInc >> 24) & 0xFF); + ptr[1] = (mDNSu8)((seqInc >> 16) & 0xFF); + ptr[2] = (mDNSu8)((seqInc >> 8) & 0xFF); + ptr[3] = (mDNSu8)((seqInc ) & 0xFF); LogMsg("mDNS_SendKeepaliveACK: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport)); mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win); } @@ -10083,12 +10376,43 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, mDNS_SendKeepalives(m); } +mDNSlocal mDNSu32 mDNSGenerateOwnerOptForInterface(mDNS *const m, const mDNSInterfaceID InterfaceID, DNSMessage *msg) +{ + mDNSu8 *ptr = msg->data; + mDNSu8 *end = mDNSNULL; + mDNSu32 length = 0; + AuthRecord opt; + + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); + opt.resrec.rdestimate = sizeof(rdataOPT); + + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + + LogSPS("Generated OPT record : %s", ARDisplayString(m, &opt)); + end = PutResourceRecord(msg, ptr, &msg->h.numAdditionals, &opt.resrec); + if (end != mDNSNULL) + { + // Put all the integer values in IETF byte-order (MSB first, LSB second) + SwapDNSHeaderBytes(msg); + length = (end - msg->data); + } + else + LogSPS("mDNSGenerateOwnerOptForInterface: Failed to generate owner OPT record"); + + return length; +} + mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID) { if (InterfaceID) { mDNSu32 updatelease = 60 * 60; // If SPS fails to indicate lease time, assume one hour const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space); + mDNSAddr spsaddr; + char *ifname; if (ptr) { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec); @@ -10138,10 +10462,21 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg } // Update the dynamic store with the IP Address and MAC address of the sleep proxy - char *ifname = InterfaceNameForID(m, InterfaceID); - mDNSAddr spsaddr; + ifname = InterfaceNameForID(m, InterfaceID); mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr)); mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname); + + // Store the Owner OPT record for this interface. + // Configd may use the OPT record if it detects a conflict with the BSP when the system wakes up + DNSMessage optMsg; + int length = 0; + InitializeDNSMessage(&optMsg.h, zeroID, ResponseFlags); + length = mDNSGenerateOwnerOptForInterface(m, InterfaceID, &optMsg); + if (length != 0) + { + length += sizeof(DNSMessageHeader); + mDNSPlatformStoreOwnerOptRecord(ifname, &optMsg, length); + } } // If we were waiting to go to sleep, then this SPS registration or wide-area record deletion // may have been the thing we were waiting for, so schedule another check to see if we can sleep now. @@ -10152,12 +10487,7 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver) { if (cr == &m->rec.r && m->rec.r.resrec.RecordType) - { - LogMsg("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); -#if ForceAlerts - *(long*)0 = 0; -#endif - } + LogFatalError("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r)); // Create empty resource record cr->resrec.RecordType = kDNSRecordTypePacketNegative; @@ -10197,12 +10527,12 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, cr->responseFlags = ResponseFlags; } -mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, +mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { mDNSInterfaceID ifid = InterfaceID; - DNSMessage *msg = (DNSMessage *)pkt; + const mDNSu8 *const pkt = (mDNSu8 *)msg; const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery; const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery; const mDNSu8 UpdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_Update; @@ -10219,7 +10549,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co if (mDNSSameIPPort(srcport, SSDPPort) || (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port))) { mDNS_Lock(m); - LNT_ConfigureRouterInfo(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + LNT_ConfigureRouterInfo(m, InterfaceID, (mDNSu8 *)msg, (mDNSu16)(end - pkt)); mDNS_Unlock(m); return; } @@ -10227,7 +10557,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co if (mDNSSameIPPort(srcport, NATPMPPort)) { mDNS_Lock(m); - uDNS_ReceiveNATPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + uDNS_ReceiveNATPacket(m, InterfaceID, (mDNSu8 *)msg, (mDNSu16)(end - pkt)); mDNS_Unlock(m); return; } @@ -10237,9 +10567,9 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co #endif #endif - if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + if ((unsigned)(end - pkt) < sizeof(DNSMessageHeader)) { - LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - pkt)); return; } QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); @@ -10265,7 +10595,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co // Track the number of multicast packets received from a source outside our subnet. // Check the destination address to avoid accounting for spurious packets that // comes in with message id zero. - if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL) && + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr) && mDNSAddressIsAllDNSLinkGroup(dstaddr)) { m->RemoteSubnet++; @@ -10290,17 +10620,21 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, srcaddr, InterfaceID); else { - LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)", - msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); if (mDNS_LoggingEnabled) { - int i = 0; - while (ih.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), (int)(end - pkt), InterfaceID); + while (i < (int)(end - pkt)) + { + char buffer[128]; + char *p = buffer + mDNS_snprintf(buffer, sizeof(buffer), "%04X", i); + do if (i < (int)(end - pkt)) p += mDNS_snprintf(p, sizeof(buffer), " %02X", pkt[i]);while (++i & 15); + LogInfo("%s", buffer); + } } } } @@ -10416,15 +10750,11 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi q->triedAllServersOnce = question->triedAllServersOnce; q->TargetQID = question->TargetQID; - if (q->LocalSocket) - { - mDNSPlatformUDPClose(q->LocalSocket); - } - q->LocalSocket = question->LocalSocket; + // No need to close old q->LocalSocket first -- duplicate questions can't have their own sockets q->state = question->state; - // q->tcp = question->tcp; + // q->tcp = question->tcp; q->ReqLease = question->ReqLease; q->expire = question->expire; q->ntries = question->ntries; @@ -10432,7 +10762,7 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi question->LocalSocket = mDNSNULL; question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question - // question->tcp = mDNSNULL; + // question->tcp = mDNSNULL; if (q->LocalSocket) debugf("UpdateQuestionDuplicates transferred LocalSocket pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -10472,8 +10802,8 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname { if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d)) { - if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); - (*p)->flags &= ~DNSServer_FlagDelete; + if (!((*p)->flags & McastResolver_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface); + (*p)->flags &= ~McastResolver_FlagDelete; tmp = *p; *p = tmp->next; tmp->next = mDNSNULL; @@ -10491,7 +10821,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname else { (*p)->interface = interface; - (*p)->flags = DNSServer_FlagNew; + (*p)->flags = McastResolver_FlagNew; (*p)->timeout = timeout; AssignDomainName(&(*p)->domain, d); (*p)->next = mDNSNULL; @@ -10638,7 +10968,7 @@ mDNSlocal mDNSBool DNSServerMatch(DNSServer *d, mDNSInterfaceID InterfaceID, mDN // // 3) Scoped questions (non-zero ServiceID) should consider *only* scoped DNSServers (DNSServer // with "scoped" set to kScopeServiceID) and their ServiceIDs should match. - // + // // The first condition in the "if" statement checks to see if both the question and the DNSServer are // unscoped. The question is unscoped only if InterfaceID is zero and ServiceID is -1. // @@ -10697,14 +11027,16 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) // // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout - if (curr->scoped && curr->interface == mDNSInterface_Any) + // Skip DNSServers that are InterfaceID Scoped but have no valid interfaceid set OR DNSServers that are ServiceID Scoped but have no valid serviceid set + if ((curr->scoped == kScopeInterfaceID && curr->interface == mDNSInterface_Any) || (curr->scoped == kScopeServiceID && curr->serviceID <= 0)) { - debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); + LogInfo("SetValidDNSServers: ScopeType[%d] Skipping DNS server %#a (Domain %##s) Interface:[%p] Serviceid:[%d]", curr->scoped, &curr->addr, curr->domain.c, curr->interface, curr->serviceID); continue; } currcount = CountLabels(&curr->domain); - if ((!DEQuery || !curr->cellIntf) && DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) + if ((!curr->cellIntf || (!DEQuery && !(question->flags & kDNSServiceFlagsDenyCellular))) && + DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) { bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); @@ -10943,7 +11275,7 @@ mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNS LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, as the DNS server is NULL", q->qname.c, DNSTypeName(q->qtype)); return mDNStrue; } - + // Check if the DNS Configuration allows A/AAAA queries to be sent if ((q->qtype == kDNSType_A) && (d->req_A)) { @@ -11017,6 +11349,12 @@ mDNSlocal mDNSBool ShouldSuppressDotLocalQuery(mDNS *const m, DNSQuestion *q) mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, DNSQuestion *q) { + if (q->InterfaceID == mDNSInterface_LocalOnly) + { + LogInfo("ShouldSuppressQuery: LocalOnly query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); @@ -11290,7 +11628,7 @@ mDNSlocal void RestartUnicastQuestions(mDNS *const m) { if (mDNSOpaque16IsZero(q->TargetQID)) LogMsg("RestartUnicastQuestions: ERROR!! Restart set for multicast question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - + q->Restart = 0; SuppressStatusChanged(m, q, &restart); } @@ -11319,7 +11657,7 @@ mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) } // If no question->Target specified, clear TargetPort - if (!question->Target.type) + if (!question->Target.type) question->TargetPort = zeroIPPort; if (!ValidateDomainName(&question->qname)) @@ -11329,14 +11667,14 @@ mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) } // If this question is referencing a specific interface, verify it exists - if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P) + if (question->InterfaceID && !LocalOnlyOrP2PInterface(question->InterfaceID) && question->InterfaceID != mDNSInterface_Unicast) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); if (!intf) LogInfo("ValidateParameters: Note: InterfaceID %d for question %##s (%s) not currently found in active interface list", (uint32_t)question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); } - + return(mStatus_NoError); } @@ -11347,16 +11685,19 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) // First reset all DNS Configuration question->qDNSServer = mDNSNULL; question->validDNSServers = zeroOpaque64; - question->triedAllServersOnce = 0; - question->noServerResponse = 0; + question->triedAllServersOnce = 0; + question->noServerResponse = 0; question->StopTime = 0; +#if TARGET_OS_EMBEDDED + mDNSPlatformMemZero(&question->metrics, sizeof(question->metrics)); +#endif // Need not initialize the DNS Configuration for Local Only OR P2P Questions - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + if (LocalOnlyOrP2PInterface(question->InterfaceID)) return; // Proceed to initialize DNS Configuration (some are set in SetValidDNSServers()) if (!mDNSOpaque16IsZero(question->TargetQID)) - { + { mDNSu32 timeout = SetValidDNSServers(m, question); // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have // a networking change/search domain change that calls this function again we keep @@ -11379,7 +11720,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); } else - { + { if (question->TimeoutQuestion) question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); } @@ -11388,7 +11729,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) SetNextQueryStopTime(m, question); // SetNextQueryTime() need not be initialized for LocalOnly OR P2P Questions since those questions // will never be transmitted on the wire. Hence we call SetNextQueryTime() here. - SetNextQueryTime(m,question); + SetNextQueryTime(m,question); } // InitCommonState() is called by mDNS_StartQuery_internal() to initialize the common(uDNS/mDNS) internal @@ -11397,6 +11738,7 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) { mDNSBool purge; int i; + mDNSBool isBlocked = mDNSfalse; // Note: In the case where we already have the answer to this question in our cache, that may be all the client // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would @@ -11444,15 +11786,21 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) question->LOAddressAnswers = 0; question->FlappingInterface1 = mDNSNULL; question->FlappingInterface2 = mDNSNULL; - - // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetServiceID() - // since we would already have the question->ServiceID in that case. - if (!(question->flags & kDNSServiceFlagsServiceIndex)) - question->ServiceID = mDNSPlatformGetServiceID(m, question); - else - LogInfo("InitCommonState: Query for %##s (%s), PID[%d], ServiceID %d is already set by client", question->qname.c, - DNSTypeName(question->qtype), question->pid, question->ServiceID); - + + // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetDNSRoutePolicy() + // since we would already have the question->ServiceID in that case. + if (!(question->flags & kDNSServiceFlagsServiceIndex)) + { +#if APPLE_OSX_mDNSResponder + mDNSPlatformGetDNSRoutePolicy(m, question, &isBlocked); +#else + question->ServiceID = -1; +#endif + } + else + LogInfo("InitCommonState: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] is already set by client", question->qname.c, + DNSTypeName(question->qtype), question->pid, question->euid, question->ServiceID); + InitDNSConfig(m, question); question->AuthInfo = GetAuthInfoForQuestion(m, question); @@ -11462,55 +11810,35 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) // If ServiceID is 0 or the policy disallows making DNS requests, // set DisallowPID - question->DisallowPID = (question->ServiceID == 0 || (mDNSPlatformAllowPID(m, question) == 0)); + question->DisallowPID = (question->ServiceID == 0 || isBlocked); if (question->DisallowPID) LogInfo("InitCommonState: Query suppressed for %##s (%s), PID %d/ServiceID %d not allowed", question->qname.c, - DNSTypeName(question->qtype), question->pid, question->ServiceID); + DNSTypeName(question->qtype), question->pid, question->ServiceID); question->NextInDQList = mDNSNULL; question->SendQNow = mDNSNULL; question->SendOnAll = mDNSfalse; - -#if mDNS_REQUEST_UNICAST_RESPONSE - question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; -#else // mDNS_REQUEST_UNICAST_RESPONSE - question->RequestUnicast = SET_QU_IN_FIRST_QUERY; -#endif // mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = kDefaultRequestUnicastCount; #if APPLE_OSX_mDNSResponder - // Request unicast response for first 4 queries to increase - // reliability in an environment with high multicast packet loss. - // Must set to one more than the number of unicast queries you want, since SendQueries() - // decrements it before calling BuildQuestion() which acts on it. - if (question->flags & kDNSServiceFlagsUnicastResponse) + // Set the QU bit in the first query for the following options. + if ((question->flags & kDNSServiceFlagsUnicastResponse) || (question->flags & kDNSServiceFlagsThresholdFinder)) { - question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; + question->RequestUnicast = SET_QU_IN_FIRST_QUERY; LogInfo("InitCommonState: setting RequestUnicast = %d for %##s (%s)", question->RequestUnicast, question->qname.c, DNSTypeName(question->qtype)); - } - else if (question->flags & kDNSServiceFlagsThresholdFinder) - { - // always send one request with QU bit set when kDNSServiceFlagsThresholdFinder is set -#if mDNS_REQUEST_UNICAST_RESPONSE - question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; -#else // mDNS_REQUEST_UNICAST_RESPONSE - question->RequestUnicast = SET_QU_IN_FIRST_QUERY; -#endif // mDNS_REQUEST_UNICAST_RESPONSE - - LogInfo("InitCommonState: kDNSServiceFlagsThresholdFinder set, setting RequestUnicast = %d for %##s (%s)", - question->RequestUnicast, question->qname.c, DNSTypeName(question->qtype)); } #endif // APPLE_OSX_mDNSResponder question->LastQTxTime = m->timenow; - question->CNAMEReferrals = 0; + question->CNAMEReferrals = 0; question->WakeOnResolveCount = 0; if (question->WakeOnResolve) - { + { question->WakeOnResolveCount = InitialWakeOnResolveCount; purge = mDNStrue; - } + } for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; @@ -11537,7 +11865,7 @@ mDNSlocal void InitWABState(DNSQuestion *const question) // We won't need one for duplicate questions, or from questions answered immediately out of the cache. // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single // NAT mapping for receiving inbound add/remove events. - question->LocalSocket = mDNSNULL; + question->LocalSocket = mDNSNULL; question->unansweredQueries = 0; question->nta = mDNSNULL; question->servAddr = zeroAddr; @@ -11569,6 +11897,11 @@ mDNSlocal void InitLLQState(DNSQuestion *const question) question->id = zeroOpaque64; } +mDNSlocal void InitDNSPNState(DNSQuestion *const question) +{ + question->dnsPushState = DNSPUSH_INIT; +} + // InitDNSSECProxyState() is called by mDNS_StartQuery_internal() to initialize // DNSSEC & DNS Proxy fields of the DNS Question. mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question) @@ -11584,7 +11917,7 @@ mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question) { if (question->qDNSServer->cellIntf) { - LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype)); + debugf("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype)); question->ValidationRequired = mDNSfalse; } if (DNSSECOptionalQuestion(question) && !(question->qDNSServer->req_DO)) @@ -11656,19 +11989,30 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu vStatus = ValidateParameters(m, question); if (vStatus) return(vStatus); - + +#ifdef USE_LIBIDN + // If the TLD includes high-ascii bytes, assume it will need to be converted to Punycode. + // (In the future the root name servers may answer UTF-8 queries directly, but for now they do not.) + if (IsHighASCIILabel(LastLabel(&question->qname))) + { + domainname newname; + if (PerformNextPunycodeConversion(question, &newname)) + AssignDomainName(&question->qname, &newname); + } +#endif // USE_LIBIDN + question->TargetQID = #ifndef UNICAST_DISABLED (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : #endif // UNICAST_DISABLED zeroID; - debugf("mDNS_StartQuery_internal: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - + debugf("mDNS_StartQuery_internal: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + // Note: It important that new questions are appended at the *end* of the list, not prepended at the start q = &m->Questions; - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + if (LocalOnlyOrP2PInterface(question->InterfaceID)) q = &m->LocalOnlyQuestions; - while (*q && *q != question) + while (*q && *q != question) q=&(*q)->next; if (*q) @@ -11678,7 +12022,6 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu return(mStatus_AlreadyRegistered); } *q = question; - // Intialize the question. The only ordering constraint we have today is that // InitDNSSECProxyState should be called after the DNS server is selected (in @@ -11688,23 +12031,24 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu purge = InitCommonState(m, question); InitWABState(question); InitLLQState(question); + InitDNSPNState(question); InitDNSSECProxyState(m, question); // FindDuplicateQuestion should be called last after all the intialization // as the duplicate logic could be potentially based on any field in the // question. question->DuplicateOf = FindDuplicateQuestion(m, question); - if (question->DuplicateOf) - question->AuthInfo = question->DuplicateOf->AuthInfo; + if (question->DuplicateOf) + question->AuthInfo = question->DuplicateOf->AuthInfo; - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + if (LocalOnlyOrP2PInterface(question->InterfaceID)) { - if (!m->NewLocalOnlyQuestions) + if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; } else { - if (!m->NewQuestions) + if (!m->NewQuestions) m->NewQuestions = question; // If the question's id is non-zero, then it's Wide Area @@ -11719,6 +12063,22 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu } else { +#if BONJOUR_ON_DEMAND + m->NumAllInterfaceQuestions++; + LogInfo("mDNS_StartQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + { + m->NextBonjourDisableTime = 0; + if (m->BonjourEnabled == 0) + { + // Enable Bonjour immediately by scheduling network changed processing where + // we will join the multicast group on each active interface. + m->BonjourEnabled = 1; + m->NetworkChanged = m->timenow; + } + } +#endif // BONJOUR_ON_DEMAND if (purge) { LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c); @@ -11755,7 +12115,8 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que //LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) qp = &m->LocalOnlyQuestions; + if (LocalOnlyOrP2PInterface(question->InterfaceID)) + qp = &m->LocalOnlyQuestions; while (*qp && *qp != question) qp=&(*qp)->next; if (*qp) *qp = (*qp)->next; else @@ -11763,14 +12124,42 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que #if !ForceAlerts if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active #endif - LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", - question->qname.c, DNSTypeName(question->qtype)); -#if ForceAlerts - *(long*)0 = 0; -#endif + LogFatalError("mDNS_StopQuery_internal: Question %##s (%s) not found in active list", question->qname.c, DNSTypeName(question->qtype)); return(mStatus_BadReferenceErr); } +#if BONJOUR_ON_DEMAND + if (!LocalOnlyOrP2PInterface(question->InterfaceID) && mDNSOpaque16IsZero(question->TargetQID)) + { + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + m->NextBonjourDisableTime = NonZeroTime(m->timenow + (BONJOUR_DISABLE_DELAY * mDNSPlatformOneSecond)); + m->NumAllInterfaceQuestions--; + LogInfo("mDNS_StopQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + } +#endif // BONJOUR_ON_DEMAND + +#if TARGET_OS_EMBEDDED + if (Question_uDNS(question) && !question->metrics.answered && (question->metrics.querySendCount > 0)) + { + const domainname * queryName; + mDNSBool isForCell; + mDNSu32 durationMs; + + queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname; + isForCell = (question->qDNSServer && question->qDNSServer->cellIntf); + + if (question->metrics.querySendCount > 0) + { + durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; + } + else + { + durationMs = 0; + } + MetricsUpdateUDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, durationMs, isForCell); + } +#endif // Take care to cut question from list *before* calling UpdateQuestionDuplicates UpdateQuestionDuplicates(m, question); // But don't trash ThisQInterval until afterwards. @@ -11783,16 +12172,31 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que if (rr->CRActiveQuestion == question) { DNSQuestion *q; - // Checking for ActiveQuestion filters questions that are suppressed also - // as suppressed questions are not active - for (q = m->Questions; q; q=q->next) // Scan our list of questions - if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) - break; - if (q) + DNSQuestion *replacement = mDNSNULL; + // If we find an active question that is answered by this cached record, use it as the cache record's + // CRActiveQuestion replacement. If there are no such questions, but there's at least one unsuppressed inactive + // question that is answered by this cache record, then use an inactive one to not forgo generating RMV events + // via CacheRecordRmv() when the cache record expires. + for (q = m->Questions; q && (q != m->NewQuestions); q = q->next) + { + if (!q->DuplicateOf && !QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + if (q->ThisQInterval > 0) + { + replacement = q; + break; + } + else if (!replacement) + { + replacement = q; + } + } + } + if (replacement) debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question " - "CurrentAnswers %d, SuppressQuery %d", q, CRDisplayString(m,rr), question->CurrentAnswers, q->CurrentAnswers, q->SuppressQuery); - rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null - if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count + "CurrentAnswers %d, SuppressQuery %d", replacement, CRDisplayString(m,rr), question->CurrentAnswers, replacement->CurrentAnswers, replacement->SuppressQuery); + rr->CRActiveQuestion = replacement; // Question used to be active; new value may or may not be null + if (!replacement) m->rrcache_active--; // If no longer active, decrement rrcache_active count } } @@ -11875,6 +12279,15 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que question->tcp = mDNSNULL; } } + else if (question->dnsPushState == DNSPUSH_ESTABLISHED) + { + if (question->tcp) + { + UnSubscribeToDNSPushNotificationServer(m, q); + question->tcp->question = mDNSNULL; + question->tcp = mDNSNULL; + } + } #if APPLE_OSX_mDNSResponder UpdateAutoTunnelDomainStatuses(m); #endif @@ -11893,6 +12306,13 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que FreeAnonInfo(question->AnonInfo); question->AnonInfo = mDNSNULL; } +#if TARGET_OS_EMBEDDED + if (question->metrics.originalQName) + { + mDNSPlatformMemFree(question->metrics.originalQName); + question->metrics.originalQName = mDNSNULL; + } +#endif return(mStatus_NoError); } @@ -11984,10 +12404,8 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu question->LongLived = mDNStrue; question->ExpectUnique = mDNSfalse; question->ForceMCast = ForceMCast; - question->ReturnIntermed = mDNSfalse; + question->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; question->SuppressUnusable = mDNSfalse; - question->DenyOnCellInterface = mDNSfalse; - question->DenyOnExpInterface = mDNSfalse; question->SearchListIndex = 0; question->AppendSearchDomains = 0; question->RetryWithSearchDomains = mDNSfalse; @@ -12028,295 +12446,6 @@ mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, return(status); } -mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) -{ - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); - return(mDNSfalse); -} - -mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) -{ - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - mDNSBool PortChanged = !mDNSSameIPPort(query->info->port, answer->rdata->u.srv.port); - if (!AddRecord) return; - if (answer->rrtype != kDNSType_SRV) return; - - query->info->port = answer->rdata->u.srv.port; - - // If this is our first answer, then set the GotSRV flag and start the address query - if (!query->GotSRV) - { - query->GotSRV = mDNStrue; - query->qAv4.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); - query->qAv6.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); - mDNS_StartQuery(m, &query->qAv4); - // Only do the AAAA query if this machine actually has IPv6 active - if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); - } - // If this is not our first answer, only re-issue the address query if the target host name has changed - else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || - !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) - { - mDNS_StopQuery(m, &query->qAv4); - if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); - if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) - { - // If we get here, it means: - // 1. This is not our first SRV answer - // 2. The interface ID is different, but the target host and port are the same - // This implies that we're seeing the exact same SRV record on more than one interface, so we should - // make our address queries at least as broad as the original SRV query so that we catch all the answers. - query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface - query->qAv6.InterfaceID = query->qSRV.InterfaceID; - } - else - { - query->qAv4.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); - query->qAv6.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); - } - debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query->qAv4.qname.c, DNSTypeName(query->qAv4.qtype)); - mDNS_StartQuery(m, &query->qAv4); - // Only do the AAAA query if this machine actually has IPv6 active - if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); - } - else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) - { - if (++query->Answers >= 100) - debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", - query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, - mDNSVal16(answer->rdata->u.srv.port)); - query->ServiceInfoQueryCallback(m, query); - } - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. -} - -mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) -{ - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - if (!AddRecord) return; - if (answer->rrtype != kDNSType_TXT) return; - if (answer->rdlength > sizeof(query->info->TXTinfo)) return; - - query->GotTXT = mDNStrue; - query->info->TXTlen = answer->rdlength; - query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero - mDNSPlatformMemCopy(query->info->TXTinfo, answer->rdata->u.txt.c, answer->rdlength); - - verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); - - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. - if (query->ServiceInfoQueryCallback && query->GotADD) - { - if (++query->Answers >= 100) - debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", - query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); - query->ServiceInfoQueryCallback(m, query); - } -} - -mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) -{ - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - //LogInfo("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); - if (!AddRecord) return; - - if (answer->rrtype == kDNSType_A) - { - query->info->ip.type = mDNSAddrType_IPv4; - query->info->ip.ip.v4 = answer->rdata->u.ipv4; - } - else if (answer->rrtype == kDNSType_AAAA) - { - query->info->ip.type = mDNSAddrType_IPv6; - query->info->ip.ip.v6 = answer->rdata->u.ipv6; - } - else - { - debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); - return; - } - - query->GotADD = mDNStrue; - query->info->InterfaceID = answer->InterfaceID; - - verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); - - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. - if (query->ServiceInfoQueryCallback && query->GotTXT) - { - if (++query->Answers >= 100) - debugf(answer->rrtype == kDNSType_A ? - "**** WARNING **** have given %lu answers for %##s (A) %.4a" : - "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", - query->Answers, query->qSRV.qname.c, &answer->rdata->u.data); - query->ServiceInfoQueryCallback(m, query); - } -} - -// On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure -// If the query is not interface-specific, then InterfaceID may be zero -// Each time the Callback is invoked, the remainder of the fields will have been filled in -// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response -mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, - ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) -{ - mStatus status; - mDNS_Lock(m); - - query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qSRV.InterfaceID = info->InterfaceID; - query->qSRV.flags = 0; - query->qSRV.Target = zeroAddr; - AssignDomainName(&query->qSRV.qname, &info->name); - query->qSRV.qtype = kDNSType_SRV; - query->qSRV.qclass = kDNSClass_IN; - query->qSRV.LongLived = mDNSfalse; - query->qSRV.ExpectUnique = mDNStrue; - query->qSRV.ForceMCast = mDNSfalse; - query->qSRV.ReturnIntermed = mDNSfalse; - query->qSRV.SuppressUnusable = mDNSfalse; - query->qSRV.DenyOnCellInterface = mDNSfalse; - query->qSRV.DenyOnExpInterface = mDNSfalse; - query->qSRV.SearchListIndex = 0; - query->qSRV.AppendSearchDomains = 0; - query->qSRV.RetryWithSearchDomains = mDNSfalse; - query->qSRV.TimeoutQuestion = 0; - query->qSRV.WakeOnResolve = 0; - query->qSRV.UseBackgroundTrafficClass = mDNSfalse; - query->qSRV.ValidationRequired = 0; - query->qSRV.ValidatingResponse = 0; - query->qSRV.ProxyQuestion = 0; - query->qSRV.qnameOrig = mDNSNULL; - query->qSRV.AnonInfo = mDNSNULL; - query->qSRV.QuestionCallback = FoundServiceInfoSRV; - query->qSRV.QuestionContext = query; - - query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qTXT.InterfaceID = info->InterfaceID; - query->qTXT.flags = 0; - query->qTXT.Target = zeroAddr; - AssignDomainName(&query->qTXT.qname, &info->name); - query->qTXT.qtype = kDNSType_TXT; - query->qTXT.qclass = kDNSClass_IN; - query->qTXT.LongLived = mDNSfalse; - query->qTXT.ExpectUnique = mDNStrue; - query->qTXT.ForceMCast = mDNSfalse; - query->qTXT.ReturnIntermed = mDNSfalse; - query->qTXT.SuppressUnusable = mDNSfalse; - query->qTXT.DenyOnCellInterface = mDNSfalse; - query->qTXT.DenyOnExpInterface = mDNSfalse; - query->qTXT.SearchListIndex = 0; - query->qTXT.AppendSearchDomains = 0; - query->qTXT.RetryWithSearchDomains = mDNSfalse; - query->qTXT.TimeoutQuestion = 0; - query->qTXT.WakeOnResolve = 0; - query->qTXT.UseBackgroundTrafficClass = mDNSfalse; - query->qTXT.ValidationRequired = 0; - query->qTXT.ValidatingResponse = 0; - query->qTXT.ProxyQuestion = 0; - query->qTXT.qnameOrig = mDNSNULL; - query->qTXT.AnonInfo = mDNSNULL; - query->qTXT.QuestionCallback = FoundServiceInfoTXT; - query->qTXT.QuestionContext = query; - - query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qAv4.InterfaceID = info->InterfaceID; - query->qAv4.flags = 0; - query->qAv4.Target = zeroAddr; - query->qAv4.qname.c[0] = 0; - query->qAv4.qtype = kDNSType_A; - query->qAv4.qclass = kDNSClass_IN; - query->qAv4.LongLived = mDNSfalse; - query->qAv4.ExpectUnique = mDNStrue; - query->qAv4.ForceMCast = mDNSfalse; - query->qAv4.ReturnIntermed = mDNSfalse; - query->qAv4.SuppressUnusable = mDNSfalse; - query->qAv4.DenyOnCellInterface = mDNSfalse; - query->qAv4.DenyOnExpInterface = mDNSfalse; - query->qAv4.SearchListIndex = 0; - query->qAv4.AppendSearchDomains = 0; - query->qAv4.RetryWithSearchDomains = mDNSfalse; - query->qAv4.TimeoutQuestion = 0; - query->qAv4.WakeOnResolve = 0; - query->qAv4.UseBackgroundTrafficClass = mDNSfalse; - query->qAv4.ValidationRequired = 0; - query->qAv4.ValidatingResponse = 0; - query->qAv4.ProxyQuestion = 0; - query->qAv4.qnameOrig = mDNSNULL; - query->qAv4.AnonInfo = mDNSNULL; - query->qAv4.QuestionCallback = FoundServiceInfo; - query->qAv4.QuestionContext = query; - - query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qAv6.InterfaceID = info->InterfaceID; - query->qAv6.flags = 0; - query->qAv6.Target = zeroAddr; - query->qAv6.qname.c[0] = 0; - query->qAv6.qtype = kDNSType_AAAA; - query->qAv6.qclass = kDNSClass_IN; - query->qAv6.LongLived = mDNSfalse; - query->qAv6.ExpectUnique = mDNStrue; - query->qAv6.ForceMCast = mDNSfalse; - query->qAv6.ReturnIntermed = mDNSfalse; - query->qAv6.SuppressUnusable = mDNSfalse; - query->qAv6.DenyOnCellInterface = mDNSfalse; - query->qAv6.DenyOnExpInterface = mDNSfalse; - query->qAv6.SearchListIndex = 0; - query->qAv6.AppendSearchDomains = 0; - query->qAv6.RetryWithSearchDomains = mDNSfalse; - query->qAv6.TimeoutQuestion = 0; - query->qAv6.UseBackgroundTrafficClass = mDNSfalse; - query->qAv6.ValidationRequired = 0; - query->qAv6.ValidatingResponse = 0; - query->qAv6.ProxyQuestion = 0; - query->qAv6.qnameOrig = mDNSNULL; - query->qAv6.AnonInfo = mDNSNULL; - query->qAv6.QuestionCallback = FoundServiceInfo; - query->qAv6.QuestionContext = query; - - query->GotSRV = mDNSfalse; - query->GotTXT = mDNSfalse; - query->GotADD = mDNSfalse; - query->Answers = 0; - - query->info = info; - query->ServiceInfoQueryCallback = Callback; - query->ServiceInfoQueryContext = Context; - -// info->name = Must already be set up by client -// info->interface = Must already be set up by client - info->ip = zeroAddr; - info->port = zeroIPPort; - info->TXTlen = 0; - - // We use mDNS_StartQuery_internal here because we're already holding the lock - status = mDNS_StartQuery_internal(m, &query->qSRV); - if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); - if (status != mStatus_NoError) mDNS_StopResolveService(m, query); - - mDNS_Unlock(m); - return(status); -} - -mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q) -{ - mDNS_Lock(m); - // We use mDNS_StopQuery_internal here because we're already holding the lock - if (q->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qSRV); - if (q->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qTXT); - if (q->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv4); - if (q->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv6); - mDNS_Unlock(m); -} mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) @@ -12331,8 +12460,6 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->ForceMCast = mDNSfalse; question->ReturnIntermed = mDNSfalse; question->SuppressUnusable = mDNSfalse; - question->DenyOnCellInterface = mDNSfalse; - question->DenyOnExpInterface = mDNSfalse; question->SearchListIndex = 0; question->AppendSearchDomains = 0; question->RetryWithSearchDomains = mDNSfalse; @@ -12345,6 +12472,7 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->qnameOrig = mDNSNULL; question->AnonInfo = mDNSNULL; question->pid = mDNSPlatformGetPID(); + question->euid = 0; question->QuestionCallback = Callback; question->QuestionContext = Context; if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); @@ -12461,26 +12589,20 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) { char buffer[MAX_REVERSE_MAPPING_NAME]; NetworkInterfaceInfo *primary; + mDNSu8 recordType; - if (!set->McastTxRx) - { - LogInfo("AdvertiseInterface: Returning, not multicast capable %s", set->ifname); - return; - } -#if TARGET_OS_EMBEDDED - if (!m->AutoTargetServices) + if (m->AutoTargetServices == 0) { LogInfo("AdvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); return; } -#endif primary = FindFirstAdvertisedInterface(m); if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary // If interface is marked as a direct link, we can assume the address record is unique // and does not need to go through the probe phase of the probe/announce packet sequence. - mDNSu8 recordType = (set->DirectLink ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique); + recordType = (set->DirectLink ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique); if (set->DirectLink) LogInfo("AdvertiseInterface: Marking address record as kDNSRecordTypeKnownUnique for %s", set->ifname); @@ -12555,28 +12677,24 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) { - NetworkInterfaceInfo *intf; + if (m->AutoTargetServices == 0) + { + LogInfo("DeadvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); + return; + } - // If we still have address records referring to this one, update them - NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); - AuthRecord *A = primary ? &primary->RR_A : mDNSNULL; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->RR_A.RRSet == &set->RR_A) - intf->RR_A.RRSet = A; +#if APPLE_OSX_mDNSResponder + D2D_stop_advertising_interface(set); +#endif // APPLE_OSX_mDNSResponder // Unregister these records. // When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered. // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal(). - if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); - if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); + if (set->RR_A .resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); + if (set->RR_PTR .resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); - -#if APPLE_OSX_mDNSResponder - D2D_stop_advertising_interface(set); -#endif // APPLE_OSX_mDNSResponder - } mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m) @@ -12594,7 +12712,6 @@ mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m) mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) { -#if TARGET_OS_EMBEDDED NetworkInterfaceInfo *intf; for (intf = m->HostInterfaces; intf; intf = intf->next) { @@ -12604,15 +12721,28 @@ mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) DeadvertiseInterface(m, intf); } } -#else - (void) m; //unused +} + +// Change target host name for record. +mDNSlocal void UpdateTargetHostName(mDNS *const m, AuthRecord *const rr) +{ +#if APPLE_OSX_mDNSResponder + // If this record was also registered with any D2D plugins, stop advertising + // the version with the old host name. + D2D_stop_advertising_record(rr); +#endif + + SetTargetToHostName(m, rr); + +#if APPLE_OSX_mDNSResponder + // Advertise the record with the updated host name with the D2D plugins if appropriate. + D2D_start_advertising_record(rr); #endif } mDNSexport void mDNS_SetFQDN(mDNS *const m) { domainname newmname; - NetworkInterfaceInfo *intf; AuthRecord *rr; newmname.c[0] = 0; @@ -12625,19 +12755,13 @@ mDNSexport void mDNS_SetFQDN(mDNS *const m) else { AssignDomainName(&m->MulticastHostname, &newmname); - - // 1. Stop advertising our address records on all interfaces - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) DeadvertiseInterface(m, intf); - - // 2. Start advertising our address records using the new name - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) AdvertiseInterface(m, intf); + DeadvertiseAllInterfaceRecords(m); + AdvertiseAllInterfaceRecords(m); } // 3. Make sure that any AutoTarget SRV records (and the like) get updated - for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); - for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); + for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->AutoTarget) UpdateTargetHostName(m, rr); + for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) UpdateTargetHostName(m, rr); mDNS_Unlock(m); } @@ -12726,7 +12850,7 @@ mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set) // be stopped during interface deregistration. We can't sanity check to see if the // question has been stopped or not before initializing it to -1 because we need to // initialize it to -1 the very first time. - + set->NetWakeBrowse.ThisQInterval = -1; for (i=0; i<3; i++) { @@ -12876,6 +13000,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s } LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); + if (m->SuppressProbes == 0 || m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0) m->SuppressProbes = NonZeroTime(m->timenow + probedelay); @@ -12902,14 +13027,8 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s if (!q->ThisQInterval || q->ThisQInterval > initial) { - q->ThisQInterval = initial; - -#if mDNS_REQUEST_UNICAST_RESPONSE - q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; -#else // mDNS_REQUEST_UNICAST_RESPONSE - q->RequestUnicast = SET_QU_IN_FIRST_QUERY; -#endif // mDNS_REQUEST_UNICAST_RESPONSE - + q->ThisQInterval = initial; + q->RequestUnicast = kDefaultRequestUnicastCount; } q->LastQTime = m->timenow - q->ThisQInterval + qdelay; q->RecentAnswerPkts = 0; @@ -12951,6 +13070,9 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se { NetworkInterfaceInfo **p = &m->HostInterfaces; mDNSBool revalidate = mDNSfalse; + NetworkInterfaceInfo *primary; + NetworkInterfaceInfo *intf; + AuthRecord *A; mDNS_Lock(m); @@ -12967,14 +13089,13 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se if (!set->InterfaceActive) { // If this interface not the active member of its set, update the v4/v6Available flags for the active member - NetworkInterfaceInfo *intf; for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID) UpdateInterfaceProtocols(m, intf); } else { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, set->InterfaceID); + intf = FirstInterfaceForID(m, set->InterfaceID); if (intf) { LogInfo("mDNS_DeregisterInterface: Another representative of InterfaceID %d %s (%#a) exists;" @@ -13007,7 +13128,7 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se if (set->McastTxRx && flapping) { - LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + LogMsg("mDNS_DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); m->mDNSStats.InterfaceDownFlap++; } @@ -13048,6 +13169,15 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se } } + // If we still have address records referring to this one, update them. + // This is safe, because this NetworkInterfaceInfo has already been unlinked from the list, + // so the call to FindFirstAdvertisedInterface() won’t accidentally find it. + primary = FindFirstAdvertisedInterface(m); + A = primary ? &primary->RR_A : mDNSNULL; + for (intf = m->HostInterfaces; intf; intf = intf->next) + if (intf->RR_A.RRSet == &set->RR_A) + intf->RR_A.RRSet = A; + // If we were advertising on this interface, deregister those address and reverse-lookup records now if (set->Advertise) DeadvertiseInterface(m, set); @@ -13184,13 +13314,17 @@ mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) } +// Derive AuthRecType from the coreFlag* values. +// Note, this is not using the external flags values, kDNSServiceFlags*, defined in dns_sd.h. +// It should be changed to do so once the use of coreFlag* is completely replaced with +// the use the kDNSServiceFlags* definitions within mDNSResponder. mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) { AuthRecType artype; if (InterfaceID == mDNSInterface_LocalOnly) artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) + else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE) artype = AuthRecordP2P; else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P) && (flags & coreFlagIncludeAWDL)) @@ -13205,6 +13339,18 @@ mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) return artype; } +// Used to derive the original D2D specific flags specified by the client in the registration +// when we don't have access to the original flag (kDNSServiceFlags*) values. +mDNSexport mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType) +{ + mDNSu32 flags = 0; + if ((authRecType == AuthRecordAnyIncludeP2P) || (authRecType == AuthRecordAnyIncludeAWDLandP2P)) + flags |= kDNSServiceFlagsIncludeP2P; + else if ((authRecType == AuthRecordAnyIncludeAWDL) || (authRecType == AuthRecordAnyIncludeAWDLandP2P)) + flags |= kDNSServiceFlagsIncludeAWDL; + return flags; +} + // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) // Type is service type (e.g. "_ipp._tcp.") @@ -13259,8 +13405,8 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, if (mDNSIPPortIsZero(port)) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, flags)); - // If the client is registering an oversized TXT record, - // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it + // If the caller is registering an oversized TXT record, + // it is the caller's responsibility to allocate a ServiceRecordSet structure that is large enough for it if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; @@ -13298,7 +13444,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, sr->SubTypes[i].Additional1 = &sr->RR_SRV; sr->SubTypes[i].Additional2 = &sr->RR_TXT; } - + SetAnonInfoSRS(sr, NumSubTypes); // 3. Set up the SRV record rdata. @@ -13358,6 +13504,7 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, mStatus status; AuthRecType artype; mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID; + ResourceRecord *rr; artype = setAuthRecType(InterfaceID, flags); @@ -13367,11 +13514,40 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name); mDNS_Lock(m); + rr = mDNSNULL; + if (extra->r.resrec.rrtype == kDNSType_TXT) + { + if (sr->RR_TXT.resrec.RecordType & kDNSRecordTypeUniqueMask) rr = &sr->RR_TXT.resrec; + } + else if (extra->r.resrec.rrtype == kDNSType_SRV) + { + if (sr->RR_SRV.resrec.RecordType & kDNSRecordTypeUniqueMask) rr = &sr->RR_SRV.resrec; + } + + if (!rr) + { + ExtraResourceRecord *srExtra; + + for (srExtra = sr->Extras; srExtra; srExtra = srExtra->next) + { + if ((srExtra->r.resrec.rrtype == extra->r.resrec.rrtype) && (srExtra->r.resrec.RecordType & kDNSRecordTypeUniqueMask)) + { + rr = &srExtra->r.resrec; + break; + } + } + } + + if (rr && (extra->r.resrec.rroriginalttl != rr->rroriginalttl)) + { + LogMsg("mDNS_AddRecordToService: Correcting TTL from %4d to %4d for %s", + extra->r.resrec.rroriginalttl, rr->rroriginalttl, RRDisplayString(m, &extra->r.resrec)); + extra->r.resrec.rroriginalttl = rr->rroriginalttl; + } + e = &sr->Extras; while (*e) e = &(*e)->next; - if (ttl == 0) ttl = kStandardTTL; - extra->r.DependentOn = &sr->RR_SRV; debugf("mDNS_AddRecordToService adding record to %##s %s %d", @@ -13544,7 +13720,7 @@ mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, if (InterfaceID == mDNSInterface_LocalOnly) artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) + else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE) artype = AuthRecordP2P; else artype = AuthRecordAny; @@ -13653,8 +13829,9 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, const char *const msg = mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC) ? msg1 : (rr->AnnounceCount == InitialAnnounceCount) ? msg2 : mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; - LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", - intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + LogMsg("Arp %-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", + intf->ifname, msg, arp->sha.b, arp->spa.b, arp->tpa.b, + &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); if (msg == msg1) { if ( rr->ProbeRestartCount < MAX_PROBE_RESTARTS) @@ -13668,7 +13845,7 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, } else if (msg == msg4) { - SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha); + SendARP(m, 2, rr, (mDNSv4Addr *)arp->tpa.b, &arp->sha, (mDNSv4Addr *)arp->spa.b, &arp->sha); } } } @@ -13682,7 +13859,7 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, // If the sender hardware address is the original owner this is benign, so we just suppress our own proxy answering for a while longer. // If the sender hardware address is *not* the original owner, then this is a conflict, and we need to wake the sleeping machine to handle it. if (mDNSSameEthAddress(&arp->sha, &intf->MAC)) - debugf("ARP from self for %.4a", &arp->tpa); + debugf("ARP from self for %.4a", arp->tpa.b); else { if (!mDNSSameIPv4Address(arp->spa, zerov4Addr)) @@ -13692,22 +13869,22 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, { if (mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC)) { - LogSPS("%-7s ARP from %.6a %.4a for %.4a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname, - &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + LogMsg("%-7s ARP from %.6a %.4a for %.4a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname, + arp->sha.b, arp->spa.b, arp->tpa.b, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); } else { RestartARPProbing(m, rr); if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) { - LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, + LogMsg("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", - &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); + arp->sha.b, arp->spa.b, arp->tpa.b, ARDisplayString(m, rr)); } else { LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, - &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + arp->sha.b, arp->spa.b, arp->tpa.b, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); } } @@ -13771,9 +13948,9 @@ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, } else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); - else if (msg == msg4) + else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha); - else if (msg == msg5) + else if (msg == msg5) SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); } } @@ -14025,11 +14202,13 @@ mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, c else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv4) && (pkt->v4.flagsfrags.b[0] & 0x1F) == 0 && pkt->v4.flagsfrags.b[1] == 0) { const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4; + const mDNSu8 * transEnd = p + 14 + mDNSVal16(pkt->v4.totlen); + if (transEnd > end) transEnd = end; debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst); src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src; dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst; - if (end >= trans + RequiredCapLen(pkt->v4.protocol)) - mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, end, InterfaceID, 0); + if (transEnd >= trans + RequiredCapLen(pkt->v4.protocol)) + mDNSCoreReceiveRawTransportPacket(m, ð->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, transEnd, InterfaceID, 0); } // Is IPv6? Length must be at least 14 + 28 = 42 bytes else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6)) @@ -14160,6 +14339,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, if (!rrcachestorage) rrcachesize = 0; m->p = p; + m->NetworkChanged = 0; m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; m->DivertMulticastAdvertisements = mDNSfalse; @@ -14198,6 +14378,14 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->NextScheduledSPS = timenow + 0x78000000; m->NextScheduledKA = timenow + 0x78000000; m->NextScheduledStopTime = timenow + 0x78000000; + m->NextBLEServiceTime = 0; // zero indicates inactive + +#if BONJOUR_ON_DEMAND + m->NextBonjourDisableTime = 0; // Timer active when non zero. + m->BonjourEnabled = 0; // Set when Bonjour on Demand is enabled and Bonjour is currently enabled. +#endif // BONJOUR_ON_DEMAND + + m->DelayConflictProcessing = MAX_CONFLICT_PROCESSING_DELAYS; m->RandomQueryDelay = 0; m->RandomReconfirmDelay = 0; m->PktNum = 0; @@ -14282,13 +14470,12 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->WABBrowseQueriesCount = 0; m->WABLBrowseQueriesCount = 0; m->WABRegQueriesCount = 0; -#if !TARGET_OS_EMBEDDED - m->mDNSOppCaching = mDNStrue; -#else - m->mDNSOppCaching = mDNSfalse; -#endif m->AutoTargetServices = 0; +#if BONJOUR_ON_DEMAND + m->NumAllInterfaceRecords = 0; + m->NumAllInterfaceQuestions = 0; +#endif // NAT traversal fields m->LLQNAT.clientCallback = mDNSNULL; m->LLQNAT.clientContext = mDNSNULL; @@ -14327,6 +14514,8 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->SPSBrowseCallback = mDNSNULL; m->ProxyRecords = 0; + m->DNSPushServers = mDNSNULL; + m->DNSPushZones = mDNSNULL; #endif #if APPLE_OSX_mDNSResponder @@ -14391,7 +14580,8 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const mDNSBool purge = cr->resrec.RecordType == kDNSRecordTypePacketNegative || cr->resrec.rrtype == kDNSType_A || cr->resrec.rrtype == kDNSType_AAAA || - cr->resrec.rrtype == kDNSType_SRV; + cr->resrec.rrtype == kDNSType_SRV || + cr->resrec.rrtype == kDNSType_CNAME; (void) lameduck; (void) ptr; @@ -14525,6 +14715,10 @@ mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) ptr->penaltyTime = 0; NumUnicastDNSServers--; ptr->flags |= DNSServer_FlagDelete; +#if APPLE_OSX_mDNSResponder + if (ptr->flags & DNSServer_FlagUnreachable) + NumUnreachableDNSServers--; +#endif } // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at // mcast resolvers. Today we get both mcast and ucast configuration using the same @@ -14539,12 +14733,33 @@ mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) ptr->penaltyTime = 0; NumUnicastDNSServers++; ptr->flags &= ~DNSServer_FlagDelete; +#if APPLE_OSX_mDNSResponder + if (ptr->flags & DNSServer_FlagUnreachable) + NumUnreachableDNSServers++; +#endif } for (mr = m->McastResolvers; mr; mr = mr->next) mr->flags &= ~McastResolver_FlagDelete; } } +mDNSlocal void SetDynDNSHostNameIfChanged(mDNS *const m, domainname *const fqdn) +{ + // Did our FQDN change? + if (!SameDomainName(fqdn, &m->FQDN)) + { + if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN); + + AssignDomainName(&m->FQDN, fqdn); + + if (m->FQDN.c[0]) + { + mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); + mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL); + } + } +} + mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) { mDNSu32 slot; @@ -14575,6 +14790,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) SetConfigState(m, mDNStrue); if (!mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL, mDNStrue)) { + SetDynDNSHostNameIfChanged(m, &fqdn); SetConfigState(m, mDNSfalse); mDNS_Unlock(m); LogInfo("uDNS_SetupDNSConfig: No configuration change"); @@ -14587,7 +14803,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // affecting them as they never change. while (*mres) { - if (((*mres)->flags & DNSServer_FlagDelete) != 0) + if (((*mres)->flags & McastResolver_FlagDelete) != 0) { mr = *mres; *mres = (*mres)->next; @@ -14751,6 +14967,14 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); } } + + // If a cache record's DNSServer pointer is NULL, but its active question got a DNSServer in this DNS configuration + // update, then use its DNSServer. This way, the active question and its duplicates don't miss out on RMV events. + if (!cr->resrec.rDNSServer && cr->CRActiveQuestion && cr->CRActiveQuestion->qDNSServer) + { + cr->resrec.rDNSServer = cr->CRActiveQuestion->qDNSServer; + LogInfo("uDNS_SetupDNSConfig: Using active question's DNS server %#a for cache record %s", &cr->resrec.rDNSServer->addr, CRDisplayString(m, cr)); + } } while (*p) @@ -14781,7 +15005,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) if (qptr->qDNSServer == ptr) { - LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) poining to DNSServer Address %#a" + LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) pointing to DNSServer Address %#a" " to be freed", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, &ptr->addr); qptr->validDNSServers = zeroOpaque64; qptr->qDNSServer = mDNSNULL; @@ -14791,7 +15015,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) { LogInfo("uDNS_SetupDNSConfig: Cache Record %s, Active question %##s (%s) (scope:%p), pointing to DNSServer %#a (to be deleted)," " resetting to question's DNSServer Address %#a", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), - qptr->InterfaceID, &ptr->addr, (qptr->qDNSServer ? &qptr->qDNSServer->addr : mDNSNULL)); + qptr->InterfaceID, &ptr->addr, (qptr->qDNSServer) ? &qptr->qDNSServer->addr : mDNSNULL); cr->resrec.rDNSServer = qptr->qDNSServer; } } @@ -14838,19 +15062,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) RestartRecordGetZoneData(m); } - // Did our FQDN change? - if (!SameDomainName(&fqdn, &m->FQDN)) - { - if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN); - - AssignDomainName(&m->FQDN, &fqdn); - - if (m->FQDN.c[0]) - { - mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); - mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL); - } - } + SetDynDNSHostNameIfChanged(m, &fqdn); mDNS_Unlock(m); @@ -14913,7 +15125,6 @@ mDNSlocal void DeregLoop(mDNS *const m, AuthRecord *const start) mDNSexport void mDNS_StartExit(mDNS *const m) { - NetworkInterfaceInfo *intf; AuthRecord *rr; mDNS_Lock(m); @@ -14953,9 +15164,7 @@ mDNSexport void mDNS_StartExit(mDNS *const m) } #endif - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->Advertise) - DeadvertiseInterface(m, intf); + DeadvertiseAllInterfaceRecords(m); // Shut down all our active NAT Traversals while (m->NATTraversals) @@ -15015,14 +15224,13 @@ mDNSexport void mDNS_StartExit(mDNS *const m) mDNSexport void mDNS_FinalExit(mDNS *const m) { mDNSu32 rrcache_active = 0; - mDNSu32 rrcache_totalused = 0; + mDNSu32 rrcache_totalused = m->rrcache_totalused; mDNSu32 slot; AuthRecord *rr; LogInfo("mDNS_FinalExit: mDNSPlatformClose"); mDNSPlatformClose(m); - rrcache_totalused = m->rrcache_totalused; for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) { while (m->rrcache_hash[slot]) @@ -15041,7 +15249,7 @@ mDNSexport void mDNS_FinalExit(mDNS *const m) } debugf("mDNS_FinalExit: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active); if (rrcache_active != m->rrcache_active) - LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active); + LogMsg("*** ERROR *** rrcache_totalused %lu; rrcache_active %lu != m->rrcache_active %lu", rrcache_totalused, rrcache_active, m->rrcache_active); for (rr = m->ResourceRecords; rr; rr = rr->next) LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr)); diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index 5467ae6..68a696e 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -148,12 +148,18 @@ extern void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...) IS // (or completely overhauled to use the new "log to a separate file" facility) #define LogMsgNoIdent LogMsg +#if APPLE_OSX_mDNSResponder +extern void LogFatalError(const char *format, ...); +#else +#define LogFatalError LogMsg +#endif + #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 extern void *mallocL(char *msg, unsigned int size); extern void freeL(char *msg, void *x); -extern void LogMemCorruption(const char *format, ...); extern void uds_validatelists(void); extern void udns_validatelists(void *const v); +extern void LogMemCorruption(const char *format, ...); #else #define mallocL(X,Y) malloc(Y) #define freeL(X,Y) free(Y) diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h index bafeb02..1ee28d8 100755 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -70,6 +70,7 @@ #include "mDNSDebug.h" #if APPLE_OSX_mDNSResponder #include +#include #endif #ifdef __cplusplus @@ -90,20 +91,14 @@ extern "C" { // In order to disable the above features pass the option to your compiler, e.g. -D UNICAST_DISABLED -// Additionally, the LIMITED_RESOURCES_TARGET compile option will eliminate caching and -// and reduce the maximum DNS message sizes. +// Additionally, the LIMITED_RESOURCES_TARGET compile option will reduce the maximum DNS message sizes. #ifdef LIMITED_RESOURCES_TARGET // Don't support jumbo frames -#define AbsoluteMaxDNSMessageData 1500 -// By the time you add IPv6 header (40 bytes) UDP header (8 bytes) and DNS message header (12 bytes) -// this makes 1560 which is 60 bytes over the standard Ethernet MTU. D'oh! - +// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total +#define AbsoluteMaxDNSMessageData 1440 // StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes) -#define MaximumRDSize 264 -// Don't cache anything -#define AUTH_HASH_SLOTS 1 -#define CACHE_HASH_SLOTS 1 +#define MaximumRDSize 264 #endif // *************************************************************************** @@ -302,20 +297,20 @@ typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opa #define bit_clr_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] &= ~(1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) #define bit_get_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] & (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) -enum +typedef enum { mDNSAddrType_None = 0, mDNSAddrType_IPv4 = 4, mDNSAddrType_IPv6 = 6, mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording -}; +} mDNSAddr_Type; -enum +typedef enum { mDNSTransport_None = 0, mDNSTransport_UDP = 1, mDNSTransport_TCP = 2 -}; +} mDNSTransport_Type; typedef struct { @@ -369,7 +364,8 @@ enum mStatus_NoRouter = -65566, mStatus_PollingMode = -65567, mStatus_Timeout = -65568, - // -65568 to -65786 currently unused; available for allocation + mStatus_HostUnreachErr = -65569, + // -65570 to -65786 currently unused; available for allocation // tcp connection status mStatus_ConnPending = -65787, @@ -486,7 +482,7 @@ typedef struct UDPSocket_struct UDPSocket; #define mDNS_numPrereqs numAnswers #define mDNS_numUpdates numAuthorities -typedef packedstruct +typedef struct { mDNSOpaque16 id; mDNSOpaque16 flags; @@ -503,7 +499,7 @@ typedef packedstruct #define AbsoluteMaxDNSMessageData 8940 #endif #define NormalMaxDNSMessageData 1440 -typedef packedstruct +typedef struct { DNSMessageHeader h; // Note: Size 12 bytes mDNSu8 data[AbsoluteMaxDNSMessageData]; // 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000 @@ -556,7 +552,7 @@ typedef packedstruct { mDNSu8 vlen; mDNSu8 tos; - mDNSu16 totlen; + mDNSOpaque16 totlen; mDNSOpaque16 id; mDNSOpaque16 flagsfrags; mDNSu8 ttl; @@ -621,7 +617,7 @@ typedef packedstruct mDNSu16 checksum; } UDPHeader; // 8 bytes; IP protocol type 0x11 -typedef packedstruct +typedef struct { mDNSu8 type; // 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement mDNSu8 code; @@ -813,14 +809,14 @@ typedef struct TrustAnchor struct TrustAnchor *next; int digestLen; mDNSu32 validFrom; - mDNSu32 validUntil; + mDNSu32 validUntil; domainname zone; rdataDS rds; } TrustAnchor; //size of rdataRRSIG excluding signerName and signature (which are variable fields) #define RRSIG_FIXED_SIZE 18 -typedef packedstruct +typedef struct { mDNSu16 typeCovered; mDNSu8 alg; @@ -829,7 +825,7 @@ typedef packedstruct mDNSu32 sigExpireTime; mDNSu32 sigInceptTime; mDNSu16 keyTag; - mDNSu8 *signerName; + mDNSu8 signerName[1]; // signerName is a dynamically-sized array // mDNSu8 *signature } rdataRRSig; @@ -880,10 +876,10 @@ typedef packedstruct // For example, SHA-1 hash of 20 bytes will be encoded as 20/5 * 8 = 32 base32 // bytes. For a max domain name size of 255 bytes of base32 encoding : (255/8)*5 // is the max hash length possible. -#define NSEC3_MAX_HASH_LEN 155 +#define NSEC3_MAX_HASH_LEN 155 // In NSEC3, the names are hashed and stored in the first label and hence cannot exceed label // size. -#define NSEC3_MAX_B32_LEN MAX_DOMAIN_LABEL +#define NSEC3_MAX_B32_LEN MAX_DOMAIN_LABEL // We define it here instead of dnssec.h so that these values can be used // in files without bringing in all of dnssec.h unnecessarily. @@ -942,7 +938,7 @@ typedef struct } TracerOptData; // Note: rdataOPT format may be repeated an arbitrary number of times in a single resource record -typedef packedstruct +typedef struct { mDNSu16 opt; mDNSu16 optlen; @@ -1186,7 +1182,7 @@ typedef enum PCPResult_ExcesRemotePeer = 13 } PCPResult_t; -typedef packedstruct +typedef struct { mDNSu8 version; mDNSu8 opCode; @@ -1201,7 +1197,7 @@ typedef packedstruct mDNSv6Addr extAddress; } PCPMapRequest; -typedef packedstruct +typedef struct { mDNSu8 version; mDNSu8 opCode; @@ -1312,16 +1308,11 @@ struct NATTraversalInfo_struct enum { - DNSServer_Untested = 0, - DNSServer_Passed = 1, - DNSServer_Failed = 2, - DNSServer_Disabled = 3 -}; - -enum -{ - DNSServer_FlagDelete = 1, - DNSServer_FlagNew = 2 + DNSServer_FlagDelete = 0x1, + DNSServer_FlagNew = 0x2, +#if APPLE_OSX_mDNSResponder + DNSServer_FlagUnreachable = 0x4, +#endif }; enum @@ -1344,8 +1335,9 @@ enum { kScopeNone = 0, // DNS server used by unscoped questions kScopeInterfaceID = 1, // Scoped DNS server used only by scoped questions - kScopeServiceID = 2 // Service specific DNS server used only by questions + kScopeServiceID = 2, // Service specific DNS server used only by questions // have a matching serviceID + kScopesMaxCount = 3 // Max count for scopes enum }; // Note: DNSSECAware is set if we are able to get a valid response to @@ -1362,10 +1354,7 @@ typedef struct DNSServer mDNSs32 serviceID; mDNSAddr addr; mDNSIPPort port; - mDNSOpaque16 testid; mDNSu32 flags; // Set when we're planning to delete this from the list - mDNSu32 teststate; // Have we sent bug-detection query to this server? - mDNSs32 lasttest; // Time we sent last bug-detection query to this server domainname domain; // name->server matching for "split dns" mDNSs32 penaltyTime; // amount of time this server is penalized mDNSu32 scoped; // See the scoped enum above @@ -1413,7 +1402,7 @@ struct ResourceRecord_struct // that are interface-specific (e.g. address records, especially linklocal addresses) const domainname *name; RData *rdata; // Pointer to storage for this rdata - DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry;null for multicast + DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry; null for multicast AnonymousInfo *AnonInfo; // Anonymous Information }; @@ -1502,7 +1491,7 @@ struct AuthRecord_struct AuthRecord *next; // Next in list; first element of structure for efficiency reasons // Field Group 1: Common ResourceRecord fields - ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit + ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit (now 44/64) // Field Group 2: Persistent metadata for Authoritative Records AuthRecord *Additional1; // Recommended additional record to include in response (e.g. SRV for PTR record) @@ -1608,8 +1597,11 @@ struct AuthRecord_struct #define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || (Q)->ProxyQuestion || \ ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) +// AuthRecordLocalOnly records are registered using mDNSInterface_LocalOnly and +// AuthRecordP2P records are created by D2DServiceFound events. Both record types are kept on the same list. #define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P) +// All other auth records, not including those defined as RRLocalOnly(). #define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P || (rr)->ARType == AuthRecordAnyIncludeAWDL || (rr)->ARType == AuthRecordAnyIncludeAWDLandP2P) // Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address @@ -1634,7 +1626,7 @@ typedef struct ARListElem struct CacheRecord_struct { CacheRecord *next; // Next in list; first element of structure for efficiency reasons - ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit + ResourceRecord resrec; // 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit (now 44/64) // Transient state for Cache Records CacheRecord *NextInKAList; // Link to the next element in the chain of known answers to send @@ -1658,13 +1650,12 @@ struct CacheRecord_struct CacheRecord *soa; // SOA record to return for proxy questions mDNSAddr sourceAddress; // node from which we received this record - // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit + // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit (now 160 bytes for 64-bit) RData_small smallrdatastorage; // Storage for small records is right here (4 bytes header + 68 bytes data = 72 bytes) }; // Should match the CacheGroup_struct members, except namestorage[]. Only used to calculate -// the size of the namestorage array in CacheGroup_struct so that -// sizeof(CacheGroup) == sizeof(CacheRecord) +// the size of the namestorage array in CacheGroup_struct so that sizeof(CacheGroup) == sizeof(CacheRecord) struct CacheGroup_base { CacheGroup *next; @@ -1805,6 +1796,25 @@ enum enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; +// DNS Push Notification +typedef enum +{ + DNSPUSH_NOERROR = 0, + DNSPUSH_FORMERR = 1, + DNSPUSH_SERVFAIL = 2, + DNSPUSH_NOTIMP = 4, + DNSPUSH_REFUSED = 5 +} DNSPUSH_ErrorCode; + +typedef enum { + DNSPUSH_INIT = 1, + DNSPUSH_NOSERVER = 2, + DNSPUSH_SERVERFOUND = 3, + DNSPUSH_ESTABLISHED = 4 +} DNSPush_State; + + + #define HMAC_LEN 64 #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5c @@ -1887,6 +1897,21 @@ typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, // RFC 4122 defines it to be 16 bytes #define UUID_SIZE 16 +#if TARGET_OS_EMBEDDED +typedef struct +{ + domainname * originalQName; // Name of original A/AAAA record if this question is for a CNAME record. + mDNSu32 querySendCount; // Number of queries that have been sent to DNS servers so far. + mDNSs32 firstQueryTime; // The time when the first query was sent to a DNS server. + mDNSBool answered; // Has this question been answered? + +} uDNSMetrics; + +extern mDNSu32 curr_num_regservices; // tracks the current number of services registered +extern mDNSu32 max_num_regservices; // tracks the max number of simultaneous services registered by the device + +#endif + struct DNSQuestion_struct { // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. @@ -1961,6 +1986,12 @@ struct DNSQuestion_struct // for TCP: there is some ambiguity in the use of this variable, but in general, it is // the number of TCP/TLS connection attempts for this LLQ state, or // the number of packets sent for this TCP/TLS connection + + // DNS Push Notification fields. These fields are only meaningful when LongLived flag is set + DNSPush_State dnsPushState; // The state of the DNS push notification negotiation + mDNSAddr dnsPushServerAddr; // Address of the system acting as the DNS Push Server + mDNSIPPort dnsPushServerPort; // Port on which the DNS Push Server is being advertised. + mDNSOpaque64 id; // DNS Proxy fields @@ -1968,7 +1999,7 @@ struct DNSQuestion_struct // till we populate in the cache mDNSBool DisallowPID; // Is the query allowed for the "PID" that we are sending on behalf of ? mDNSs32 ServiceID; // Service identifier to match against the DNS server - + // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() mDNSInterfaceID InterfaceID; // Non-zero if you want to issue queries only on a single specific IP interface mDNSu32 flags; // flags from original DNSService*() API request. @@ -1983,8 +2014,6 @@ struct DNSQuestion_struct mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire - mDNSBool DenyOnCellInterface; // Set by client to suppress uDNS queries on cellular interface - mDNSBool DenyOnExpInterface; // Set by client to suppress uDNS queries on expensive interface mDNSu8 RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords mDNSu8 TimeoutQuestion; // Timeout this question if there is no reply in configured time mDNSu8 WakeOnResolve; // Send wakeup on resolve @@ -1997,48 +2026,16 @@ struct DNSQuestion_struct mDNSu8 ProxyDNSSECOK; // Proxy Question with EDNS0 DNSSEC OK bit set mDNSs32 pid; // Process ID of the client that is requesting the question mDNSu8 uuid[UUID_SIZE]; // Unique ID of the client that is requesting the question (valid only if pid is zero) + mDNSu32 euid; // Effective User Id of the client that is requesting the question domainname *qnameOrig; // Copy of the original question name if it is not fully qualified mDNSQuestionCallback *QuestionCallback; void *QuestionContext; +#if TARGET_OS_EMBEDDED + uDNSMetrics metrics; // Data used for collecting unicast DNS query metrics. +#endif }; -typedef struct -{ - // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() - // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. - domainname name; - mDNSInterfaceID InterfaceID; // ID of the interface the response was received on - mDNSAddr ip; // Remote (destination) IP address where this service can be accessed - mDNSIPPort port; // Port where this service can be accessed - mDNSu16 TXTlen; - mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) -} ServiceInfo; - -// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() -typedef struct ServiceInfoQuery_struct ServiceInfoQuery; -typedef void mDNSServiceInfoQueryCallback (mDNS *const m, ServiceInfoQuery *query); -struct ServiceInfoQuery_struct -{ - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); - // all required data is passed as parameters to that function. - // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information - // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may - // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. - DNSQuestion qSRV; - DNSQuestion qTXT; - DNSQuestion qAv4; - DNSQuestion qAv6; - mDNSu8 GotSRV; - mDNSu8 GotTXT; - mDNSu8 GotADD; - mDNSu32 Answers; - ServiceInfo *info; - mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; - void *ServiceInfoQueryContext; -}; - -typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ } ZoneService; +typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ, ZoneServiceDNSPush } ZoneService; typedef void ZoneDataCallback (mDNS *const m, mStatus err, const ZoneData *result); @@ -2145,6 +2142,8 @@ struct NetworkInterfaceInfo_struct mDNSu8 SendGoodbyes; // Send goodbyes on this interface while sleeping mDNSBool DirectLink; // a direct link, indicating we can skip the probe for // address records + mDNSBool SupportsUnicastMDNSResponse; // Indicates that the interface supports unicast responses + // to Bonjour queries. Generally true for an interface. }; #define SLE_DELETE 0x00000001 @@ -2259,8 +2258,29 @@ typedef struct mDNSu32 CacheRefreshed; // Number of times the cache was refreshed due to a response mDNSu32 WakeOnResolves; // Number of times we did a wake on resolve } mDNSStatistics; + extern void LogMDNSStatistics(mDNS *const m); +typedef struct mDNS_DNSPushNotificationServer DNSPushNotificationServer; +typedef struct mDNS_DNSPushNotificationZone DNSPushNotificationZone; + +struct mDNS_DNSPushNotificationServer +{ + mDNSAddr serverAddr; // Server Address + tcpInfo_t *connection; // TCP Connection pointer + mDNSu32 numberOfQuestions; // Number of questions for this server + DNSPushNotificationServer *next; +} ; + +struct mDNS_DNSPushNotificationZone +{ + domainname zoneName; + DNSPushNotificationServer *servers; // DNS Push Notification Servers for this zone + mDNSu32 numberOfQuestions; // Number of questions for this zone + DNSPushNotificationZone *next; +} ; + + struct mDNS_struct { // Internal state fields. These hold the main internal state of mDNSCore; @@ -2269,6 +2289,7 @@ struct mDNS_struct // all required data is passed as parameters to that function. mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size + mDNSs32 NetworkChanged; mDNSBool CanReceiveUnicastOn5353; mDNSBool AdvertiseLocalAddresses; mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only @@ -2304,6 +2325,11 @@ struct mDNS_struct mDNSs32 NextScheduledNATOp; // Next time to send NAT-traversal packets mDNSs32 NextScheduledSPS; // Next time to purge expiring Sleep Proxy records mDNSs32 NextScheduledKA; // Next time to send Keepalive packets (SPS) +#if BONJOUR_ON_DEMAND + mDNSs32 NextBonjourDisableTime; // Next time to leave multicast group if Bonjour on Demand is enabled + mDNSu8 BonjourEnabled; // Non zero if Bonjour is currently enabled by the Bonjour on Demand logic +#endif // BONJOUR_ON_DEMAND + mDNSs32 DelayConflictProcessing; // To prevent spurious confilcts due to stale packets on the wire/air. mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire mDNSu32 RandomReconfirmDelay; // For de-synchronization of reconfirmation queries on the wire mDNSs32 PktNum; // Unique sequence number assigned to each received packet @@ -2332,6 +2358,7 @@ struct mDNS_struct mDNSs32 NextScheduledStopTime; // Next time to stop a question + mDNSs32 NextBLEServiceTime; // Next time to call the BLE discovery management layer. Non zero when active. // These fields only required for mDNS Searcher... DNSQuestion *Questions; // List of all registered questions, active and inactive @@ -2368,7 +2395,7 @@ struct mDNS_struct mDNSs32 ProbeFailTime; mDNSu32 NumFailedProbes; mDNSs32 SuppressProbes; - Platform_t mDNS_plat; + Platform_t mDNS_plat; // Why is this here in the “only required for mDNS Responder” section? -- SC // Unicast-specific data mDNSs32 NextuDNSEvent; // uDNS next event @@ -2424,6 +2451,10 @@ struct mDNS_struct mDNSu8 *UPnPRouterAddressString; // holds both the router's address and port mDNSu8 *UPnPSOAPAddressString; // holds both address and port for SOAP messages + // DNS Push Notification fields + DNSPushNotificationServer *DNSPushServers; // DNS Push Notification Servers + DNSPushNotificationZone *DNSPushZones; + // Sleep Proxy client fields AuthRecord *SPSRRSet; // To help the client keep track of the records registered with the sleep proxy @@ -2454,9 +2485,15 @@ struct mDNS_struct TrustAnchor *TrustAnchors; int notifyToken; - int uds_listener_skt; // Listening socket for incoming UDS clients - mDNSBool mDNSOppCaching; // Opportunistic Caching + int uds_listener_skt; // Listening socket for incoming UDS clients. This should not be here -- it's private to uds_daemon.c and nothing to do with mDNSCore -- SC mDNSu32 AutoTargetServices; // # of services that have AutoTarget set + +#if BONJOUR_ON_DEMAND + // Counters used in Bonjour on Demand logic. + mDNSu32 NumAllInterfaceRecords; // Right now we count *all* multicast records here. Later we may want to change to count interface-specific records separately. (This count includes records on the DuplicateRecords list too.) + mDNSu32 NumAllInterfaceQuestions; // Right now we count *all* multicast questions here. Later we may want to change to count interface-specific questions separately. +#endif // BONJOUR_ON_DEMAND + DNSSECStatistics DNSSECStats; mDNSStatistics mDNSStats; @@ -2468,8 +2505,8 @@ struct mDNS_struct }; #define FORALL_CACHERECORDS(SLOT,CG,CR) \ - for ((SLOT) = 0; (SLOT) < CACHE_HASH_SLOTS; (SLOT)++) \ - for ((CG)=m->rrcache_hash[(SLOT)]; (CG); (CG)=(CG)->next) \ + for ((SLOT) = 0; (SLOT) < CACHE_HASH_SLOTS; (SLOT)++) \ + for ((CG)=m->rrcache_hash[(SLOT)]; (CG); (CG)=(CG)->next) \ for ((CR) = (CG)->members; (CR); (CR)=(CR)->next) // *************************************************************************** @@ -2484,6 +2521,9 @@ extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value extern const mDNSInterfaceID mDNSInterfaceMark; // Special value extern const mDNSInterfaceID mDNSInterface_P2P; // Special value extern const mDNSInterfaceID uDNSInterfaceMark; // Special value +extern const mDNSInterfaceID mDNSInterface_BLE; // Special value + +#define LocalOnlyOrP2PInterface(INTERFACE) ((INTERFACE == mDNSInterface_LocalOnly) || (INTERFACE == mDNSInterface_P2P) || (INTERFACE == mDNSInterface_BLE)) extern const mDNSIPPort DiscardPort; extern const mDNSIPPort SSHPort; @@ -2525,11 +2565,16 @@ extern const mDNSOpaque16 DNSSecQFlags; extern const mDNSOpaque16 ResponseFlags; extern const mDNSOpaque16 UpdateReqFlags; extern const mDNSOpaque16 UpdateRespFlags; +extern const mDNSOpaque16 SubscribeFlags; +extern const mDNSOpaque16 UnSubscribeFlags; extern const mDNSOpaque64 zeroOpaque64; extern mDNSBool StrictUnicastOrdering; extern mDNSu8 NumUnicastDNSServers; +#if APPLE_OSX_mDNSResponder +extern mDNSu8 NumUnreachableDNSServers; +#endif #define localdomain (*(const domainname *)"\x5" "local") #define DeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp") @@ -2586,7 +2631,7 @@ mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) // Every client should call mDNS_Init, passing in storage for the mDNS object and the mDNS_PlatformSupport object. // // Clients that are only advertising services should use mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize. -// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, mDNS_StartResolveService, etc.) +// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, etc.) // need to provide storage for the resource record cache, or the query calls will return 'mStatus_NoCache'. // The rrcachestorage parameter is the address of memory for the resource record cache, and // the rrcachesize parameter is the number of entries in the CacheRecord array passed in. @@ -2648,7 +2693,6 @@ extern mStatus mDNS_Init (mDNS *const m, mDNS_PlatformSupport *const p, extern void mDNS_ConfigChanged(mDNS *const m); extern void mDNS_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numrecords); -extern void mDNS_GrowAuth (mDNS *const m, AuthEntity *storage, mDNSu32 numrecords); extern void mDNS_StartExit (mDNS *const m); extern void mDNS_FinalExit (mDNS *const m); #define mDNS_Close(m) do { mDNS_StartExit(m); mDNS_FinalExit(m); } while(0) @@ -2700,11 +2744,6 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_De // mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. // -// mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, -// to find the IP address, port number, and demultiplexing information for a given named service. -// As with mDNS_StartQuery, it executes asynchronously, and calls the ServiceInfoQueryCallback when the answer is -// found. After the service is resolved, the client should call mDNS_StopResolveService to complete the transaction. -// The client can also call mDNS_StopResolveService at any time to abort the transaction. // // mDNS_AddRecordToService adds an additional record to a Service Record Set. This record may be deregistered // via mDNS_RemoveRecordFromService, or by deregistering the service. mDNS_RemoveRecordFromService is passed a @@ -2734,6 +2773,7 @@ enum coreFlagWakeOnly = 0x8 // Service won't be registered with sleep proxy }; +extern mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType); extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, const domainlabel *const name, const domainname *const type, const domainname *const domain, const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen, @@ -2761,8 +2801,6 @@ extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopBrowse mDNS_StopQuery -extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); -extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); typedef enum { @@ -2784,7 +2822,7 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT #define mDNS_StopAdvertiseDomains mDNS_Deregister extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m); -extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself); +extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr); extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question); extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); @@ -2805,7 +2843,7 @@ extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); // because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size. // This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. #define AssignDomainName(DST, SRC) do { mDNSu16 len__ = DomainNameLength((SRC)); \ - if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__);else (DST)->c[0] = 0;} while(0) + if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__); else (DST)->c[0] = 0; } while(0) // Comparison functions #define SameDomainLabelCS(A,B) ((A)[0] == (B)[0] && mDNSPlatformMemSame((A)+1, (B)+1, (A)[0])) @@ -2887,7 +2925,7 @@ extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel // then the output will be truncated by one character to allow space for the terminating null. // Unlike standard C vsnprintf/snprintf, they return the number of characters *actually* written, // not the number of characters that *would* have been printed were buflen unlimited. -extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg); +extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) IS_A_PRINTF_STYLE_FUNCTION(3,0); extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); extern mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id); extern char *DNSTypeName(mDNSu16 rrtype); @@ -3076,14 +3114,14 @@ extern mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCache extern mStatus mDNSPlatformInit (mDNS *const m); extern void mDNSPlatformClose (mDNS *const m); extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, mDNSBool useBackgroundTrafficClass); -extern mDNSBool mDNSPlatformPeekUDP (mDNS *const m, UDPSocket *src); extern void mDNSPlatformLock (const mDNS *const m); extern void mDNSPlatformUnlock (const mDNS *const m); extern void mDNSPlatformStrCopy ( void *dst, const void *src); +extern mDNSu32 mDNSPlatformStrLCopy ( void *dst, const void *src, mDNSu32 len); extern mDNSu32 mDNSPlatformStrLen ( const void *src); extern void mDNSPlatformMemCopy ( void *dst, const void *src, mDNSu32 len); extern mDNSBool mDNSPlatformMemSame (const void *dst, const void *src, mDNSu32 len); @@ -3180,7 +3218,8 @@ extern void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNS extern mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti); extern mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr); extern mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname); -extern mStatus mDNSPlatformClearSPSMACAddr(void); +extern mStatus mDNSPlatformClearSPSData(void); +extern mStatus mDNSPlatformStoreOwnerOptRecord(char *ifname, DNSMessage *msg, int length); // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd extern mStatus mDNSPlatformTLSSetupCerts(void); @@ -3195,12 +3234,13 @@ extern mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, m extern void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status); extern void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason); +extern void mDNSPlatformPreventSleep(mDNS *const m, mDNSu32 timeout, const char *reason); extern void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration); extern mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID); extern mDNSBool mDNSPlatformInterfaceIsAWDL(const NetworkInterfaceInfo *intf); extern mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); -extern mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf); +extern mDNSBool mDNSPlatformValidRecordForInterface(const AuthRecord *rr, mDNSInterfaceID InterfaceID); extern mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf); extern void mDNSPlatformFormatTime(unsigned long t, mDNSu8 *buf, int bufsize); @@ -3256,7 +3296,7 @@ extern void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceI extern mStatus mDNS_RegisterInterface (mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping); extern void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping); extern void mDNSCoreInitComplete(mDNS *const m, mStatus result); -extern void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, +extern void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID); extern void mDNSCoreRestartQueries(mDNS *const m); @@ -3295,7 +3335,7 @@ extern void RetrySearchDomainQuestions(mDNS *const m); extern mDNSBool DomainEnumQuery(const domainname *qname); extern mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr); extern void UpdateKeepaliveRMACAsync(mDNS *const m, void *context); -extern void UpdateRMACCallback(mDNS *const m, void *context); +extern void UpdateRMAC(mDNS *const m, void *context); // Used only in logging to restrict the number of /etc/hosts entries printed extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result); @@ -3317,29 +3357,28 @@ extern void RemoveAutoTunnel6Record(mDNS *const m); extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); // For now this LocalSleepProxy stuff is specific to Mac OS X. // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer -extern mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf); +extern mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf, mDNSBool *keepaliveOnly); extern void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q); extern void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q); extern void mDNSPlatformLogToFile(int log_level, const char *buffer); extern mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf); +extern mStatus SymptomReporterDNSServerReachable(mDNS *const m, const mDNSAddr *addr); +extern mStatus SymptomReporterDNSServerUnreachable(DNSServer *s); #endif -typedef void ProxyCallback (mDNS *const m, void *socket, void *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, +typedef void ProxyCallback (mDNS *const m, void *socket, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); extern void mDNSPlatformInitDNSProxySkts(mDNS *const m, ProxyCallback *UDPCallback, ProxyCallback *TCPCallback); extern void mDNSPlatformCloseDNSProxySkts(mDNS *const m); extern void mDNSPlatformDisposeProxyContext(void *context); extern mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *start, mDNSu8 *limit); -// Sleep Assertions are specific to Mac OS X #if APPLE_OSX_mDNSResponder -extern void mDNSPlatformSleepAssertion(mDNS *const m, double timeout); +extern void mDNSPlatformGetDNSRoutePolicy(mDNS *const m, DNSQuestion *q, mDNSBool *isBlocked); #endif - -extern mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q); -extern mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q); -extern void mDNSPlatformSetuDNSSocktOpt(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q); +extern void mDNSPlatformSetSocktOpt(void *sock, mDNSTransport_Type transType, mDNSAddr_Type addrType, const DNSQuestion *q); extern mDNSs32 mDNSPlatformGetPID(void); +extern mDNSBool mDNSValidKeepAliveRecord(AuthRecord *rr); // *************************************************************************** #if 0 @@ -3547,6 +3586,11 @@ struct CompileTimeAssertionChecks_mDNS char assertK[(sizeof(UDPHeader ) == 8 ) ? 1 : -1]; char assertL[(sizeof(IKEHeader ) == 28 ) ? 1 : -1]; char assertM[(sizeof(TCPHeader ) == 20 ) ? 1 : -1]; + char assertN[(sizeof(rdataOPT) == 24 ) ? 1 : -1]; + char assertO[(sizeof(rdataRRSig) == 20 ) ? 1 : -1]; + char assertP[(sizeof(PCPMapRequest) == 60 ) ? 1 : -1]; + char assertQ[(sizeof(PCPMapReply) == 60 ) ? 1 : -1]; + // 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 @@ -3556,21 +3600,17 @@ struct CompileTimeAssertionChecks_mDNS char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1]; char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 232) ? 1 : -1]; char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 232) ? 1 : -1]; - char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 832) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 894) ? 1 : -1]; -// 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_ZoneData [(sizeof(ZoneData) <= 1648) ? 1 : -1]; + char sizecheck_ZoneData [(sizeof(ZoneData) <= 1730) ? 1 : -1]; char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 200) ? 1 : -1]; char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; - char sizecheck_DNSServer [(sizeof(DNSServer) <= 340) ? 1 : -1]; -// char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6988) ? 1 : -1]; + char sizecheck_DNSServer [(sizeof(DNSServer) <= 330) ? 1 : -1]; + char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 7272) ? 1 : -1]; char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5540) ? 1 : -1]; char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1]; -// char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3302) ? 1 : -1]; #if APPLE_OSX_mDNSResponder -// char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1160) ? 1 : -1]; + char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1230) ? 1 : -1]; #endif }; @@ -3580,6 +3620,13 @@ mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr); #if APPLE_OSX_mDNSResponder extern void D2D_start_advertising_interface(NetworkInterfaceInfo *interface); extern void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface); +extern void D2D_start_advertising_record(AuthRecord *ar); +extern void D2D_stop_advertising_record(AuthRecord *ar); +#else +#define D2D_start_advertising_interface(X) +#define D2D_stop_advertising_interface(X) +#define D2D_start_advertising_record(X) +#define D2D_stop_advertising_record(X) #endif // *************************************************************************** diff --git a/mDNSCore/nsec.c b/mDNSCore/nsec.c index a9f16b3..09d2735 100644 --- a/mDNSCore/nsec.c +++ b/mDNSCore/nsec.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -611,7 +611,7 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname { const domainname *oname = rr->name; // owner name - if (wildcard) *wildcard = mDNSNULL; + *wildcard = mDNSNULL; // RFC 4035 // // section 3.1.3.1 : Name matches. Prove that the type does not exist and also CNAME is @@ -676,10 +676,10 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname // a subdomain e.g., y.x.example or z.y.x.example and so on. if (oname->c[0] == 1 && oname->c[1] == '*') { - int r, s; + int s; const domainname *ce = SkipLeadingLabels(oname, 1); - r = DNSSECCanonicalOrder(name, ce, &s); + DNSSECCanonicalOrder(name, ce, &s); if (s) { if (RRAssertsExistence(rr, qtype) || RRAssertsExistence(rr, kDNSType_CNAME)) @@ -912,18 +912,18 @@ mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) // First verify wildcard NSEC and then when we are done, we // will verify the noname nsec dv->pendingNSEC = r; - LogDNSSEC("NoDataProof: Verifying wild and noname %s", RRDisplayString(m, nsec_wild)); + LogDNSSEC("NoDataProof: Verifying wild and noname %s", nsec_wild ? RRDisplayString(m, nsec_wild) : "NULL"); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback); } else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) || (dv->flags & NSEC_PROVES_NOTYPE_EXISTS)) { - LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild)); + LogDNSSEC("NoDataProof: Verifying wild %s", nsec_wild ? RRDisplayString(m, nsec_wild) : "NULL"); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL); } else if (dv->flags & NSEC_PROVES_NONAME_EXISTS) { - LogDNSSEC("NoDataProof: Verifying noname %s", RRDisplayString(m, nsec_noname)); + LogDNSSEC("NoDataProof: Verifying noname %s", nsec_noname ? RRDisplayString(m, nsec_noname) : "NULL"); VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL); } return; diff --git a/mDNSCore/nsec.h b/mDNSCore/nsec.h index 3dbb841..198f57d 100644 --- a/mDNSCore/nsec.h +++ b/mDNSCore/nsec.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-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. @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef __NSEC_H #define __NSEC_H diff --git a/mDNSCore/nsec3.c b/mDNSCore/nsec3.c index a039418..4e9e8c8 100644 --- a/mDNSCore/nsec3.c +++ b/mDNSCore/nsec3.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -238,7 +238,7 @@ mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *nc name = SkipLeadingLabels(origName, i); if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen)) { - LogMsg("NSEC3Find: NSEC3HashName failed for ##s", name->c); + LogMsg("NSEC3Find: NSEC3HashName failed for %##s", name->c); continue; } @@ -332,7 +332,7 @@ mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *nc } } - if ((val == NSEC3Covers || val == NSEC3CEProof) && !(*closerEncloser)) + if ((val == NSEC3Covers || val == NSEC3CEProof) && (!closerEncloser || !(*closerEncloser))) { if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len)) { @@ -349,23 +349,22 @@ mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *nc // 2.3) If there is a matching NSEC3 RR in the response and the flag // was set, then the proof is complete, and SNAME is the closest // encloser. - if (val == NSEC3CEProof) + if (val == NSEC3CEProof && closestEncloser && *closestEncloser) { - if (*closestEncloser && *closerEncloser) + if (closerEncloser && *closerEncloser) { LogDNSSEC("NSEC3Find: Found closest and closer encloser"); return mDNStrue; } - - // 2.4) If there is a matching NSEC3 RR in the response, but the flag - // is not set, then the response is bogus. - // - // Note: We don't have to wait till we finish trying all the names. If the matchName - // happens, we found the closest encloser which means we should have found the closer - // encloser before. - - if (*closestEncloser && !(*closerEncloser)) + else { + // 2.4) If there is a matching NSEC3 RR in the response, but the flag + // is not set, then the response is bogus. + // + // Note: We don't have to wait till we finish trying all the names. If the matchName + // happens, we found the closest encloser which means we should have found the closer + // encloser before. + LogDNSSEC("NSEC3Find: Found closest, but not closer encloser"); return mDNSfalse; } @@ -388,8 +387,7 @@ mDNSlocal mDNSBool NSEC3ClosestEncloserProof(mDNS *const m, CacheRecord *ncr, do // Note: It is possible that closestEncloser and closerEncloser are the same. if (!closestEncloser || !closerEncloser || !ce) { - LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", *closestEncloser, - *closerEncloser, *ce); + LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", closestEncloser, closerEncloser, ce); return mDNSfalse; } @@ -710,7 +708,7 @@ mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen)) { - LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name->c); + LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for %##s", name->c); return mDNSNULL; } diff --git a/mDNSCore/nsec3.h b/mDNSCore/nsec3.h index ce3b85a..be32b80 100644 --- a/mDNSCore/nsec3.h +++ b/mDNSCore/nsec3.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-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. diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index ce12d01..3677b9f 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -51,6 +51,9 @@ mDNSBool StrictUnicastOrdering = mDNSfalse; // arbitrary limitation of 64 DNSServers can be removed. mDNSu8 NumUnicastDNSServers = 0; #define MAX_UNICAST_DNS_SERVERS 64 +#if APPLE_OSX_mDNSResponder +mDNSu8 NumUnreachableDNSServers = 0; +#endif #define SetNextuDNSEvent(m, rr) { \ if ((m)->NextuDNSEvent - ((rr)->LastAPTime + (rr)->ThisAPInterval) >= 0) \ @@ -121,7 +124,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons return mDNSNULL; } - if (!d) + if (!d) d = (const domainname *)""; LogInfo("mDNS_AddDNSServer(%d): Adding %#a for %##s, InterfaceID %p, serviceID %u, scoped %d, resGroupID %d req_A is %s req_AAAA is %s cell %s req_DO is %s", @@ -132,11 +135,11 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons while (*p) // Check if we already have this {interface,address,port,domain} tuple registered + reqA/reqAAAA bits { - if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->serviceID == serviceID && (*p)->teststate != DNSServer_Disabled && - mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d) && + if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->serviceID == serviceID && + mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d) && (*p)->req_A == reqA && (*p)->req_AAAA == reqAAAA) { - if (!((*p)->flags & DNSServer_FlagDelete)) + if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); tmp = *p; *p = tmp->next; @@ -164,6 +167,12 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons if (tmp) { +#if APPLE_OSX_mDNSResponder + if (tmp->flags & DNSServer_FlagDelete) + { + tmp->flags &= ~DNSServer_FlagUnreachable; + } +#endif tmp->flags &= ~DNSServer_FlagDelete; *p = tmp; // move to end of list, to ensure ordering from platform layer } @@ -183,8 +192,6 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (*p)->addr = *addr; (*p)->port = port; (*p)->flags = DNSServer_FlagNew; - (*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed; - (*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL; (*p)->timeout = timeout; (*p)->cellIntf = cellIntf; (*p)->req_A = reqA; @@ -199,11 +206,13 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (*p)->next = mDNSNULL; } } - (*p)->penaltyTime = 0; - // We always update the ID (not just when we allocate a new instance) because we could - // be adding a new non-scoped resolver with a new ID and we want all the non-scoped - // resolvers belong to the same group. - (*p)->resGroupID = resGroupID; + if (*p) { + (*p)->penaltyTime = 0; + // We always update the ID (not just when we allocate a new instance) because we could + // be adding a new non-scoped resolver with a new ID and we want all the non-scoped + // resolvers belong to the same group. + (*p)->resGroupID = resGroupID; + } return(*p); } @@ -214,6 +223,7 @@ mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 re { DNSServer *new; DNSServer *orig = q->qDNSServer; + mDNSu8 rcode = '\0'; mDNS_CheckLock(m); @@ -225,9 +235,12 @@ mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 re if (mDNSOpaque16IsZero(q->responseFlags)) q->responseFlags = responseFlags; + rcode = (mDNSu8)(responseFlags.b[1] & kDNSFlag1_RC_Mask); + // After we reset the qDNSServer to NULL, we could get more SERV_FAILS that might end up - // peanlizing again. - if (!q->qDNSServer) goto end; + // penalizing again. + if (!q->qDNSServer) + goto end; // If strict ordering of unicast servers needs to be preserved, we just lookup // the next best match server below @@ -246,6 +259,10 @@ mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 re { LogInfo("PenalizeDNSServer: Not Penalizing PTR question"); } + else if ((rcode == kDNSFlag1_RC_FormErr) || (rcode == kDNSFlag1_RC_ServFail) || (rcode == kDNSFlag1_RC_NotImpl) || (rcode == kDNSFlag1_RC_Refused)) + { + LogInfo("PenalizeDNSServer: Not Penalizing DNS Server since it at least responded with rcode %d", rcode); + } else { LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype); @@ -465,7 +482,7 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) { mStatus err = mStatus_NoError; - + if (!m->NATTraversals) { m->retryGetAddr = NonZeroTime(m->timenow + 0x78000000); @@ -480,7 +497,7 @@ mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) mDNSu8* end = start + sizeof(NATAddrRequest); err = mDNSPlatformSendUDP(m, start, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); debugf("uDNS_RequestAddress: Sent NAT-PMP external address request %d", err); - + #ifdef _LEGACY_NAT_TRAVERSAL_ if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) { @@ -492,7 +509,7 @@ mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) mStatus lnterr = LNT_GetExternalAddress(m); if (lnterr) LogMsg("uDNS_RequestAddress: LNT_GetExternalAddress returned error %d", lnterr); - + err = err ? err : lnterr; // NAT-PMP error takes precedence } #endif // _LEGACY_NAT_TRAVERSAL_ @@ -537,7 +554,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP LogMsg("uDNS_SendNATMsg called unexpectedly with NULL info"); return mStatus_BadParamErr; } - + // send msg if the router's address is private (which means it's non-zero) if (mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) { @@ -550,7 +567,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP static NATPortMapRequest NATPortReq; static const mDNSu8* end = (mDNSu8 *)&NATPortReq + sizeof(NATPortMapRequest); mDNSu8 *p = (mDNSu8 *)&NATPortReq.NATReq_lease; - + NATPortReq.vers = NATMAP_VERS; NATPortReq.opcode = info->Protocol; NATPortReq.unused = zeroID; @@ -560,7 +577,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); p[3] = (mDNSu8)( info->NATLease & 0xFF); - + err = mDNSPlatformSendUDP(m, (mDNSu8 *)&NATPortReq, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); debugf("uDNS_SendNATMsg: Sent NAT-PMP mapping request %d", err); } @@ -587,31 +604,31 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP mDNSu8* start = (mDNSu8*)&req; mDNSu8* end = start + sizeof(req); mDNSu8* p = (mDNSu8*)&req.lifetime; - + req.version = PCP_VERS; req.opCode = PCPOp_Map; req.reserved = zeroID; - + p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF); p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); p[3] = (mDNSu8)( info->NATLease & 0xFF); - + mDNSAddrMapIPv4toIPv6(&m->AdvertisedV4.ip.v4, &req.clientAddr); - + req.nonce[0] = m->PCPNonce[0]; req.nonce[1] = m->PCPNonce[1]; req.nonce[2] = m->PCPNonce[2]; - + req.protocol = (info->Protocol == NATOp_MapUDP ? PCPProto_UDP : PCPProto_TCP); - + req.reservedMapOp[0] = 0; req.reservedMapOp[1] = 0; req.reservedMapOp[2] = 0; - + req.intPort = info->Protocol ? info->IntPort : DiscardPort; req.extPort = info->RequestedPort; - + // Since we only support IPv4, even if using the all-zeros address, map it, so // the PCP gateway will give us an IPv4 address & not an IPv6 address. mDNSAddrMapIPv4toIPv6(&info->NewAddress, &req.extAddress); @@ -637,7 +654,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP mStatus lnterr = LNT_MapPort(m, info); if (lnterr) LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr); - + err = err ? err : lnterr; // PCP error takes precedence } #endif // _LEGACY_NAT_TRAVERSAL_ @@ -710,7 +727,7 @@ mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv m->NextScheduledNATOp = m->retryGetAddr; last_err = err; - + for (n = m->NATTraversals; n; n=n->next) { // We should change n->NewAddress only when n is one of: @@ -812,11 +829,8 @@ mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalIn { if (traversal == *n) { - LogMsg("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d", + LogFatalError("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d", traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease); - #if ForceAlerts - *(long*)0 = 0; - #endif return(mStatus_AlreadyRegistered); } if (traversal->Protocol && traversal->Protocol == (*n)->Protocol && mDNSSameIPPort(traversal->IntPort, (*n)->IntPort) && @@ -910,7 +924,7 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra { traversal->NATLease = 0; traversal->retryInterval = 0; - + // In case we most recently sent NAT-PMP, we need to set sentNATPMP to false so // that we'll send a NAT-PMP request to destroy the mapping. We do this because // the NATTraversal struct has already been cut from the list, and the client @@ -926,7 +940,7 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra // would have requested an IPv4 address. traversal->RequestedPort = zeroIPPort; traversal->NewAddress = zerov4Addr; - + uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP); } @@ -1559,6 +1573,7 @@ mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, con } if (!info->sock) { LogMsg("MakeTCPConn: unable to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); } + mDNSPlatformSetSocktOpt(info->sock, mDNSTransport_TCP, Addr->type, question); err = mDNSPlatformTCPConnect(info->sock, Addr, Port, hostname, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info); // Probably suboptimal here. @@ -1689,6 +1704,7 @@ mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) } } + // forward declaration so GetServiceTarget can do reverse lookup if needed mDNSlocal void GetStaticHostname(mDNS *m); @@ -1740,11 +1756,13 @@ mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE = (const domainname*)"\x mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x0F_dns-update-tls" "\x04_tcp"; mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE = (const domainname*)"\x0E_dns-query-tls" "\x04_tcp"; mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE = (const domainname*)"\x0C_dns-llq-tls" "\x04_tcp"; +mDNSlocal const domainname *DNS_PUSH_NOTIFICATION_SERVICE_TYPE = (const domainname*)"\x0C_dns-push-tls" "\x04_tcp"; #define ZoneDataSRV(X) ( \ - (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ - (X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \ - (X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : (const domainname*)"") + (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ + (X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \ + (X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : \ + (X)->ZoneService == ZoneServiceDNSPush ? DNS_PUSH_NOTIFICATION_SERVICE_TYPE : (const domainname*)"") // Forward reference: GetZoneData_StartQuery references GetZoneData_QuestionCallback, and // GetZoneData_QuestionCallback calls GetZoneData_StartQuery @@ -1872,7 +1890,7 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.InterfaceID = mDNSInterface_Any; zd->question.flags = 0; zd->question.Target = zeroAddr; - //zd->question.qname.c[0] = 0; // Already set + //zd->question.qname.c[0] = 0; // Already set zd->question.qtype = qtype; zd->question.qclass = kDNSClass_IN; zd->question.LongLived = mDNSfalse; @@ -1880,8 +1898,6 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.ForceMCast = mDNSfalse; zd->question.ReturnIntermed = mDNStrue; zd->question.SuppressUnusable = mDNSfalse; - zd->question.DenyOnCellInterface = mDNSfalse; - zd->question.DenyOnExpInterface = mDNSfalse; zd->question.SearchListIndex = 0; zd->question.AppendSearchDomains = 0; zd->question.RetryWithSearchDomains = mDNSfalse; @@ -1894,6 +1910,7 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.qnameOrig = mDNSNULL; zd->question.AnonInfo = mDNSNULL; zd->question.pid = mDNSPlatformGetPID(); + zd->question.euid = 0; zd->question.QuestionCallback = GetZoneData_QuestionCallback; zd->question.QuestionContext = zd; @@ -2156,7 +2173,7 @@ mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr) else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; } //LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s", - // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); + // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo); rr->NATinfo.Protocol = protocol; @@ -2569,8 +2586,6 @@ mDNSlocal void GetStaticHostname(mDNS *m) q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNStrue; q->SuppressUnusable = mDNSfalse; - q->DenyOnCellInterface = mDNSfalse; - q->DenyOnExpInterface = mDNSfalse; q->SearchListIndex = 0; q->AppendSearchDomains = 0; q->RetryWithSearchDomains = mDNSfalse; @@ -2583,6 +2598,7 @@ mDNSlocal void GetStaticHostname(mDNS *m) q->qnameOrig = mDNSNULL; q->AnonInfo = mDNSNULL; q->pid = mDNSPlatformGetPID(); + q->euid = 0; q->QuestionCallback = FoundStaticHostname; q->QuestionContext = mDNSNULL; @@ -2629,12 +2645,30 @@ mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) // below could free the memory, and we have to make sure we don't touch hi fields after that. mDNSBool f4 = hi->arv4.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv4.state != regState_Unregistered; mDNSBool f6 = hi->arv6.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv6.state != regState_Unregistered; - if (f4) LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn); - if (f6) LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn); *ptr = (*ptr)->next; // unlink - if (f4) mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal); - if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal); - // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback + if (f4 || f6) + { + if (f4) + { + LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn); + mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal); + } + if (f6) + { + LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn); + mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal); + } + // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback + } + else + { + if (hi->natinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &hi->natinfo); + hi->natinfo.clientContext = mDNSNULL; + } + mDNSPlatformMemFree(hi); + } } mDNS_CheckLock(m); m->NextSRVUpdate = NonZeroTime(m->timenow); @@ -3605,13 +3639,13 @@ mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interface // Minimum NAT-PMP packet is vers (1) opcode (1) + err (2) = 4 bytes if (len < 4) { LogMsg("NAT-PMP message too short (%d bytes)", len); return; } - + // Read multi-byte error value (field is identical in a NATPortMapReply) AddrReply->err = (mDNSu16) ((mDNSu16)pkt[2] << 8 | pkt[3]); - + if (AddrReply->err == NATErr_Vers) { - NATTraversalInfo *n; + NATTraversalInfo *n; LogInfo("NAT-PMP version unsupported message received"); for (n = m->NATTraversals; n; n=n->next) { @@ -3619,7 +3653,7 @@ mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interface // and update the state variables uDNS_SendNATMsg(m, n, mDNSfalse); } - + m->NextScheduledNATOp = m->timenow; return; @@ -3633,7 +3667,7 @@ mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interface LogMsg("NAT-PMP message too short (%d bytes) 0x%X 0x%X", len, AddrReply->opcode, AddrReply->err); return; } - + // Read multi-byte upseconds value (field is identical in a NATPortMapReply) AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]); @@ -3710,16 +3744,16 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 protocol = 0; mDNSIPPort intport = zeroIPPort; mDNSIPPort extport = zeroIPPort; - + // Minimum PCP packet is 24 bytes if (len < 24) { LogMsg("uDNS_ReceivePCPPacket: message too short (%d bytes)", len); return; } - + strippedOpCode = reply->opCode & 0x7f; - + if ((reply->opCode & 0x80) == 0x00 || (strippedOpCode != PCPOp_Announce && strippedOpCode != PCPOp_Map)) { LogMsg("uDNS_ReceivePCPPacket: unhandled opCode %u", reply->opCode); @@ -3758,11 +3792,11 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, if (strippedOpCode == PCPOp_Announce) return; - + // We globally keep track of the most recent error code for mappings. // This seems bad to do with PCP, but best not change it now. m->LastNATMapResultCode = reply->result; - + if (!reply->result) { if (len < sizeof(PCPMapReply)) @@ -3770,7 +3804,7 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, LogMsg("uDNS_ReceivePCPPacket: mapping response too short (%d bytes)", len); return; } - + // Check the nonce if (reply->nonce[0] != m->PCPNonce[0] || reply->nonce[1] != m->PCPNonce[1] || reply->nonce[2] != m->PCPNonce[2]) { @@ -3809,7 +3843,7 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, { LogInfo("uDNS_ReceivePCPPacket: error received from server. opcode %X result %X lifetime %X epoch %X", reply->opCode, reply->result, reply->lifetime, reply->epoch); - + // If the packet is long enough, get the protocol & intport for matching to report // the error if (len >= sizeof(PCPMapReply)) @@ -3842,135 +3876,6 @@ mDNSexport void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID LogMsg("uDNS_ReceiveNATPacket: packet with version %u (expected %u or %u)", pkt[0], PCP_VERS, NATMAP_VERS); } -// Shorten DNS-SD queries to avoid NAT bugs -// Add check to avoid crashing NAT gateways that have buggy DNS relay code -// -// We know of bugs in home NAT gateways that cause them to crash if they receive certain DNS queries. -// The DNS queries that make them crash are perfectly legal DNS queries, but even if they weren't, -// the gateway shouldn't crash -- in today's world of viruses and network attacks, software has to -// be written assuming that a malicious attacker could send them any packet, properly-formed or not. -// Still, we don't want to be crashing people's home gateways, so we go out of our way to avoid -// the queries that crash them. -// -// Some examples: -// -// 1. Any query where the name ends in ".in-addr.arpa." and the text before this is 32 or more bytes. -// The query type does not need to be PTR -- the gateway will crash for any query type. -// e.g. "ping long-name-crashes-the-buggy-router.in-addr.arpa" will crash one of these. -// -// 2. Any query that results in a large response with the TC bit set. -// -// 3. Any PTR query that doesn't begin with four decimal numbers. -// These gateways appear to assume that the only possible PTR query is a reverse-mapping query -// (e.g. "1.0.168.192.in-addr.arpa") and if they ever get a PTR query where the first four -// labels are not all decimal numbers in the range 0-255, they handle that by crashing. -// These gateways also ignore the remainder of the name following the four decimal numbers -// -- whether or not it actually says in-addr.arpa, they just make up an answer anyway. -// -// The challenge therefore is to craft a query that will discern whether the DNS server -// is one of these buggy ones, without crashing it. Furthermore we don't want our test -// queries making it all the way to the root name servers, putting extra load on those -// name servers and giving Apple a bad reputation. To this end we send this query: -// dig -t ptr 1.0.0.127.dnsbugtest.1.0.0.127.in-addr.arpa. -// -// The text preceding the ".in-addr.arpa." is under 32 bytes, so it won't cause crash (1). -// It will not yield a large response with the TC bit set, so it won't cause crash (2). -// It starts with four decimal numbers, so it won't cause crash (3). -// The name falls within the "1.0.0.127.in-addr.arpa." domain, the reverse-mapping name for the local -// loopback address, and therefore the query will black-hole at the first properly-configured DNS server -// it reaches, making it highly unlikely that this query will make it all the way to the root. -// -// Finally, the correct response to this query is NXDOMAIN or a similar error, but the -// gateways that ignore the remainder of the name following the four decimal numbers -// give themselves away by actually returning a result for this nonsense query. - -mDNSlocal const domainname *DNSRelayTestQuestion = (const domainname*) - "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest" - "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa"; - -// See comments above for DNSRelayTestQuestion -// If this is the kind of query that has the risk of crashing buggy DNS servers, we do a test question first -mDNSlocal mDNSBool NoTestQuery(DNSQuestion *q) -{ - int i; - mDNSu8 *p = q->qname.c; - if (q->AuthInfo) return(mDNStrue); // Don't need a test query for private queries sent directly to authoritative server over TLS/TCP - if (q->qtype != kDNSType_PTR) return(mDNStrue); // Don't need a test query for any non-PTR queries - for (i=0; i<4; i++) // If qname does not begin with num.num.num.num, can't skip the test query - { - if (p[0] < 1 || p[0] > 3) return(mDNSfalse); - if ( p[1] < '0' || p[1] > '9' ) return(mDNSfalse); - if (p[0] >= 2 && (p[2] < '0' || p[2] > '9')) return(mDNSfalse); - if (p[0] >= 3 && (p[3] < '0' || p[3] > '9')) return(mDNSfalse); - p += 1 + p[0]; - } - // If remainder of qname is ".in-addr.arpa.", this is a vanilla reverse-mapping query and - // we can safely do it without needing a test query first, otherwise we need the test query. - return(SameDomainName((domainname*)p, (const domainname*)"\x7" "in-addr" "\x4" "arpa")); -} - -// Returns mDNStrue if response was handled -mDNSlocal mDNSBool uDNS_ReceiveTestQuestionResponse(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport) -{ - const mDNSu8 *ptr = msg->data; - DNSQuestion pktq; - DNSServer *s; - mDNSu32 result = 0; - - // 1. Find out if this is an answer to one of our test questions - if (msg->h.numQuestions != 1) return(mDNSfalse); - ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &pktq); - if (!ptr) return(mDNSfalse); - if (pktq.qtype != kDNSType_PTR || pktq.qclass != kDNSClass_IN) return(mDNSfalse); - if (!SameDomainName(&pktq.qname, DNSRelayTestQuestion)) return(mDNSfalse); - - // 2. If the DNS relay gave us a positive response, then it's got buggy firmware - // else, if the DNS relay gave us an error or no-answer response, it passed our test - if ((msg->h.flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0) - result = DNSServer_Failed; - else - result = DNSServer_Passed; - - // 3. Find occurrences of this server in our list, and mark them appropriately - for (s = m->DNSServers; s; s = s->next) - { - mDNSBool matchaddr = (s->teststate != result && mDNSSameAddress(srcaddr, &s->addr) && mDNSSameIPPort(srcport, s->port)); - mDNSBool matchid = (s->teststate == DNSServer_Untested && mDNSSameOpaque16(msg->h.id, s->testid)); - if (matchaddr || matchid) - { - DNSQuestion *q; - s->teststate = result; - if (result == DNSServer_Passed) - { - LogInfo("DNS Server %#a:%d (%#a:%d) %d passed%s", - &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), - matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); - } - else - { - LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a:%d (%#a:%d) %d%s", - &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), - matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); - } - - // If this server has just changed state from DNSServer_Untested to DNSServer_Passed, then retrigger any waiting questions. - // We use the NoTestQuery() test so that we only retrigger questions that were actually blocked waiting for this test to complete. - if (result == DNSServer_Passed) // Unblock any questions that were waiting for this result - for (q = m->Questions; q; q=q->next) - if (q->qDNSServer == s && !NoTestQuery(q)) - { - q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; - q->unansweredQueries = 0; - q->LastQTime = m->timenow - q->ThisQInterval; - m->NextScheduledQuery = m->timenow; - } - } - } - - return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doesn't need to process this packet further -} - // Called from mDNSCoreReceive with the lock held mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport) { @@ -3991,11 +3896,14 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", msg->h.numAdditionals, msg->h.numAdditionals == 1 ? "" : "s", end - msg->data); +#if APPLE_OSX_mDNSResponder + if (NumUnreachableDNSServers > 0) + SymptomReporterDNSServerReachable(m, srcaddr); +#endif if (QR_OP == StdR) { //if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return; - if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return; for (qptr = m->Questions; qptr; qptr = qptr->next) if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW) { @@ -4017,7 +3925,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond; mDNSu32 random = mDNSRandom((mDNSs32)lease * mDNSPlatformOneSecond/10); - //rcode = kDNSFlag1_RC_ServFail; // Simulate server failure (rcode 2) + //rcode = kDNSFlag1_RC_ServFail; // Simulate server failure (rcode 2) // Walk through all the records that matches the messageID. There could be multiple // records if we had sent them in a group @@ -4141,7 +4049,7 @@ mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneI q->servAddr = zeroAddr; q->servPort = zeroIPPort; - if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) + if (!err && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) { q->servAddr = zoneInfo->Addr; q->servPort = zoneInfo->Port; @@ -4180,6 +4088,40 @@ mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneI mDNS_Unlock(m); } +mDNSexport void DNSPushNotificationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) +{ + DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext; + mDNS_Lock(m); + + // If we get here it means that the GetZoneData operation has completed. + // We hold on to the zone data if it is AutoTunnel as we use the hostname + // in zoneInfo during the TLS connection setup. + q->servAddr = zeroAddr; + q->servPort = zeroIPPort; + if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) + { + q->dnsPushState = DNSPUSH_SERVERFOUND; + q->dnsPushServerAddr = zoneInfo->Addr; + q->dnsPushServerPort = zoneInfo->Port; + q->ntries = 0; + LogInfo("DNSPushNotificationGotZoneData %#a:%d", &q->dnsPushServerAddr, mDNSVal16(q->dnsPushServerPort)); + SubscribeToDNSPushNotificationServer(m,q); + } + else + { + q->dnsPushState = DNSPUSH_NOSERVER; + StartLLQPolling(m,q); + if (err == mStatus_NoSuchNameErr) + { + // this actually failed, so mark it by setting address to all ones + q->servAddr.type = mDNSAddrType_IPv4; + q->servAddr.ip.v4 = onesIPv4Addr; + } + } + mDNS_Unlock(m); +} + + // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) { @@ -4238,10 +4180,14 @@ mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneDat // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData) { - AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext; + AuthRecord *newRR; AuthRecord *ptr; int c1, c2; + if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; } + + newRR = (AuthRecord*)zoneData->ZoneDataContext; + if (newRR->nta != zoneData) LogMsg("RecordRegistrationGotZoneData: nta (%p) != zoneData (%p) %##s (%s)", newRR->nta, zoneData, newRR->resrec.name->c, DNSTypeName(newRR->resrec.rrtype)); @@ -4267,8 +4213,6 @@ mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const return; } - if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; } - if (newRR->resrec.rrclass != zoneData->ZoneClass) { LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", newRR->resrec.rrclass, zoneData->ZoneClass); @@ -4445,7 +4389,7 @@ mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) if (!rr->nta) { LogMsg("SendRecordDeregistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %d", err); - //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this + //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this } SetRecordRetry(m, rr, 0); return; @@ -4656,7 +4600,7 @@ mDNSlocal void handle_unanswered_query(mDNS *const m) // Note: req_DO affects only DNSSEC_VALIDATION_SECURE_OPTIONAL questions; // DNSSEC_VALIDATION_SECURE questions ignores req_DO. - if (q->qDNSServer && !q->qDNSServer->DNSSECAware && q->qDNSServer->req_DO) + if (!q->qDNSServer->DNSSECAware && q->qDNSServer->req_DO) { q->qDNSServer->retransDO++; if (q->qDNSServer->retransDO == MAX_DNSSEC_RETRANSMISSIONS) @@ -4668,17 +4612,35 @@ mDNSlocal void handle_unanswered_query(mDNS *const m) if (!q->qDNSServer->req_DO) { - q->ValidationState = DNSSECValNotRequired; + q->ValidationState = DNSSECValNotRequired; q->ValidationRequired = DNSSEC_VALIDATION_NONE; - + if (q->ProxyQuestion) q->ProxyDNSSECOK = mDNSfalse; - LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a", + LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr); } } } +mDNSlocal void uDNS_HandleLLQState(mDNS *const m, DNSQuestion *q) +{ + // First attempt to use DNS Push Notification. + if (q->dnsPushState == DNSPUSH_INIT) + DiscoverDNSPushNotificationServer(m, q); + switch (q->state) + { + case LLQ_InitialRequest: startLLQHandshake(m, q); break; + case LLQ_SecondaryRequest: + // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step + if (PrivateQuery(q)) startLLQHandshake(m, q); + else sendChallengeResponse(m, q, mDNSNULL); + break; + case LLQ_Established: sendLLQRefresh(m, q); break; + case LLQ_Poll: break; // Do nothing (handled below) + } +} + // The question to be checked is not passed in as an explicit parameter; // instead it is implicit that the question to be checked is m->CurrentQuestion. mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) @@ -4688,22 +4650,10 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) if (q->LongLived) { - switch (q->state) - { - case LLQ_InitialRequest: startLLQHandshake(m, q); break; - case LLQ_SecondaryRequest: - // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step - if (PrivateQuery(q)) - startLLQHandshake(m, q); - else - sendChallengeResponse(m, q, mDNSNULL); - break; - case LLQ_Established: sendLLQRefresh(m, q); break; - case LLQ_Poll: break; // Do nothing (handled below) - } + uDNS_HandleLLQState(m,q); } - handle_unanswered_query(m); + handle_unanswered_query(m); // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll if (!(q->LongLived && q->state != LLQ_Poll)) { @@ -4714,6 +4664,9 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c); +#if APPLE_OSX_mDNSResponder + SymptomReporterDNSServerUnreachable(orig); +#endif PenalizeDNSServer(m, q, zeroID); q->noServerResponse = 1; } @@ -4748,36 +4701,25 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) for (qptr = q->next ; qptr; qptr = qptr->next) if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } } - if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled) + if (q->qDNSServer) { - mDNSu8 *end = m->omsg.data; + mDNSu8 *end; mStatus err = mStatus_NoError; mDNSBool private = mDNSfalse; InitializeDNSMessage(&m->omsg.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); - if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q)) - { - end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) - { - if (q->ProxyQuestion) - end = DNSProxySetAttributes(q, &m->omsg.h, &m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); - else - end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); - } - private = PrivateQuery(q); - } - else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL) // Make sure at least three seconds has elapsed since last test query + end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) { - LogInfo("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port)); - q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; - q->qDNSServer->lasttest = m->timenow; - end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN); - q->qDNSServer->testid = m->omsg.h.id; + if (q->ProxyQuestion) + end = DNSProxySetAttributes(q, &m->omsg.h, &m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); + else + end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); } + private = PrivateQuery(q); - if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q))) + if (end > m->omsg.data) { //LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype)); if (private) @@ -4795,39 +4737,95 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); if (q->LocalSocket) - mDNSPlatformSetuDNSSocktOpt(q->LocalSocket, &q->qDNSServer->addr, q); + { + mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, mDNSAddrType_IPv4, q); + mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, mDNSAddrType_IPv6, q); + } } if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time - else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); + else + { + err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); +#if TARGET_OS_EMBEDDED + if (!err) + { + if (q->metrics.answered) + { + q->metrics.querySendCount = 0; + q->metrics.answered = mDNSfalse; + } + if (q->metrics.querySendCount++ == 0) + { + q->metrics.firstQueryTime = m->timenow; + } + } +#endif + } } } - if (err != mStatus_TransientErr) // if it is not a transient error backoff and DO NOT flood queries unnecessarily + if (err == mStatus_HostUnreachErr) { - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; // Only increase interval if send succeeded - q->unansweredQueries++; - if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) - q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; - if (private && q->state != LLQ_Poll) + DNSServer *newServer; + + LogInfo("uDNS_CheckCurrentQuestion: host unreachable error for DNS server %#a for question [%p] %##s (%s)", + &q->qDNSServer->addr, q, q->qname.c, DNSTypeName(q->qtype)); + + if (!StrictUnicastOrdering) { - // We don't want to retransmit too soon. Hence, we always schedule our first - // retransmisson at 3 seconds rather than one second - if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; - if (q->ThisQInterval > LLQ_POLL_INTERVAL) - q->ThisQInterval = LLQ_POLL_INTERVAL; - LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME); } - if (q->qDNSServer->cellIntf) + + newServer = GetServerForQuestion(m, q); + DNSServerChangeForQuestion(m, q, newServer); + + if (q->triedAllServersOnce) + { + q->LastQTime = m->timenow; + } + else { - // We don't want to retransmit too soon. Schedule our first retransmisson at - // MIN_UCAST_RETRANS_TIMEOUT seconds. - if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) - q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; } - debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); + q->unansweredQueries = 0; + q->noServerResponse = 1; + } + else + { + if (err != mStatus_TransientErr) // if it is not a transient error backoff and DO NOT flood queries unnecessarily + { + // If all DNS Servers are not responding, then we back-off using the multiplier UDNSBackOffMultiplier(*2). + // Only increase interval if send succeeded + + q->ThisQInterval = q->ThisQInterval * UDNSBackOffMultiplier; + if ((q->ThisQInterval > 0) && (q->ThisQInterval < MinQuestionInterval)) // We do not want to retx within 1 sec + q->ThisQInterval = MinQuestionInterval; + + q->unansweredQueries++; + if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) + q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + if (private && q->state != LLQ_Poll) + { + // We don't want to retransmit too soon. Hence, we always schedule our first + // retransmisson at 3 seconds rather than one second + if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + if (q->qDNSServer->cellIntf) + { + // We don't want to retransmit too soon. Schedule our first retransmisson at + // MIN_UCAST_RETRANS_TIMEOUT seconds. + if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) + q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; + } + debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); + } + q->LastQTime = m->timenow; } - q->LastQTime = m->timenow; SetNextQueryTime(m, q); } else @@ -4890,7 +4888,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { if (SameNameRecordAnswersQuestion(&rr->resrec, q)) { - LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); + LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); mDNS_PurgeCacheResourceRecord(m, rr); } } @@ -4916,7 +4914,6 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) mDNSexport void CheckNATMappings(mDNS *m) { - mStatus err = mStatus_NoError; mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4); mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4); m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF; @@ -4974,7 +4971,7 @@ mDNSexport void CheckNATMappings(mDNS *m) cur->retryInterval = NATMAP_INIT_RETRY; } - err = uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary + uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry NATSetNextRenewalTime(m, cur); @@ -5013,7 +5010,7 @@ mDNSexport void CheckNATMappings(mDNS *m) const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&EffectiveAddress) ? mStatus_DoubleNAT : mStatus_NoError; const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort : !mDNSIPv4AddressIsZero(EffectiveAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort; - + if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8) { if (!mDNSSameIPv4Address(cur->ExternalAddress, EffectiveAddress) || @@ -5301,8 +5298,8 @@ mDNSexport void udns_validatelists(void *const v) DNSServer *d; for (d = m->DNSServers; d; d=d->next) - if (d->next == (DNSServer *)~0 || d->teststate > DNSServer_Disabled) - LogMemCorruption("m->DNSServers: %p is garbage (%d)", d, d->teststate); + if (d->next == (DNSServer *)~0) + LogMemCorruption("m->DNSServers: %p is garbage", d); DomainAuthInfo *info; for (info = m->AuthInfoList; info; info = info->next) @@ -5774,101 +5771,327 @@ struct CompileTimeAssertionChecks_uDNS char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; }; +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - DNS Push Notification functions +#endif + +mDNSlocal tcpInfo_t * GetTCPConnectionToPushServer(mDNS *m, DNSQuestion *q) +{ + // If we already have a question for this zone and if the server is the same, reuse it + DNSPushNotificationZone *zone = mDNSNULL; + for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next) + { + if (SameDomainName(&q->nta->ChildName, &zone->zoneName)) + { + DNSPushNotificationServer *zoneServer = mDNSNULL; + for (zoneServer = zone->servers; zoneServer != mDNSNULL; zoneServer = zoneServer->next) + { + if (mDNSSameAddress(&q->dnsPushServerAddr, &zoneServer->serverAddr)) + { + zone->numberOfQuestions++; + zoneServer->numberOfQuestions++; + return zoneServer->connection; + } + } + } + } + + // If we have a connection to this server but it is for a differnt zone, create a new zone entry and reuse the connection + DNSPushNotificationServer *server = mDNSNULL; + for (server = m->DNSPushServers; server != mDNSNULL; server = server->next) + { + if (mDNSSameAddress(&q->dnsPushServerAddr, &server->serverAddr)) + { + DNSPushNotificationZone *newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone)); + newZone->numberOfQuestions = 1; + newZone->zoneName = q->nta->ChildName; + newZone->servers = server; + + // Add the new zone to the begining of the list + newZone->next = m->DNSPushZones; + m->DNSPushZones = newZone; + + server->numberOfQuestions++; + return server->connection; + } + } + + // If we do not have any existing connections, create a new connection + DNSPushNotificationServer *newServer = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationServer)); + DNSPushNotificationZone *newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone)); + + newServer->numberOfQuestions = 1; + newServer->serverAddr = q->dnsPushServerAddr; + newServer->connection = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->dnsPushServerAddr, q->dnsPushServerPort, &q->nta->Host, q, mDNSNULL); + + newZone->numberOfQuestions = 1; + newZone->zoneName = q->nta->ChildName; + newZone->servers = newServer; + + // Add the new zone to the begining of the list + newZone->next = m->DNSPushZones; + m->DNSPushZones = newZone; + + newServer->next = m->DNSPushServers; + m->DNSPushServers = newServer; + return newServer->connection; +} + +mDNSexport void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + /* Use the same NAT setup as in the LLQ case */ + if (m->LLQNAT.clientContext != mDNSNULL) // LLQNAT just started, give it some time + { + LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + return; + } + + // Either we don't have {PCP, NAT-PMP, UPnP/IGD} support (ExternalPort is zero) or behind a Double NAT that may or + // may not have {PCP, NAT-PMP, UPnP/IGD} support (NATResult is non-zero) + if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) + { + LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d", + q->qname.c, DNSTypeName(q->qtype), mDNSVal16(m->LLQNAT.ExternalPort), m->LLQNAT.Result); + StartLLQPolling(m, q); // Actually sets up the NAT Auto Tunnel + return; + } + + if (mDNSIPPortIsZero(q->dnsPushServerPort) && q->dnsPushState == DNSPUSH_INIT) + { + LogInfo("SubscribeToDNSPushNotificationServer: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + q->dnsPushServerAddr = zeroAddr; + // We know q->dnsPushServerPort is zero because of check above + if (q->nta) CancelGetZoneData(m, q->nta); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceDNSPush, DNSPushNotificationGotZoneData, q); + return; + } + + if (q->tcp) + { + LogInfo("SubscribeToDNSPushNotificationServer: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + DisposeTCPConn(q->tcp); + q->tcp = mDNSNULL; + } + + if (!q->nta) + { + // Normally we lookup the zone data and then call this function. And we never free the zone data + // for "PrivateQuery". But sometimes this can happen due to some race conditions. When we + // switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT. + // When we poll, we free the zone information as we send the query to the server (See + // PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we + // are still behind Double NAT, we would have returned early in this function. But we could + // have switched to a network with no NATs and we should get the zone data again. + LogInfo("SubscribeToDNSPushNotificationServer: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceDNSPush, DNSPushNotificationGotZoneData, q); + return; + } + else if (!q->nta->Host.c[0]) + { + // This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname + LogMsg("SubscribeToDNSPushNotificationServer: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived); + } + q->tcp = GetTCPConnectionToPushServer(m,q); + // If TCP failed (transient networking glitch) try again in five seconds + q->ThisQInterval = (q->tcp != mDNSNULL) ? q->ThisQInterval = 0 : (mDNSPlatformOneSecond * 5); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); +} + + +mDNSexport void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + mDNSu8 *end = mDNSNULL; + InitializeDNSMessage(&m->omsg.h, zeroID, SubscribeFlags); + end = putQuestion(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (!end) + { + LogMsg("ERROR: SubscribeToDNSPushNotificationServer putQuestion failed"); + return; + } + + mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->dnsPushServerAddr, q->dnsPushServerPort, q->tcp->sock, mDNSNULL, mDNSfalse); + + // update question state + q->dnsPushState = DNSPUSH_ESTABLISHED; + q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + +} + +mDNSlocal void reconcileDNSPushConnection(mDNS *m, DNSQuestion *q) +{ + DNSPushNotificationZone *zone; + DNSPushNotificationServer *server; + // Update the counts + for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next) + { + if (SameDomainName(&zone->zoneName, &q->nta->ChildName)) + { + zone->numberOfQuestions--; + for (server = zone->servers; server != mDNSNULL; server = server->next) + { + if (mDNSSameAddress(&server->serverAddr, &q->dnsPushServerAddr)) + server->numberOfQuestions--; + } + } + } + + // Now prune the lists + server = m->DNSPushServers; + DNSPushNotificationServer *nextServer = mDNSNULL; + while(server != mDNSNULL) + { + nextServer = server->next; + if (server->numberOfQuestions <= 0) + { + DisposeTCPConn(server->connection); + if (server == m->DNSPushServers) + m->DNSPushServers = nextServer; + mDNSPlatformMemFree(server); + server = nextServer; + } + else server = server->next; + } + + zone = m->DNSPushZones; + DNSPushNotificationZone *nextZone = mDNSNULL; + while(zone != mDNSNULL) + { + nextZone = zone->next; + if (zone->numberOfQuestions <= 0) + { + if (zone == m->DNSPushZones) + m->DNSPushZones = nextZone; + mDNSPlatformMemFree(zone); + zone = nextZone; + } + else zone = zone->next; + } + +} + +mDNSexport void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + mDNSu8 *end = mDNSNULL; + InitializeDNSMessage(&m->omsg.h, q->TargetQID, UnSubscribeFlags); + end = putQuestion(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (!end) + { + LogMsg("ERROR: UnSubscribeToDNSPushNotificationServer - putQuestion failed"); + return; + } + + mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->dnsPushServerAddr, q->dnsPushServerPort, q->tcp->sock, mDNSNULL, mDNSfalse); + + reconcileDNSPushConnection(m, q); +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#endif #else // !UNICAST_DISABLED mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) { - (void) m; - (void) rr; + (void) m; + (void) rr; - return mDNSNULL; + return mDNSNULL; } mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name) { - (void) m; - (void) name; + (void) m; + (void) name; - return mDNSNULL; + return mDNSNULL; } mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) { - (void) m; - (void) q; + (void) m; + (void) q; - return mDNSNULL; + return mDNSNULL; } mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) { - (void) m; - (void) q; + (void) m; + (void) q; } mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) { - (void) tcp; + (void) tcp; } mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) { - (void) m; - (void) traversal; + (void) m; + (void) traversal; - return mStatus_UnsupportedErr; + return mStatus_UnsupportedErr; } mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) { - (void) m; - (void) traversal; + (void) m; + (void) traversal; - return mStatus_UnsupportedErr; + return mStatus_UnsupportedErr; } mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) { - (void) m; - (void) q; + (void) m; + (void) q; } mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext) { - (void) m; - (void) name; - (void) target; - (void) callback; - (void) ZoneDataContext; + (void) m; + (void) name; + (void) target; + (void) callback; + (void) ZoneDataContext; - return mDNSNULL; + return mDNSNULL; } mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData) { - (void) m; - (void) err; - (void) zoneData; + (void) m; + (void) err; + (void) zoneData; } mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion) { - (void) m; - (void) msg; - (void) end; - (void) srcaddr; - (void) srcport; - (void) matchQuestion; + (void) m; + (void) msg; + (void) end; + (void) srcaddr; + (void) srcport; + (void) matchQuestion; - return uDNS_LLQ_Not; + return uDNS_LLQ_Not; } mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags) { - (void) m; - (void) q; - (void) responseFlags; + (void) m; + (void) q; + (void) responseFlags; } mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) @@ -5892,7 +6115,7 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const (void) hostname; (void) port; (void) autoTunnel; - + return mStatus_UnsupportedErr; } @@ -5902,7 +6125,7 @@ mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID (void) InterfaceID; (void) searchIndex; (void) ignoreDotLocal; - + return mDNSNULL; } @@ -5910,7 +6133,7 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const n { (void) m; (void) name; - + return mDNSNULL; } @@ -5918,7 +6141,7 @@ mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *trave { (void) m; (void) traversal; - + return mStatus_UnsupportedErr; } @@ -5926,7 +6149,7 @@ mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traver { (void) m; (void) traversal; - + return mStatus_UnsupportedErr; } @@ -5947,7 +6170,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (void) reqA; (void) reqAAAA; (void) reqDO; - + return mDNSNULL; } @@ -5998,8 +6221,27 @@ mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks) mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q) { (void)q; - + return mDNSfalse; } +mDNSexport void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + (void)m; + (void)q; +} + +mDNSexport void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + (void)m; + (void)q; +} + +mDNSexport void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + (void)m; + (void)q; +} + #endif // !UNICAST_DISABLED + diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index eca8b70..910449f 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -47,9 +47,8 @@ extern "C" { #define QuestionIntervalStep3 (QuestionIntervalStep*QuestionIntervalStep*QuestionIntervalStep) #define InitialQuestionInterval ((mDNSPlatformOneSecond + QuestionIntervalStep-1) / QuestionIntervalStep) #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) - -// just move to MaxQuestionInterval once over this threshold -#define QuestionIntervalThreshold (QuestionIntervalStep3 * mDNSPlatformOneSecond) +#define UDNSBackOffMultiplier 2 +#define MinQuestionInterval (1 * mDNSPlatformOneSecond) // For Unicast record registrations, we initialize the interval to 1 second. When we send any query for // the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep @@ -82,6 +81,11 @@ extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) extern void startLLQHandshake(mDNS *m, DNSQuestion *q); extern void sendLLQRefresh(mDNS *m, DNSQuestion *q); +extern void DNSPushNotificationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo); +extern void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q); +extern void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); +extern void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); + extern void SleepRecordRegistrations(mDNS *m); // uDNS_UpdateRecord @@ -144,6 +148,10 @@ extern void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mD extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr); extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol); +// DNS Push Notification +extern void SubscribeToDNSPushNotification(mDNS *m, DNSQuestion *q); + + #ifdef __cplusplus } #endif diff --git a/mDNSMacOS9/Mac OS Test Responder.c b/mDNSMacOS9/Mac OS Test Responder.c deleted file mode 100644 index eff6a5f..0000000 --- a/mDNSMacOS9/Mac OS Test Responder.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -*- 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. - */ - -#include // For printf() -#include // For strlen() etc. - -#include // For WaitNextEvent() -#include // For SIOUXHandleOneEvent() - -#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above - -#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform - -// These don't have to be globals, but their memory does need to remain valid for as -// long as the search is going on. They are declared as globals here for simplicity. -static mDNS m; -static mDNS_PlatformSupport p; -static ServiceRecordSet p1, p2, afp, http, njp; -static AuthRecord browsedomain1, browsedomain2; - -// This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new -// unique name for the service. For a device such as a printer, this may be appropriate. -// For a device with a user interface, and a screen, and a keyboard, the appropriate -// response may be to prompt the user and ask them to choose a new name for the service. -mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) - { - switch (result) - { - case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name->c); break; - case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name->c); break; - case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name->c); break; - default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name->c, result); break; - } - - if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); - } - -// RegisterService() is a simple wrapper function which takes C string -// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() -mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, - UInt16 PortAsNumber, const char txtinfo[], - const domainlabel *const n, const char type[], const char domain[]) - { - domainname t; - domainname d; - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - UInt8 txtbuffer[512]; - - MakeDomainNameFromDNSNameString(&t, type); - MakeDomainNameFromDNSNameString(&d, domain); - - if (txtinfo) - { - strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); - txtbuffer[0] = (UInt8)strlen(txtinfo); - } - else - txtbuffer[0] = 0; - - mDNS_RegisterService(m, recordset, - n, &t, &d, // Name, type, domain - mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), - txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length - mDNSNULL, 0, // Subtypes (none) - mDNSInterface_Any, // Interface ID - Callback, mDNSNULL); // Callback and context - - ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); - printf("Made Service Records for %s\n", buffer); - } - -// RegisterFakeServiceForTesting() simulates the effect of services being registered on -// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. -mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], - const char name[], const char type[], const char domain[]) - { - static UInt16 NextPort = 0xF000; - domainlabel n; - MakeDomainLabelFromLiteralString(&n, name); - RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); - } - -// CreateProxyRegistrationForRealService() checks to see if the given port is currently -// in use, and if so, advertises the specified service as present on that port. -// This is useful for advertising existing real services (Personal Web Sharing, Personal -// File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. -mDNSlocal OSStatus CreateProxyRegistrationForRealService(mDNS *m, UInt16 PortAsNumber, const char txtinfo[], - const char *servicetype, ServiceRecordSet *recordset) - { - InetAddress ia; - TBind bindReq; - OSStatus err; - TEndpointInfo endpointinfo; - EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); - if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } - - ia.fAddressType = AF_INET; - ia.fPort = mDNSOpaque16fromIntVal(PortAsNumber).NotAnInteger; - ia.fHost = 0; - bindReq.addr.maxlen = sizeof(ia); - bindReq.addr.len = sizeof(ia); - bindReq.addr.buf = (UInt8*)&ia; - bindReq.qlen = 0; - err = OTBind(ep, &bindReq, NULL); - - if (err == kOTBadAddressErr) - RegisterService(m, recordset, PortAsNumber, txtinfo, &m->nicelabel, servicetype, "local."); - else if (err) - debugf("OTBind failed %d", err); - - OTCloseProvider(ep); - return(noErr); - } - -// Done once on startup, and then again every time our address changes -mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) - { - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; - - ConvertDomainNameToCString(&m->MulticastHostname, buffer); - printf("Name %s\n", buffer); - printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); - - printf("\n"); - printf("Registering Service Records\n"); - // Create example printer discovery records - //static ServiceRecordSet p1, p2; - -#define SRSET 0 -#if SRSET==0 - RegisterFakeServiceForTesting(m, &p1, "path=/index.html", "Web Server One", "_http._tcp.", "local."); - RegisterFakeServiceForTesting(m, &p2, "path=/path.html", "Web Server Two", "_http._tcp.", "local."); -#elif SRSET==1 - RegisterFakeServiceForTesting(m, &p1, "rn=lpq1", "Epson Stylus 900N", "_printer._tcp.", "local."); - RegisterFakeServiceForTesting(m, &p2, "rn=lpq2", "HP LaserJet", "_printer._tcp.", "local."); -#else - RegisterFakeServiceForTesting(m, &p1, "rn=lpq3", "My Printer", "_printer._tcp.", "local."); - RegisterFakeServiceForTesting(m, &p2, "lrn=pq4", "My Other Printer", "_printer._tcp.", "local."); -#endif - - // If AFP Server is running, register a record for it - CreateProxyRegistrationForRealService(m, 548, "", "_afpovertcp._tcp.", &afp); - - // If Web Server is running, register a record for it - CreateProxyRegistrationForRealService(m, 80, "", "_http._tcp.", &http); - - // And pretend we always have an NJP server running on port 80 too - //RegisterService(m, &njp, 80, "NJP/", &m->nicelabel, "_njp._tcp.", "local."); - - // Advertise that apple.com. is available for browsing - mDNS_AdvertiseDomains(m, &browsedomain1, mDNS_DomainTypeBrowse, mDNSInterface_Any, "apple.com."); - mDNS_AdvertiseDomains(m, &browsedomain2, mDNS_DomainTypeBrowse, mDNSInterface_Any, "IL 2\\4th Floor.apple.com."); - - return(kOTNoError); - } - -// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS -mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) - { - extern Boolean SIOUXQuitting; - EventRecord e; - WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); - SIOUXHandleOneEvent(&e); - return(SIOUXQuitting); - } - -int main() - { - mStatus err; - Boolean DoneSetup = false; - - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; - - printf("Multicast DNS Responder\n\n"); - printf("This software reports errors using MacsBug breaks,\n"); - printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); - printf("******************************************************************************\n"); - - err = InitOpenTransport(); - if (err) { debugf("InitOpenTransport failed %d", err); return(err); } - - err = mDNS_Init(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, - mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - if (err) return(err); - - while (!YieldSomeTime(35)) - { -#if MDNS_ONLYSYSTEMTASK - // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. - // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() - extern void mDNSPlatformIdle(mDNS *const m); - mDNSPlatformIdle(&m); // Only needed for debugging version -#endif - if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) - { - DoneSetup = true; - printf("\nListening for mDNS queries...\n"); - mDNSResponderTestSetup(&m); - } - } - - if (p1.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p1); - if (p2.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &p2); - if (afp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &afp); - if (http.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &http); - if (njp.RR_SRV.resrec.RecordType ) mDNS_DeregisterService(&m, &njp); - - mDNS_Close(&m); - - return(0); - } diff --git a/mDNSMacOS9/Mac OS Test Searcher.c b/mDNSMacOS9/Mac OS Test Searcher.c deleted file mode 100644 index 1de6463..0000000 --- a/mDNSMacOS9/Mac OS Test Searcher.c +++ /dev/null @@ -1,243 +0,0 @@ -/* -*- 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. - */ - -#include // For printf() -#include // For WaitNextEvent() -#include // For SIOUXHandleOneEvent() - -#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above -#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform - -typedef struct - { - OTLIFO serviceinfolist; - Boolean headerPrinted; - Boolean lostRecords; - } SearcherServices; - -typedef struct { ServiceInfo i; mDNSBool add; mDNSBool dom; OTLink link; } linkedServiceInfo; - -// These don't have to be globals, but their memory does need to remain valid for as -// long as the search is going on. They are declared as globals here for simplicity. -#define RR_CACHE_SIZE 1000 -static CacheEntity rrcachestorage[RR_CACHE_SIZE]; -static mDNS mDNSStorage; -static mDNS_PlatformSupport PlatformSupportStorage; -static SearcherServices services; -static DNSQuestion browsequestion, domainquestion; - -// PrintServiceInfo prints the service information to standard out -// A real application might want to do something else with the information -static void PrintServiceInfo(SearcherServices *services) - { - OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); - - while (link) - { - linkedServiceInfo *ls = OTGetLinkObject(link, linkedServiceInfo, link); - ServiceInfo *s = &ls->i; - - if (!services->headerPrinted) - { - printf("%-55s Type Domain IP Address Port Info\n", "Name"); - services->headerPrinted = true; - } - - if (ls->dom) - { - char c_dom[MAX_ESCAPED_DOMAIN_NAME]; - ConvertDomainNameToCString(&s->name, c_dom); - if (ls->add) printf("%-55s available for browsing\n", c_dom); - else printf("%-55s no longer available for browsing\n", c_dom); - } - else - { - domainlabel name; - domainname type, domain; - char c_name[MAX_DOMAIN_LABEL+1], c_type[MAX_ESCAPED_DOMAIN_NAME], c_dom[MAX_ESCAPED_DOMAIN_NAME], c_ip[20]; - DeconstructServiceName(&s->name, &name, &type, &domain); - ConvertDomainLabelToCString_unescaped(&name, c_name); - ConvertDomainNameToCString(&type, c_type); - ConvertDomainNameToCString(&domain, c_dom); - sprintf(c_ip, "%d.%d.%d.%d", s->ip.ip.v4.b[0], s->ip.ip.v4.b[1], s->ip.ip.v4.b[2], s->ip.ip.v4.b[3]); - - printf("%-55s %-16s %-14s ", c_name, c_type, c_dom); - if (ls->add) printf("%-15s %5d %#s\n", c_ip, mDNSVal16(s->port), s->TXTinfo); - else printf("Removed\n"); - } - - link = link->fNext; - OTFreeMem(ls); - } - } - -// When the name, address, port, and txtinfo for a service is found, FoundInstanceInfo() -// enqueues a record for PrintServiceInfo() to print. -// Note, a browsing application would *not* normally need to get all this information -- -// all it needs is the name, to display to the user. -// Finding out the address, port, and txtinfo should be deferred to the time that the user -// actually needs to contact the service to use it. -static void FoundInstanceInfo(mDNS *const m, ServiceInfoQuery *query) - { - SearcherServices *services = (SearcherServices *)query->ServiceInfoQueryContext; - linkedServiceInfo *info = (linkedServiceInfo *)(query->info); - if (query->info->ip.type == mDNSAddrType_IPv4) - { - mDNS_StopResolveService(m, query); // For this test code, one answer is sufficient - OTLIFOEnqueue(&services->serviceinfolist, &info->link); - OTFreeMem(query); - } - } - -// When a new named instance of a service is found, FoundInstance() is called. -// In this sample code we turn around and immediately issue a query to resolve that service name to -// find its address, port, and txtinfo, but a normal browing application would just display the name. -static void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - #pragma unused (question) - SearcherServices *services = (SearcherServices *)question->QuestionContext; - linkedServiceInfo *info; - - debugf("FoundInstance %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); - - if (answer->rrtype != kDNSType_PTR) return; - if (!services) { debugf("FoundInstance: services is NULL"); return; } - - info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); - if (!info) { services->lostRecords = true; return; } - - info->i.name = answer->rdata->u.name; - info->i.InterfaceID = answer->InterfaceID; - info->i.ip.type = mDNSAddrType_IPv4; - info->i.ip.ip.v4 = zerov4Addr; - info->i.port = zeroIPPort; - info->add = AddRecord; - info->dom = mDNSfalse; - - if (!AddRecord) // If TTL == 0 we're deleting a service, - OTLIFOEnqueue(&services->serviceinfolist, &info->link); - else // else we're adding a new service - { - ServiceInfoQuery *q = (ServiceInfoQuery *)OTAllocMem(sizeof(ServiceInfoQuery)); - if (!q) { OTFreeMem(info); services->lostRecords = true; return; } - mDNS_StartResolveService(m, q, &info->i, FoundInstanceInfo, services); - } - } - -static void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) - { - #pragma unused (m) - #pragma unused (question) - SearcherServices *services = (SearcherServices *)question->QuestionContext; - linkedServiceInfo *info; - - debugf("FoundDomain %##s PTR %##s", answer->name->c, answer->rdata->u.name.c); - - if (answer->rrtype != kDNSType_PTR) return; - if (!services) { debugf("FoundDomain: services is NULL"); return; } - - info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); - if (!info) { services->lostRecords = true; return; } - - info->i.name = answer->rdata->u.name; - info->i.InterfaceID = answer->InterfaceID; - info->i.ip.type = mDNSAddrType_IPv4; - info->i.ip.ip.v4 = zerov4Addr; - info->i.port = zeroIPPort; - info->add = AddRecord; - info->dom = mDNStrue; - - OTLIFOEnqueue(&services->serviceinfolist, &info->link); - } - -// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS -static Boolean YieldSomeTime(UInt32 milliseconds) - { - extern Boolean SIOUXQuitting; - EventRecord e; - WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); - SIOUXHandleOneEvent(&e); - return(SIOUXQuitting); - } - -int main() - { - mStatus err; - Boolean DoneSetup = false; - void *tempmem; - - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; - SIOUXSettings.rows = 40; - SIOUXSettings.columns = 132; - - printf("Multicast DNS Searcher\n\n"); - printf("This software reports errors using MacsBug breaks,\n"); - printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); - printf("******************************************************************************\n"); - - err = InitOpenTransport(); - if (err) { debugf("InitOpenTransport failed %d", err); return(err); } - - err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, - mDNS_Init_DontAdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - if (err) return(err); - - // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time - tempmem = OTAllocMem(0x10000); - if (tempmem) OTFreeMem(tempmem); - else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); - - services.serviceinfolist.fHead = NULL; - services.headerPrinted = false; - services.lostRecords = false; - - while (!YieldSomeTime(35)) - { -#if MDNS_ONLYSYSTEMTASK - // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. - // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() - extern void mDNSPlatformIdle(mDNS *const m); - mDNSPlatformIdle(&mDNSStorage); // Only needed for debugging version -#endif - if (mDNSStorage.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) - { - domainname srvtype, srvdom; - DoneSetup = true; - printf("\nSending mDNS service lookup queries and waiting for responses...\n\n"); - MakeDomainNameFromDNSNameString(&srvtype, "_http._tcp."); - MakeDomainNameFromDNSNameString(&srvdom, "local."); - err = mDNS_StartBrowse(&mDNSStorage, &browsequestion, &srvtype, &srvdom, mDNSInterface_Any, mDNSfalse, FoundInstance, &services); - if (err) break; - err = mDNS_GetDomains(&mDNSStorage, &domainquestion, mDNS_DomainTypeBrowse, NULL, mDNSInterface_Any, FoundDomain, &services); - if (err) break; - } - - if (services.serviceinfolist.fHead) - PrintServiceInfo(&services); - - if (services.lostRecords) - { - services.lostRecords = false; - printf("**** Warning: Out of memory: Records have been missed.\n"); - } - } - - mDNS_StopBrowse(&mDNSStorage, &browsequestion); - mDNS_Close(&mDNSStorage); - return(0); - } diff --git a/mDNSMacOS9/README.txt b/mDNSMacOS9/README.txt deleted file mode 100644 index c30c423..0000000 --- a/mDNSMacOS9/README.txt +++ /dev/null @@ -1,48 +0,0 @@ -This directory contains support files for running mDNS on Mac OS 9 -(and Carbon). - -mDNS.mcp is a CodeWarrior 8 project file. - -mDNSMacOS9.c and mDNSMacOS9.h are the Platform Support files that go below -mDNS Core. - -"Mac OS Test Responder.c" and "Mac OS Test Searcher.c" build an example -standalone (embedded) mDNS Responder and Searcher, respectively. - -"mDNSLibrary.c" builds a CFM Shared Library that goes in the Extensions Folder - -The CFM Shared Library inplements the same "/usr/include/dns_sd.h" API -that exists on OS X, Windows, Linux, etc., one exception: - - - You don't need to call DNSServiceRefSockFD() to get a file descriptor, - and add that file descriptor to your select loop (or equivalent), - wait for data, - and then call DNSServiceProcessResult() every time data arrives. - - On OS 9, your callback functions are called "by magic" without having - to do any of this. Of course no magic comes without a price, and - the magic being used here is an OT Notifier function. Your callback - functions are called directly from the OT Notifier function that - received the UDP packet from the wire, which is fast and efficient, - but it does mean that you're being called at OT Notifier time -- so - no QuickDraw calls. If you need to allocate memory in your callback - function, use OTAllocMem(), not NewPtr() or NewHandle(). Typically - what you'll do in your callback function is just update your program's - data structures, and leave screen updating and UI to some foreground - code. - -"Searcher.c" and "Responder.c" build sample applications that link with -this CFM Shared Library in the Extensions Folder. You'll see that if -you try to run them without first putting the "Multicast DNS & DNS-SD" -library in the Extensions Folder and restarting, they will report an -error message. - -"Searcher.c" builds a sample application that browses for HTTP servers. - -"Responder.c" builds a sample application that advertises some test -services. By default it advertises a couple of (nonexistent) HTTP servers -called "Web Server One" and "Web Server Two". In addition, if it finds that -TCP port 548 is occupied, then it concludes that personal file sharing is -running, and advertises the existence of an AFP server. Similarly, if it -finds that TCP port 80 is occupied, then it concludes that personal Web -sharing is running, advertises the existence of an HTTP server. diff --git a/mDNSMacOS9/Responder.c b/mDNSMacOS9/Responder.c deleted file mode 100644 index 78851a6..0000000 --- a/mDNSMacOS9/Responder.c +++ /dev/null @@ -1,183 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2004 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 // For printf() -#include // For strcpy() - -#include // For WaitNextEvent() - -#include -#include - -#include // For SIOUXHandleOneEvent() - -#include "dns_sd.h" - -typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16; -static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); } -static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v) -{ mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } - -typedef struct RegisteredService_struct RegisteredService; -struct RegisteredService_struct -{ - RegisteredService *next; - DNSServiceRef sdRef; - Boolean gotresult; - DNSServiceErrorType errorCode; - char namestr[64]; - char typestr[kDNSServiceMaxDomainName]; - char domstr [kDNSServiceMaxDomainName]; -}; - -static RegisteredService p1, p2, afp, http, njp; -static RegisteredService *services = NULL, **nextservice = &services; - -static void RegCallback(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, - const char *name, const char *regtype, const char *domain, void *context) -{ - RegisteredService *rs = (RegisteredService *)context; - (void)sdRef; // Unused - (void)flags; // Unused - rs->gotresult = true; - rs->errorCode = errorCode; - strcpy(rs->namestr, name); - strcpy(rs->typestr, regtype); - strcpy(rs->domstr, domain); -} - -static DNSServiceErrorType RegisterService(RegisteredService *rs, mDNSOpaque16 OpaquePort, - const char name[], const char type[], const char domain[], const char txtinfo[]) -{ - DNSServiceErrorType err; - unsigned char txtbuffer[257]; - strncpy((char*)txtbuffer+1, txtinfo, 255); - txtbuffer[256] = 0; - txtbuffer[0] = (unsigned char)strlen((char*)txtbuffer); - rs->gotresult = 0; - rs->errorCode = kDNSServiceErr_NoError; - err = DNSServiceRegister(&rs->sdRef, /* kDNSServiceFlagsAutoRename*/ 0, 0, - name, type, domain, NULL, OpaquePort.NotAnInteger, (unsigned short)(1+txtbuffer[0]), txtbuffer, RegCallback, rs); - if (err) - printf("RegisterService(%s %s %s) failed %d\n", name, type, domain, err); - else - { *nextservice = rs; nextservice = &rs->next; } - return(err); -} - -// RegisterFakeServiceForTesting() simulates the effect of services being registered on -// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. -static DNSServiceErrorType RegisterFakeServiceForTesting(RegisteredService *rs, - const char name[], const char type[], const char domain[], const char txtinfo[]) -{ - static UInt16 NextPort = 0xF000; - return RegisterService(rs, mDNSOpaque16fromIntVal(NextPort++), name, type, domain, txtinfo); -} - -// CreateProxyRegistrationForRealService() checks to see if the given port is currently -// in use, and if so, advertises the specified service as present on that port. -// This is useful for advertising existing real services (Personal Web Sharing, Personal -// File Sharing, etc.) that currently don't register with mDNS Service Discovery themselves. -static DNSServiceErrorType CreateProxyRegistrationForRealService(RegisteredService *rs, - const char *servicetype, UInt16 PortAsNumber, const char txtinfo[]) -{ - mDNSOpaque16 OpaquePort = mDNSOpaque16fromIntVal(PortAsNumber); - InetAddress ia; - TBind bindReq; - OSStatus err; - TEndpointInfo endpointinfo; - EndpointRef ep = OTOpenEndpoint(OTCreateConfiguration(kTCPName), 0, &endpointinfo, &err); - if (!ep || err) { printf("OTOpenEndpoint (CreateProxyRegistrationForRealService) failed %d", err); return(err); } - - ia.fAddressType = AF_INET; - ia.fPort = OpaquePort.NotAnInteger; - ia.fHost = 0; - bindReq.addr.maxlen = sizeof(ia); - bindReq.addr.len = sizeof(ia); - bindReq.addr.buf = (UInt8*)&ia; - bindReq.qlen = 0; - err = OTBind(ep, &bindReq, NULL); - - if (err == kOTBadAddressErr) - err = RegisterService(rs, OpaquePort, "", servicetype, "local.", txtinfo); - else if (err) - printf("OTBind failed %d", err); - - OTCloseProvider(ep); - return(err); -} - -// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS -static Boolean YieldSomeTime(UInt32 milliseconds) -{ - extern Boolean SIOUXQuitting; - EventRecord e; - WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); - SIOUXHandleOneEvent(&e); - return(SIOUXQuitting); -} - -int main() -{ - OSStatus err; - RegisteredService *s; - - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; - - printf("Multicast DNS Responder\n\n"); - printf("This software reports errors using MacsBug breaks,\n"); - printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); - printf("******************************************************************************\n\n"); - - err = InitOpenTransport(); - if (err) { printf("InitOpenTransport failed %d", err); return(err); } - - printf("Advertising Services...\n"); - -#define SRSET 0 -#if SRSET==0 - RegisterFakeServiceForTesting(&p1, "Web Server One", "_http._tcp.", "local.", "path=/index.html"); - RegisterFakeServiceForTesting(&p2, "Web Server Two", "_http._tcp.", "local.", "path=/path.html"); -#elif SRSET==1 - RegisterFakeServiceForTesting(&p1, "Epson Stylus 900N", "_printer._tcp.", "local.", "rn=lpq1"); - RegisterFakeServiceForTesting(&p2, "HP LaserJet", "_printer._tcp.", "local.", "rn=lpq2"); -#else - RegisterFakeServiceForTesting(&p1, "My Printer", "_printer._tcp.", "local.", "rn=lpq3"); - RegisterFakeServiceForTesting(&p2, "My Other Printer", "_printer._tcp.", "local.", "lrn=pq4"); -#endif - - // If AFP Server is running, register a record for it - CreateProxyRegistrationForRealService(&afp, "_afpovertcp._tcp.", 548, ""); - - // If Web Server is running, register a record for it - CreateProxyRegistrationForRealService(&http, "_http._tcp.", 80, "path=/index.html"); - - while (!YieldSomeTime(35)) - for (s = services; s; s = s->next) - if (s->gotresult) - { - printf("%s %s %s registered\n", s->namestr, s->typestr, s->domstr); - s->gotresult = false; - } - - for (s = services; s; s = s->next) - if (s->sdRef) DNSServiceRefDeallocate(s->sdRef); - - CloseOpenTransport(); - return(0); -} diff --git a/mDNSMacOS9/Searcher.c b/mDNSMacOS9/Searcher.c deleted file mode 100644 index a109683..0000000 --- a/mDNSMacOS9/Searcher.c +++ /dev/null @@ -1,240 +0,0 @@ -/* -*- 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. - */ - -#include // For printf() -#include // For strcpy() - -#include // For WaitNextEvent() -#include // For SIOkUnresolvedCFragSymbolAddress - -#include // For SIOUXHandleOneEvent() - -#include -#include - -#include "dns_sd.h" - -#define ns_c_in 1 -#define ns_t_a 1 - -typedef union { UInt8 b[2]; UInt16 NotAnInteger; } mDNSOpaque16; -static UInt16 mDNSVal16(mDNSOpaque16 x) { return((UInt16)(x.b[0]<<8 | x.b[1])); } -static mDNSOpaque16 mDNSOpaque16fromIntVal(UInt16 v) -{ mDNSOpaque16 x; x.b[0] = (UInt8)(v >> 8); x.b[1] = (UInt8)(v & 0xFF); return(x); } - -typedef struct -{ - OTLIFO serviceinfolist; - Boolean headerPrinted; - Boolean lostRecords; -} SearcherServices; - -typedef struct -{ - SearcherServices *services; - char name[kDNSServiceMaxDomainName]; - char type[kDNSServiceMaxDomainName]; - char domn[kDNSServiceMaxDomainName]; - char host[kDNSServiceMaxDomainName]; - char text[kDNSServiceMaxDomainName]; - InetHost address; - mDNSOpaque16 notAnIntPort; - DNSServiceRef sdRef; - Boolean add; - Boolean dom; - OTLink link; -} linkedServiceInfo; - -static SearcherServices services; - -// PrintServiceInfo prints the service information to standard out -// A real application might want to do something else with the information -static void PrintServiceInfo(SearcherServices *services) -{ - OTLink *link = OTReverseList(OTLIFOStealList(&services->serviceinfolist)); - - while (link) - { - linkedServiceInfo *s = OTGetLinkObject(link, linkedServiceInfo, link); - - if (!services->headerPrinted) - { - printf("%-55s Type Domain Target Host IP Address Port Info\n", "Name"); - services->headerPrinted = true; - } - - if (s->dom) - { - if (s->add) printf("%-55s available for browsing\n", s->domn); - else printf("%-55s no longer available for browsing\n", s->domn); - } - else - { - char ip[16]; - unsigned char *p = (unsigned char *)&s->address; - sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); - printf("%-55s %-16s %-14s ", s->name, s->type, s->domn); - if (s->add) printf("%-15s %-15s %5d %s\n", s->host, ip, mDNSVal16(s->notAnIntPort), s->text); - else printf("Removed\n"); - } - - link = link->fNext; - OTFreeMem(s); - } -} - -static void FoundInstanceAddress(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, - const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) -{ - linkedServiceInfo *info = (linkedServiceInfo *)context; - SearcherServices *services = info->services; - (void)sdRef; // Unused - (void)interfaceIndex; // Unused - (void)fullname; // Unused - (void)ttl; // Unused - if (errorCode == kDNSServiceErr_NoError) - if (flags & kDNSServiceFlagsAdd) - if (rrclass == ns_c_in && rrtype == ns_t_a && rdlen == sizeof(info->address)) - { - memcpy(&info->address, rdata, sizeof(info->address)); - DNSServiceRefDeallocate(info->sdRef); - OTLIFOEnqueue(&services->serviceinfolist, &info->link); - } -} - -static void FoundInstanceInfo(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, - DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t notAnIntPort, - uint16_t txtLen, const unsigned char *txtRecord, void *context) -{ - linkedServiceInfo *info = (linkedServiceInfo *)context; - SearcherServices *services = info->services; - (void)sdRef; // Unused - (void)flags; // Unused - (void)interfaceIndex; // Unused - (void)errorCode; // Unused - (void)fullname; // Unused - strcpy(info->host, hosttarget); - if (txtLen == 0) info->text[0] = 0; - else - { - strncpy(info->text, (char *)txtRecord+1, txtRecord[0]); - info->text[txtRecord[0]] = 0; - } - info->notAnIntPort.NotAnInteger = notAnIntPort; - DNSServiceRefDeallocate(info->sdRef); - DNSServiceQueryRecord(&info->sdRef, 0, 0, info->host, ns_t_a, ns_c_in, FoundInstanceAddress, info); -} - -// When a new named instance of a service is found, FoundInstance() is called. -// In this sample code we turn around and immediately to a DNSServiceResolve() to resolve that service name -// to find its target host, port, and txtinfo, but a normal browing application would just display the name. -// Resolving every single thing you find can be quite hard on the network, so you shouldn't do this -// in a real application. Defer resolving until the client has picked which instance from the -// long list of services is the one they want to use, and then resolve only that one. -static void FoundInstance(DNSServiceRef client, DNSServiceFlags flags, uint32_t interface, DNSServiceErrorType errorCode, - const char *replyName, const char *replyType, const char *replyDomain, void *context) -{ -#pragma unused(client, interface, errorCode) - SearcherServices *services = (SearcherServices *)context; - linkedServiceInfo *info; - - if (!services) { DebugStr("\pFoundInstance: services is NULL"); return; } - - info = (linkedServiceInfo *)OTAllocMem(sizeof(linkedServiceInfo)); - if (!info) { services->lostRecords = true; return; } - - info->services = services; - strcpy(info->name, replyName); - strcpy(info->type, replyType); - strcpy(info->domn, replyDomain); - info->text[0] = 0; - info->add = (flags & kDNSServiceFlagsAdd) ? true : false; - info->dom = false; - - if (!info->add) // If TTL == 0 we're deleting a service, - OTLIFOEnqueue(&services->serviceinfolist, &info->link); - else // else we're adding a new service - DNSServiceResolve(&info->sdRef, 0, 0, info->name, info->type, info->domn, FoundInstanceInfo, info); -} - -// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS -static Boolean YieldSomeTime(UInt32 milliseconds) -{ - extern Boolean SIOUXQuitting; - EventRecord e; - WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); - SIOUXHandleOneEvent(&e); - return(SIOUXQuitting); -} - -int main() -{ - OSStatus err; - void *tempmem; - DNSServiceRef sdRef; - DNSServiceErrorType dse; - - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Searcher"; - SIOUXSettings.rows = 40; - SIOUXSettings.columns = 160; - - printf("DNS-SD Search Client\n\n"); - printf("This software reports errors using MacsBug breaks,\n"); - printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); - printf("******************************************************************************\n\n"); - - if (DNSServiceBrowse == (void*)kUnresolvedCFragSymbolAddress) - { - printf("Before you can use mDNS/DNS-SD clients, you need to place the \n"); - printf("\"Multicast DNS & DNS-SD\" Extension in the Extensions Folder and restart\n"); - return(-1); - } - - err = InitOpenTransport(); - if (err) { printf("InitOpenTransport failed %d", err); return(err); } - - // Make sure OT has a large enough memory pool for us to draw from at OTNotifier (interrupt) time - tempmem = OTAllocMem(0x10000); - if (tempmem) OTFreeMem(tempmem); - else printf("**** Warning: OTAllocMem couldn't pre-allocate 64K for us.\n"); - - services.serviceinfolist.fHead = NULL; - services.headerPrinted = false; - services.lostRecords = false; - - printf("Sending mDNS service lookup queries and waiting for responses...\n\n"); - dse = DNSServiceBrowse(&sdRef, 0, 0, "_http._tcp", "", FoundInstance, &services); - if (dse == kDNSServiceErr_NoError) - { - while (!YieldSomeTime(35)) - { - if (services.serviceinfolist.fHead) - PrintServiceInfo(&services); - - if (services.lostRecords) - { - services.lostRecords = false; - printf("**** Warning: Out of memory: Records have been missed.\n"); - } - } - } - - DNSServiceRefDeallocate(sdRef); - CloseOpenTransport(); - return(0); -} diff --git a/mDNSMacOS9/ShowInitIcon.c b/mDNSMacOS9/ShowInitIcon.c deleted file mode 100755 index 15d0221..0000000 --- a/mDNSMacOS9/ShowInitIcon.c +++ /dev/null @@ -1,160 +0,0 @@ -// ShowInitIcon - version 1.0.1, May 30th, 1995 -// This code is intended to let INIT writers easily display an icon at startup time. -// View in Geneva 9pt, 4-space tabs - -// Written by: Peter N Lewis , Jim Walker -// and François Pottier , with thanks to previous ShowINIT authors. -// Send comments and bug reports to François Pottier. - -// This version features: -// - Short and readable code. -// - Correctly wraps around when more than one row of icons has been displayed. -// - works with System 6 -// - Built with Universal Headers & CodeWarrior. Should work with other headers/compilers. - -#include -#include -#include -#include -#include "ShowInitIcon.h" - -// You should set SystemSixOrLater in your headers to avoid including glue for SysEnvirons. - -// --------------------------------------------------------------------------------------------------------------------- -// Set this flag to 1 if you want to compile this file into a stand-alone resource (see note below). -// Set it to 0 if you want to include this source file into your INIT project. - -#if 0 -#define ShowInitIcon main -#endif - -// --------------------------------------------------------------------------------------------------------------------- -// The ShowINIT mechanism works by having each INIT read/write data from these globals. -// The MPW C compiler doesn't accept variables declared at an absolute address, so I use these macros instead. -// Only one macro is defined per variable; there is no need to define a Set and a Get accessor like in . - -#define LMVCheckSum (*(unsigned short*) 0x928) -#define LMVCoord (*( short*) 0x92A) -#define LMHCoord (*( short*) 0x92C) -#define LMHCheckSum (*(unsigned short*) 0x92E) - -// --------------------------------------------------------------------------------------------------------------------- -// Prototypes for the subroutines. The main routine comes first; this is necessary to make THINK C's "Custom Header" option work. - -static unsigned short CheckSum (short x); -static void ComputeIconRect (Rect* iconRect, Rect* screenBounds); -static void AdvanceIconPosition (Rect* iconRect); -static void DrawBWIcon (short iconID, Rect *iconRect); - -// --------------------------------------------------------------------------------------------------------------------- -// Main routine. - -typedef struct { - QDGlobals qd; // Storage for the QuickDraw globals - long qdGlobalsPtr; // A5 points to this place; it will contain a pointer to qd -} QDStorage; - -pascal void ShowInitIcon (short iconFamilyID, Boolean advance) -{ - long oldA5; // Original value of register A5 - QDStorage qds; // Fake QD globals - CGrafPort colorPort; - GrafPort bwPort; - Rect destRect; - SysEnvRec environment; // Machine configuration. - - oldA5 = SetA5((long) &qds.qdGlobalsPtr); // Tell A5 to point to the end of the fake QD Globals - InitGraf(&qds.qd.thePort); // Initialize the fake QD Globals - - SysEnvirons(curSysEnvVers, &environment); // Find out what kind of machine this is - - ComputeIconRect(&destRect, &qds.qd.screenBits.bounds); // Compute where the icon should be drawn - - if (environment.systemVersion >= 0x0700 && environment.hasColorQD) { - OpenCPort(&colorPort); - PlotIconID(&destRect, atNone, ttNone, iconFamilyID); - CloseCPort(&colorPort); - } - else { - OpenPort(&bwPort); - DrawBWIcon(iconFamilyID, &destRect); - ClosePort(&bwPort); - } - - if (advance) - AdvanceIconPosition (&destRect); - - SetA5(oldA5); // Restore A5 to its previous value -} - -// --------------------------------------------------------------------------------------------------------------------- -// A checksum is used to make sure that the data in there was left by another ShowINIT-aware INIT. - -static unsigned short CheckSum (short x) -{ - return (unsigned short)(((x << 1) | (x >> 15)) ^ 0x1021); -} - -// --------------------------------------------------------------------------------------------------------------------- -// ComputeIconRect computes where the icon should be displayed. - -static void ComputeIconRect (Rect* iconRect, Rect* screenBounds) -{ - if (CheckSum(LMHCoord) != LMHCheckSum) // If we are first, we need to initialize the shared data. - LMHCoord = 8; - if (CheckSum(LMVCoord) != LMVCheckSum) - LMVCoord = (short)(screenBounds->bottom - 40); - - if (LMHCoord + 34 > screenBounds->right) { // Check whether we must wrap - iconRect->left = 8; - iconRect->top = (short)(LMVCoord - 40); - } - else { - iconRect->left = LMHCoord; - iconRect->top = LMVCoord; - } - iconRect->right = (short)(iconRect->left + 32); - iconRect->bottom = (short)(iconRect->top + 32); -} - -// AdvanceIconPosition updates the shared global variables so that the next extension will draw its icon beside ours. - -static void AdvanceIconPosition (Rect* iconRect) -{ - LMHCoord = (short)(iconRect->left + 40); // Update the shared data - LMVCoord = iconRect->top; - LMHCheckSum = CheckSum(LMHCoord); - LMVCheckSum = CheckSum(LMVCoord); -} - -// DrawBWIcon draws the 'ICN#' member of the icon family. It works under System 6. - -static void DrawBWIcon (short iconID, Rect *iconRect) -{ - Handle icon; - BitMap source, destination; - GrafPtr port; - - icon = Get1Resource('ICN#', iconID); - if (icon != NULL) { - HLock(icon); - // Prepare the source and destination bitmaps. - source.baseAddr = *icon + 128; // Mask address. - source.rowBytes = 4; - SetRect(&source.bounds, 0, 0, 32, 32); - GetPort(&port); - destination = port->portBits; - // Transfer the mask. - CopyBits(&source, &destination, &source.bounds, iconRect, srcBic, nil); - // Then the icon. - source.baseAddr = *icon; - CopyBits(&source, &destination, &source.bounds, iconRect, srcOr, nil); - } -} - -// --------------------------------------------------------------------------------------------------------------------- -// Notes - -// Checking for PlotIconID: -// We (PNL) now check for system 7 and colour QD, and use colour graf ports and PlotIconID only if both are true -// Otherwise we use B&W grafport and draw using PlotBWIcon. diff --git a/mDNSMacOS9/ShowInitIcon.h b/mDNSMacOS9/ShowInitIcon.h deleted file mode 100755 index cbf680c..0000000 --- a/mDNSMacOS9/ShowInitIcon.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef __ShowInitIcon__ -#define __ShowInitIcon__ - -#include - -// Usage: pass the ID of your icon family (ICN#/icl4/icl8) to have it drawn in the right spot. -// If 'advance' is true, the next INIT icon will be drawn to the right of your icon. If it is false, the next INIT icon will overwrite -// yours. You can use it to create animation effects by calling ShowInitIcon several times with 'advance' set to false. - -#ifdef __cplusplus -extern "C" { -#endif - -pascal void ShowInitIcon (short iconFamilyID, Boolean advance); - -#ifdef __cplusplus -} -#endif - -#endif /* __ShowInitIcon__ */ diff --git a/mDNSMacOS9/SubTypeTester.c b/mDNSMacOS9/SubTypeTester.c deleted file mode 100644 index 4845dfb..0000000 --- a/mDNSMacOS9/SubTypeTester.c +++ /dev/null @@ -1,217 +0,0 @@ -/* -*- 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. - */ - -#include // For printf() -#include // For strlen() etc. - -#include // For WaitNextEvent() -#include // For SIOUXHandleOneEvent() - -#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above - -#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform - -// These don't have to be globals, but their memory does need to remain valid for as -// long as the search is going on. They are declared as globals here for simplicity. -static mDNS m; -static mDNS_PlatformSupport p; -static ServiceRecordSet p1, p2; -static AuthRecord availRec1, availRec2; -static Boolean availRec2Active; - -// This sample code just calls mDNS_RenameAndReregisterService to automatically pick a new -// unique name for the service. For a device such as a printer, this may be appropriate. -// For a device with a user interface, and a screen, and a keyboard, the appropriate -// response may be to prompt the user and ask them to choose a new name for the service. -mDNSlocal void Callback(mDNS *const m, ServiceRecordSet *const sr, mStatus result) -{ - switch (result) - { - case mStatus_NoError: debugf("Callback: %##s Name Registered", sr->RR_SRV.resrec.name.c); break; - case mStatus_NameConflict: debugf("Callback: %##s Name Conflict", sr->RR_SRV.resrec.name.c); break; - case mStatus_MemFree: debugf("Callback: %##s Memory Free", sr->RR_SRV.resrec.name.c); break; - default: debugf("Callback: %##s Unknown Result %d", sr->RR_SRV.resrec.name.c, result); break; - } - - if (result == mStatus_NameConflict) mDNS_RenameAndReregisterService(m, sr, mDNSNULL); -} - -// RegisterService() is a simple wrapper function which takes C string -// parameters, converts them to domainname parameters, and calls mDNS_RegisterService() -mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, - UInt16 PortAsNumber, const char txtinfo[], - const domainlabel *const n, const char type[], const char domain[]) -{ - domainname t; - domainname d; - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - UInt8 txtbuffer[512]; - - MakeDomainNameFromDNSNameString(&t, type); - MakeDomainNameFromDNSNameString(&d, domain); - - if (txtinfo) - { - strncpy((char*)txtbuffer+1, txtinfo, sizeof(txtbuffer)-1); - txtbuffer[0] = (UInt8)strlen(txtinfo); - } - else - txtbuffer[0] = 0; - - mDNS_RegisterService(m, recordset, - n, &t, &d, // Name, type, domain - mDNSNULL, mDNSOpaque16fromIntVal(PortAsNumber), - txtbuffer, (mDNSu16)(1+txtbuffer[0]), // TXT data, length - mDNSNULL, 0, // Subtypes (none) - mDNSInterface_Any, // Interface ID - Callback, mDNSNULL, 0); // Callback, context, flags - - ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer); - printf("Made Service Records for %s\n", buffer); -} - -// RegisterFakeServiceForTesting() simulates the effect of services being registered on -// dynamically-allocated port numbers. No real service exists on that port -- this is just for testing. -mDNSlocal void RegisterFakeServiceForTesting(mDNS *m, ServiceRecordSet *recordset, const char txtinfo[], - const char name[], const char type[], const char domain[]) -{ - static UInt16 NextPort = 0xF000; - domainlabel n; - MakeDomainLabelFromLiteralString(&n, name); - RegisterService(m, recordset, NextPort++, txtinfo, &n, type, domain); -} - -// Done once on startup, and then again every time our address changes -mDNSlocal OSStatus mDNSResponderTestSetup(mDNS *m) -{ - char buffer[MAX_ESCAPED_DOMAIN_NAME]; - mDNSv4Addr ip = m->HostInterfaces->ip.ip.v4; - - ConvertDomainNameToCString(&m->MulticastHostname, buffer); - printf("Name %s\n", buffer); - printf("IP %d.%d.%d.%d\n", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); - - printf("\n"); - printf("Registering Service Records\n"); - // Create example printer discovery records - //static ServiceRecordSet p1, p2; - - RegisterFakeServiceForTesting(m, &p1, "", "One", "_raop._tcp.", "local."); - RegisterFakeServiceForTesting(m, &p2, "", "Two", "_raop._tcp.", "local."); - - return(kOTNoError); -} - -mDNSlocal void AvailCallback(mDNS *const m, AuthRecord *const rr, mStatus result) -{ - Boolean *b = (Boolean *)rr->RecordContext; - (void)m; // Unused - // Signal that our record is now free for re-use - if (result == mStatus_MemFree) *b = false; -} - -mDNSlocal OSStatus mDNSResponderSetAvail(mDNS *m, AuthRecord *rr, ServiceRecordSet *sr) -{ - // 1. Initialize required fields of AuthRecord - // 2. Set name of subtype PTR record to our special subtype name denoting "available" instances - // 3. Set target of subtype PTR record to point to our SRV record (exactly the same as the main service PTR record) - // 4. And register it - mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 2*3600, kDNSRecordTypeShared, AvailCallback, &availRec2Active); - MakeDomainNameFromDNSNameString(rr->resrec.name, "a._sub._raop._tcp.local."); - AssignDomainName(&rr->resrec.rdata->u.name, sr->RR_SRV.resrec.name); - return(mDNS_Register(m, rr)); -} - -// YieldSomeTime() just cooperatively yields some time to other processes running on classic Mac OS -mDNSlocal Boolean YieldSomeTime(UInt32 milliseconds) -{ - extern Boolean SIOUXQuitting; - EventRecord e; - WaitNextEvent(everyEvent, &e, milliseconds / 17, NULL); - SIOUXHandleOneEvent(&e); - return(SIOUXQuitting); -} - -int main() -{ - mStatus err; - Boolean DoneSetup = false; - mDNSs32 nextAvail, nextBusy; - - SIOUXSettings.asktosaveonclose = false; - SIOUXSettings.userwindowtitle = "\pMulticast DNS Responder"; - - printf("Multicast DNS Responder\n\n"); - printf("This software reports errors using MacsBug breaks,\n"); - printf("so if you don't have MacsBug installed your Mac may crash.\n\n"); - printf("******************************************************************************\n"); - - err = InitOpenTransport(); - if (err) { debugf("InitOpenTransport failed %d", err); return(err); } - - err = mDNS_Init(&m, &p, mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize, - mDNS_Init_AdvertiseLocalAddresses, mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext); - if (err) return(err); - - while (!YieldSomeTime(35)) - { -#if MDNS_ONLYSYSTEMTASK - // For debugging, use "#define MDNS_ONLYSYSTEMTASK 1" and call mDNSPlatformIdle() periodically. - // For shipping code, don't define MDNS_ONLYSYSTEMTASK, and you don't need to call mDNSPlatformIdle() - extern void mDNSPlatformIdle(mDNS *const m); - mDNSPlatformIdle(&m); // Only needed for debugging version -#endif - if (m.mDNSPlatformStatus == mStatus_NoError && !DoneSetup) - { - DoneSetup = true; - printf("\nListening for mDNS queries...\n"); - mDNSResponderTestSetup(&m); - mDNSResponderSetAvail(&m, &availRec1, &p1); - availRec2Active = false; - nextAvail = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 10; - nextBusy = mDNS_TimeNow(&m) + mDNSPlatformOneSecond * 15; - } - - if (DoneSetup) - { - // We check availRec2.RecordType because we don't want to re-register this record - // if the previous mDNS_Deregister() has not yet completed - if (mDNS_TimeNow(&m) - nextAvail > 0 && !availRec2Active) - { - printf("Setting Two now available\n"); - availRec2Active = true; - mDNSResponderSetAvail(&m, &availRec2, &p2); - nextAvail = nextBusy + mDNSPlatformOneSecond * 10; - } - else if (mDNS_TimeNow(&m) - nextBusy > 0) - { - printf("Setting Two now busy\n"); - mDNS_Deregister(&m, &availRec2); - nextBusy = nextAvail + mDNSPlatformOneSecond * 5; - } - } - } - - if (p1.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p1); - if (p2.RR_SRV.resrec.RecordType) mDNS_DeregisterService(&m, &p2); - if (availRec1.resrec.RecordType) mDNS_Deregister(&m, &availRec1); - if (availRec2Active) mDNS_Deregister(&m, &availRec2); - - mDNS_Close(&m); - - return(0); -} diff --git a/mDNSMacOS9/mDNS.mcp b/mDNSMacOS9/mDNS.mcp deleted file mode 100644 index 24cdedd73790ef4fad01371fb5629adc20362d12..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1002330 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

y~XS9^M)f1?MeRBR0gu&(Yd+N9qtVV zHse&!&F}~ZBM$9D0j8ENiqf(}V+8`-vB8avu{m@|awDCy?2R}woKJjhtIOWL=~@@3 z7h{bQD@GkO@PJeJP@siVs6WbFWhP^@xxd2BRK1VENP!%}6dx02dQ58#po+16+;uUn zm7um?XEZh*J(SPw4Y9xY@L|NUeEQ+x7s^lG;$q5da4UI!JbX-AQv;Jg0~PW)Gn1}c zb%niKe1ln@;?!wy;^c#gDY>Iff;=A2B&fZ}@swfmHZ_o411Ix_nLXSLF*Pt<2-!>Ke27~93}O7#KfKKP}i z(8*X2e9mNd;19tiF?Jf2f^zqfJ3by`r%QWmDO2XN;--JTu3KAfYweN(5avEOr} ze24FtTBo*`Vig_hbojH(Zr`|$&Sds^+)-aJ!y;&+%OG8u#?*kR0aF8}222f@8Zb3r zYQWThsR2_1rUpz6m>QU58kjT5(qkrP!Zom|VdJ$EF4PogYGA5qfUkn~yFJ0k;8e?+ znIBUFg|0ysxNe0SdFjZBh~JZuX+lqz{*$|zl_%2F@T$v%V*qHuk3vU_+vu?-x?Qq z&rykM1dhc7-hEhNyTBin{AF+mrw z3*0{=_#WxsEB$-9Kfdu&7%zYKQ9(YPt zi0~hl;lmZe{WT`=S8D`*uwLM=3<>;zjQ`~k;r`N5i6z~k9^w9CNZ^AKACUA1Dun;% zWxUTz`um54|9a-(NOR0e>FxuL13o9+9^n0ed!@S<_i5FCVMY%I{Nhu>Bm zbZwyX>Y&>Iy0di9(Lr7db>z{PcX>jxcVJj~k$>nMr=Bib-n5Xu8>qT@Sg% zNx5o5d9o6TKvzivtznDsL3t#CLM7__=&m;H+BSZ(Mx4hFE6(Qd8wF&Et#-KkKR+K;= z(FOXvgBNk18LfdEz2PXgmu>XA{gTcbB|3Mn-#hcFP#=a+F5h6gJ5=88-r;TSBLA|6 zzCJ~9hl#GQuQAvkn(_bHI}^aDsl0)|%$Zius;GDmSOpadC~5`NLR&1*!bn-clMd|w zh0+<@qD4^#@AdldSeJD@iVCtGyLc|EsHmvudZ41{dZD7=jjpb`>gM~sBr~0sHZ$!E z1=|FEeaXvv?|;0P%$NX0?^vrW`3LDV$VD>O3O{XOA>3(W8H-MjhMtAx!%YRzI<( zsaot}euExx}R; zqd+D8QeQ3WZ7kVHZeL^VYizq3j-4rK4=A#?ZKO_gEGJ#2ozV8cZ_xt|L+Q+2Cv9%4 z`pUxB7r=CAfVHqGbBDmN%pFd@J#&Z2lZ^Gr4Mr&AC6hNd-Hygb!^wsoE=JIs{8K!N z%lOwczU3`tgji$s&o!y>->B-Tp&VWqRV!zX#BzB~X#D3mA-R8zxy2gS8IAwM8A<+& zO6NIH2QJ%ZpMwWi%%*V(H}gZhLKPW2quGHcI^Fp?#!kHZY-h2H*j4lpyNTV!9-=4j zLEB60&70Bo<;mIpJk{wXdW!=@wm486B>ITH;$U%zI8^i#{Y8!#z*D|K%sr7$YK{;` zilcZqdNdu6p<cR;yf`=M8tevOLqZpk-JD-EH2?~Z@(9p ziOYE#-Id}h@qfH=?rL$3xR&?D{XtwW{>XfoKQZs*&&)-+iMcMfhz4f8+$wGpw=>ga ziMWIJ)BQ!gglXH4ipRv`yy5N% z@uYZ)ci%lNo)OQA)#5qvy!a>ca9$KIiI-{VuZUO0YhtZ{7=Z|upL_e;VS*;kFWpXVuZ*S1)@-l6vs0kX_OevT!ms$BF2hwylw6T zF;tV}4JG$Cht-K?Z4p_RfC$&tT6 z)8)*X+DZw@MJJLYOR1@?DNnAn)>9=RN8Y-MoWShX3t`Xb;p2UyW`#pFydk2cgo)(j znVE9Rs{*waXaACkKFgvkcg87}Gp*9Hg3@Nsj6jS2h^mq{duCW40BMtBp97;+*QPM% z?8>lJhIG^q_fXUOkF&gq8mPa6&`Oas>?@-T`GvVO6lGb2lv7f0Qn6(scG9ml&rLqV zMa?qg56D0C&}MPXH%p2~jc6X%d^;_2N{Ty1j=y+hbN!odr^TL$`323(P4{*g1*W49 zkw`BoN58fQYAny`iONZ4zagjI@|=-OIf>HfauW0+I5+8sh<@}iMAN*?WR4$0j`d-d zJ~`F~rB9A^^`udbrJ%IQ33gC>v%I_EoStHP%c6nm?QQzjZEuZo!dta|Vsfg=E&Hs^ zkIVLK>wcSMoio+?k)<1QtOcb_PPp8%#ic4I614|8RxK`_a;!X;Ryh`e(kG|N@`TNc zZ^FD$XAa{Cj0?;6;#5|}-(@!0FB?}2i{9SCRUs=uP0C3{PJ-US_~6+sXxJE)XunLU z+jG{;MB`(Pg|>5z*V0?x__wp%_-titq+={2zRZ@^r5w5BBp4}5N55B>gY$HOTj`JA zEH|fOPF3ZU)`g_ao&;ki`JwO}#y@@jK>5tTw6-_Rm_3!{)fPiQB5iUy*EilpPN?-Z zAorxL9VEE5Pv1DW`J1Cw%u2N#$bOYva>C(|#o=QlO5tyrnKvhFJ>Z_|czYx&XDgpf zWA<2^la}WP(xjYP-fdSst<_UP+S)-~j}Mg*#=LZND3QLrl9lJk>^Gy9U3*Y>paB|A}{H4qNB zy;IPvLXv3*={Wr*Bd2zHRmzu*L~^3$Cwg9$j&qZgm1Fn4qApJ=jP+SNU7yJM;-0p? zinSQK57Dd@HufPb=Sp_I=X`B@F4n?R3X#;czoc!?#o8~&elFImWhb|(_q_rg^qiuj z`jVDC$++hsEw(lx(*FD{WjWRc#m0?OmSf$8Poo@5L1~k-y*+VxDC@_m<?I79bVrh_L<+)V-7H^ML6{Sy( z{alPD6kC$HSJuxvt&S8LMbXTL*IG1`WX6beIUVr)ZBqWYg!8mynvu~NZY>-`$=K7` zp7SL$Hsdd}@>+8D3(364G#zqe%gyPi=ZW@nzSub@nH9g8D5u)$DJ=c;bUhzU@SHCl z=L2a{$Nk>Le$JOnJBy}?m@Do3AW^iG#9>)fjy*<~*jaHY!GtIV+?XWzHeNq zPkw+ix+HV_==PLb%~WkVmXVqqYlGtNl~a=wj=G@5FpY971*J{Sb~qN;p8Cn6HuUz^ zhOxlZrwwC{smie!N=n&p z@%C6%QL6RRD90XiqzT2Aq{kdPU@Wk+jnPRbCz^|&j()E$r=!Ns63oR8xx}1(03+#0B&g{APdP7a}PnnsnBi`jBMRlgR_!BFu4?oOT zpL=BMFDTl}xK}ov+)5DZdr?$NNz#5#p0b=I@5xh^V|lCG*f&mHj-{Zq)lWsx>gORX zRehV|jC*D4Y^mDQ=6mvX)=#6HB=5;nl@qF|u=ZT)d-eKB_8zvK_C!!KD#vP+F4cAr zlat^zV#wiC+4?)$l9^vn*|zuRwBotM^;bD=#OOq+#!Z9MDy?vWCF2|jK1Bt$q?bk55~B3r(3})wa_nr^D_ktRF0=?KdVh zLCYCo^5hn*H4mW{9GuMgO+O!4o0Fd3vZxAQCu%`SJIeMzGY_O=tf|C)X57rH$!+TW zw+{N9N^%<6sXDs{__$=4W4n#W{_R$zt$7L_$TIIDyUEX)`fGwyE9+aiO3IUlo;dEd zQ%RgH1>JrNS>;*8UThC+I}hxpADG)tzAf4I!1m&SxJ%RaqLQtL?E%{Zwg)=Y1C1j- zUH*(1bAYj;lrZkmKi~T6VjWsn9V~44?7C3@PWDvHkQiGt4r=Rc5}Y%ydtg>7Hyb_CINEKe67}ej;LqJ|kSY(hSSYP#EFk z4Q7~UgpX}DLo@y{zp?$O8UJX+-1Zsa3R9mIVRJjn2p?H#hGzUDX8glu{(r2O`-Nl2 zlJTSEJC7^N19I4J%Qh1`Wo)_2j1GWdY zxd(RI<{H=vY-0~h96tW!ZLESV-uA$6-vjz6=&V3_sJ8yMZ;Wk$?SWK0U>^rbd(WxJ zCHCXQlG0IyN$0f_c1jOSud53W88Bc5^XqeJXIJNxhh_~3*Mur&m)F$}m{Bovz@VH# z1NhZ>?SMeIazH0^flG)C{bR4bakJ}$$n5q4&nVxMVV)WCtWs|OlV_BAxWEkkM)>?P zGYp%dneVyH#`be7&2WJkmYLx|GZaR+dW{)2n4y_|wV8gknSQmI{#i5qvu65d!$!Vm z&2-P2>7H3;#6J@;!q?6HYd0I)Ya_?8=wGzu+~5fq?<~*k_L{l>)o5tuf5qH>Wu=k- zm1w-**nX?Q49kr0O*7p(Gv7LM|2i}MI$@-HBW#A|_Up@x?ez=HFi(bgd?cHHezBT= zMdBT$o5)joD#fBmIaN7TOj7EUI^kCulwU-VW1wT8sBoO>Xkh(r$KB)-4&hj--1tGn zAq4V1?|Hruxop>GMnq51{mmjFPmsdhQ>J01$R|WZEEJhS7%4yRxn&G*Q7IM`&C<*j zH^#^G6r_kWi|1}Pu!zXTZ<~no5oz93eA{v~xJ8u{FIX5{siN{>sT;0SPm*3?V z@3`t*b>b@_ywVF|2sN<&(u*TvcfrDXx@?L>7quu1mmWGstVr6_TL@f|Yo5fT$Z4;zv7DLGkMVzId_r%h) ziggMRcLr=UurWoKS{u@(thJ5Z!UOshaEsV36=5F(U8=gbG}a-!T<^FRDDuablk-QG zjH<5W_MqdJsQd}Rnpy3W&h?E&zpgd8xN+sKtqatoWR*)OnT7Si^4WFfZ?oI3qP+H& z6i$*O5qU;#x+iH`qW138sAKy+$z-1sxht@juIvFvPG=ncNxnvv;mN19Q`#QrbRO8z z+M-nZ5=}0#^uF8y-j)@#mT9$jYxXOma-*ZA(d{;eO^j}H?oN@x?pJh*YHYU=UA|Qdvs*WGS*4Fv( zf$t#s7f-}&mfjgPcGQGeL|azuzK1=g(U$gNr)|3j6nPh7WJ`1`x4m#XhwXvikO$;d zp6NGY=bd=|F2te}5BYA^bOz5`a6 zN#>dL%QM3)BRq7y87?qGzY!j?%nZY3Xy!Y3v$1{fN;6zwhGk|r&H;GraNev5r0s`2#+xL58iBS4~~qgo*K&GWgoTr zyt~2X{=?1vhebm({~&XF&`Kl!plH0`*gm?!49ko#*GzYmneQlb|50Z8qlA&}$gmlj z+ea)jw(}O4VV(^0(lPJsrl%uRJMv!E=bh~c08t0we zw5t8{&Td-OIPZ-0X7kRBy~ez=*xuN@Gh?qY?~L^}%{#m4`R4P^ZhF29Z*Dp7?55{i zo_9vPMf&)8XVl8Trt{8jdan7rGtxAhcSa2DCVt+T5feYlti>3#il28znr8FPj9l^a z&WLGs-kDL;dxfjy(Jvx$-Wjv+j2UZ7DJHqlnV{UTk)p5fc_+8FNWQ?2kUQ zL_J+U(T|ja^?x!&U*>QwWfq;BKS#RmSu&lm?z**c-7#LSn`Jr7vig#{nFMNLuqcqR zDDBs095hbPmuhs$rAMFqaRr5ae0>UrPZ&<%AC*6$PgnAUBcZX;F_V@PD#FDMiN5G7 z{E3EUbe;^QC|aT0m>(izwugl)V7x+nFQYw6W_74KFe}*Z$4F8Od$B!Wdmzmoh`tNJ zxYjxIzlo!J6NwCAQk_^}zICRh4f-C{*S1hnO=S zBHHWs?Ij<*q7kzzt19?bI`c*>zo@>wsBG)86+Pf)icf6G5c)Xt3Ww2i=6<{7@h#Hv zlYQ}V66xf9{Km#~O}gZ%*jRnHLx76B(kwNHhE5?oXf8BU>-zZ4$Ou5a6ViB^}s9@ z-qa$Li{N6o1TKZ&!)0(e@NyG{*P19-!T-SmxEjueYv5YA4*q~{$P~yF$P~yDq!H*M zUt=PW$@@fvKo{QrAp|l7G6gbu4~P)R6v!0F)L*EAOo2>+Od)5}+zt1@U*T`C6z+m$ za4*~kcfg%+Kl~SdhF{uSs;UDlYJOV4=QTPk|3jYJ@Mt85Y>w5SjECg(4 zMuHH3f*at^fKLQ|;5|D++ypnnEzkgq;8wT|ZimIN1n`}B3Rc0>@C-Z-Pr|dX8lHp4 z;0bshzJM=*c?#m6@B+LDFTu;O23~8(|ZC3ZKE}unztMKfsUh6J*1Ia1itX9~=OE;b1rf_J`hZDD;E=kOKo? zAPj=T;BXiWN5GMA6y!oLI2wk)P#6Yf5P&IA4yVFosDL0$g;U^km-dppg#3;1@)*ePX1D#dQXaa6x5^M z3!)wc^(d%EK|PAyV0YL9dcvMSyU=?YdlT*h`@#swhXN>sV_`UqgyW$Ij)6QF1*2gM z6hjG&g>m4A6JR`)!UXsooCwFkM3@98!O5sXojRyX2X*P7E*;dNV?UtI9le42bWooT z>eE3Ra8TEd1AsbnP`|u?OE{=s7xEp{v4c8xP{$7H*g+jTsAC6p>{tTSvEvS)o*mS) zgL-zMGw-Joj-~Jyz(xnQJJ8jI9S+)`BM@Mz=!Y=Y#<+gb>Ua% z$M6YkgiY`%@%Y-g20nx5;05>`zF;5zcYX<9!Pl@EzJY(kf8huC1%8E};XC*d{saF5 zjWQI#ZkGd`-~u;zzzZq}$bd}P0lGnVz*jDO<=PQ;f-Kk>b^+U0X<<)*o!pvFc0R!d^jH>FbB?sbKnBF5H2RqB*IJJQusYw2A9JXa3x#>SHb_m z0=OEkA>T=GGV9mEb?^tc9{x!DDTL?2LO2V~hCjg#>^qh4&u}B$1UJJiun3mIVz?9T zg1^A6umo<0yWt-A8|925Tn6{TeQ-ZK01v`L@K^XdEQf!R>L=X1uf~ zFKx(6d-BqrycYoN$9oZ64749F_3fn%d1*sl+K`tvgS z%z&9t1+$<6sv!jEpyC%5AE@Y};sX``sQ5!gPZfWu_(;WH>S=(_)Dz((z*j21Qt^X| zzf}CC;x83{srbu7dJnQZ=<7iz4?20!&x0)<^z~qi2OT}==s`aZ`gzdLgKi#l^Prmt z-8|^#K{pR}c+k&-ejfDmpq~f*Jm}{^KM%Tj(8+^N9(3}clLwtV_{D=>9(?7&ZyxmX zNd2f!5B2GxK0VZ@hr0AomoCz~kmW*O7dpAn$%TH*-4rhLbzzGO9bM??LO&P!xzLZd z8wwY?xzNppZZ33lp_>akTZ|;8-{g@?bcO zfP5%`LKp@k;dm&5Q7{_DKrxiSSQrO>I044P5l{*_FbF2V?|^pWnFy1B_TxDjPC@sp zVIlkhZh$|-pWqs}95R8ekFJ3b(=Suo#xW9dI)&g}=a^a2MPS_rPD_Z?FvR zh5O)scmVzg55fYt79N7X!*cisJPeP(3V0Oufdjw?2f{&+4f{eb*dO{pUpNF#gF~So z^auRoqRw2@j|)G!4hHJTMg6&`FBg7vQJ=2UVKPvcF6z}qy}GDJ7xm~WgWf=0x%P(r z;25B8T>+qeUDU5@I8eVX>eux+JOfX`YIqKwg_W=ho`mP&pYS5Q1TVuHcm-aC*I+HY z4llqPunyjYx8Ps!HoODx!h7&OtcMTaLwFiKg2&(q*Z?2HC$JGV!Kd&Ue9k$AdQzza zl{!(W1NBj$K2+*XrH)kUOQkFo`&7zPu|vg16`NFKso1Pyr;5!gHmlgHVyB9oDmJLt zs$#2(ttz&v*s5ZyimfWPs@SSxr%GE;u~nt*sI(Uqn^kO9v024t6`NITRL8rD1nJE2~L6& zU^I+{VmKL2fm7i$I2|TK83bSol*7$X0k=R9rouF6fJHDJDq#kUgPBkSW1tFVK{bRR z3}-+MeG<|-(btLoPIPplqZ56d=4!(TR>u^mC%06aAd%=R`jz`Z>|hiEd78 zaH5+N-JIy=L?8U5#3xR4a-x${>QoDLFdNQh0qU1!hz5i zjt6||q;8$mp%dRasY9o%<8$FWm_*J2Z4g}PJlsY2Z;)Q3XdD%6=m8&IfI1>Y;wi9&rT)SE*6E7ZBbKJ_BF0%(Wo zRq%gsC0q=b0c})W09V7cK$}x(b1F8e*sCH##U>S-ReYje1NcS7Cn`Qs@q>yVRQ#Y~ zvxHmcaDVxNj#Dt4)t19qtw!XTEred3lZ7O!D*rQ5&UIE&Q@*1p#*I_lh z3@^YNunyjYHSip~2>*n);9u}IyaVsTd+6ieMy+g>i5KjE7Q~0KbD1VIoX|lfVxr!zpko zoCc@EWGI6GOo4K!fFMkTQ7{cgKp{+rN|*sNp$cX}HH6Up7x)$a2O7r&O-A`Q{1<*; zTLB^+PPTuB@8C!H4{=%(hl6zshc`?Q>Sj~)J^@mX&Y|ZfSdNZV@Z)T^6%b)%OXf4H&Rjec(IcGI@p_{RMz zybk!ojU8@$=Eg^EeCo!hZhY#-ry1Cufn6EcoPk{#*p-2e8TcauA7tQ<4D8In&WwFw zKiD69&L)@I2aCrL!lq^ha4CH17Q#x2KYJSaG*XisE3Tfa0DC) zM?o$e4MSik?E_m>>fVbDDs`<=*DCG6i+xnJ@M51z-K*5SO1-PpyGp&Q)VoT(tH@EY zUBylnc`BJyY*q1tirp&usQ5+27b<>H@r#NdRQ#Y~qlzz7e4)|?Rs7+_zh3;R;ujUa zsQ5+2FDiaf@r#O2RD7c14;8yr{GejDip?rMQ1Ow9UsQagV!Mj%Dt4>btzx%|-70pg z!{8V=7LEh_qvpYI7yJ^0jv z|2_ECgI_)P)q`I>_|=17J^0mwPd)h3gD*Y!(t|HO_|ijtdhjPAg@^j~;8zcR^i^Z0u8VTZiU-mG29J*fxp7v;2u~4 zcfwLw2KU1K@Blmr55eDIIs5}2hDYE&SOJg1WAHevgeTxhcnVg*)9?&D3#;KScn)rd zJK%ZvC%gbJ!b|WntbtcJ2C&78j$Ul=qN5l6z2x&^pO-Sc*ylxmFXei%$BS)VZ1ZA^ z7hAmO>BSx|_IR^uLJgZUj^**z6o!^+wcy&3-7`E zupT~u58)&D7i@r!;S<;ho8VLU3_gc1;7j-lzJ|^48hiuz*Nb1h-@?D)JNO>{13$oz z@DuC?`@o*CAM6kN!tSsa^aLODf&(BM4upfC5A=nD;Se|!dP6_x4>>RZ2ErgX3=W6E za0DC)M?o&^4M#%{*aL>ZP#6Zsz_D-~g)kD1hf&~%5*QDqZ~}~mu}};X;CC<)Cc#N? zGMoaZ!f9|iOokJo3<59(%Ao>+Fcqf3bf|8p{@ITO~PXP)zzzHsJg9q5@ zRUrd1N&h9`4uqe;r>u8_?yw{51X-{%>;k*e@1lJuKA@fy+KaL$P)`c&M4`PX*+AP- zdID`qq1`A4K_BP~2g4z7DD;E=kOKo?APj=T;BcS~D6}1gwxiH?6xxnL9Vul%{V3Fr zLR~7Pn%m z6zWQ$jVRQYLVYP`!dY+}OF zhZA5tl)@000KWs;r9%5vKY&f}F?9NoEUbp-;Cc8byZ|r4OYky018d+FcoklQweUK; z0qfvRcnkgoZ^JwAIJ^tX;SqQb-UsaP;9C!V^k9z%KYH-7=W@6P{tvE$Kftwc1uTH8 z;ClEY{0VM=Kf{f16Wk29Km#m-g>Wm}2DigvSORyzQuqtp33tKWa1Zvb!>%Hv#j7}p?s>7pL^9~b-hb|J4LyKuf(p=nQZy|tGv>VaSA z05)$IJIQt-#If~V)F-ytLj1~2LGyO8v(YXBXV?zvvL5(=8^vbrLabTw=DX9lXXL)l zUb?6U)-!m}yj^_vsQbB(H0{PdUDPMG*+P8AU{bSop>#K{Bj=rEJE+Tg;AaLHo41P{ zjr)-o8BesAF6x1=cif^~i0_T_#YDzo?WK!)Kw~hx+4(~JK)*9A#JRn?s84LOg_4=I zMY~XTFs>v28D=}E%X;9aowsZkyBXIJ`C9;c>6#u;_T6R6cCqV1Cvy8cxsxKN%ZbuF9FXSI~t1@PP zHr8dCF}ZSMAC8`S+3wcKqedo_2gtg8WP@FoOM|V?pYP0cugjxs+1B4+7Sty9I=$V$ z!7QgYNy`DTE?3WRs?0D>&rp##gTuY)*((e=EEGo$UJev+F<1uK!riLi$bF^`B?gf0bSTO?Lfv+4VnU*Z)_~P8R-? zmy8thYxW!=6U=cUQU0Ifk$LB2WY6iAJ!hxvIlE-f*)6)OXZD=Ev*+xWJ*QXpob2p5 zeX{2qqG#!!J!fFH{`^yJ@*dctV4xycQ=3!X1&;;&HMo=W12t1Z)syoNJ#;b=HKE#^ z>0QVd%$E?=Azyi@x;j{1R~f3V^##rhR0gJ01v5T9ds*%ULdx*13DH*wkEXxgu z3Ayuye~gx2*tkC8g9&Ax|GNjxh%FWZurL0)F^R>-pxeasQJ$%juA1=rpx=M(l7wP3+*q1VqGdK6i znYp^0TjXq3d+FjHkVk>N*dEw+9@xzs1a2G)-gdg!l5G!cOApw`z_#?hE!*~h?SbvV z1IBq!DG5|o_s_2i)YfhfN_D&r!)Mop`X3*x4%P(ff)yPv-dlF5~D+^YYH;m2B&IjujAj zx$=3~ua&=N{R+8WBv#DHou@y;TFx`9W%@I$McQ|pefj#bwDj`Fv~s&2WWSRpbh1M23qV<}l1vwwfp$e@%g4o?GB`z1euiyg_9=aOs|`jR3)@hSRy%0rW3lHSyNjn zA-U*8a%3qrwKe6*mDYNyB;@EvX-;5v>xHmq^ziY%QM1CK8twpFN|;Dao|!49+1qCcXlq|Kfg)(1e^HWu9el~sJaKFFUN{@8xE2EI%;RFpO4T7Ycl7f>G+EZLP#XL9p3>P(%oj-uNbZ$!+cf&b7#rBqkl7^hE+uj=Ggf0D+HhZedE&Hsg){V@s`)!tW zNvYb?D92h*+T?`GEn8fwaw1W`A;+r4rBjZT=h7<2LQwkTR9T*|{WYO*u%>Pf;|R5N zzWmUvaAj5eU1pR0tu$S|UGq@VN>Gz>QjwFOcQ8J9b}L#M(TVoURJuK9%}g{tW+^9` zofV;S>l^=M3Q3TrjQ!~t%Sa$bNI7!JNib5Bj()E$2j}Smx6&WKS#D0noT|zxtqVz; zJqgB4@obA0tr;f6L6g9X77Nl~1NIdph9xfplF? zZCy=e^|V$`32AEwb(K@CI@qbok>@uCpvIN@N`f`5pC5aqV~j3QA41k)I>zXdk&}+G znPlYXeWEFrU41bkP*oKQRr`v9RbgDvuEp$2T3*}J)|c!=ebzuY-1bgEvl>dK9i-#* zmyDd+=~XFTHWJB+nxE)-RXWa1QdW-L_lmkasW8@O?R0%2>x+A;a|0r#cROMZf)v&H z`Vb7nw?46=^F8Nl+jFrNo>GXUuKgu#doI>~Ireig^9<0oK<|46I_Nn?NA)Ewdy;X_ zLt1QYj2|TN{95;0xz*@&I^|d!6dN~A*&gdId>Z9g3QC)t?d|znJL@NlLt1ZdmQO=c zZ*Prql6?M_sy!CZLgZO2ZS7!|wV<@gN%px|JN*`kwu5A!i={!1mFH6RTf9A1Rg^wC z_H!|sP;5!&URgiyv^r8~6h$)|UTe`%k{Kh?<#fRFw@LZq63)|-X+}n8xV3N$C1X!# zd(M~4*o?o>%4^BpFVxn}PB;dX4mq;r=5*BaMEf~k?3|O#ir-9>vz zcd?)ICDYELX(HxIPC5pcS2HB=NqG&WHlMT^NrCZF&AG>z#nJv zOQB@OGLo5#pB6buj5(%7P7-5|X^~@(Ihtp*wgq}$zoW(+t%uHoQ|~ zFN$g@W!mq_Q`G-tZ*SH=VQ9RkXs11~ zd)Q>>=cFuW>()=qo>0wJfBU*#Kgr(1w$pDB8c6T$4^X7g4c*4hf`(i?`TVA zenDm1-k;No=Mo=TmE%T?PLyiglo`?4677~FWhAToz7X!t*vf62+V25{cX`j^Yklo6p%dhEVnYjGl&_MXl%Lu| zes!k@bOZg(=Y%eEeFwsYU@i_B-}<8DHSXgZ7I`AJ?P#*)Y_jEQvgK~FHUlW{KS)bGLxU)QI=!v6;gT9$CTPJs@ zJd4?vh}b%V0*y!K!1s0(iOc`_YrToU3kcYSpk$sFa?b#gXE_soC4 zu{mzLTjsyl4UF5)%*@@G7q^{}+4F!kaoZ~8#cz8v_c^BQnsa~O!z|yEdCbR=xb*JK zZ#I?1ZM)dci`#aRetq1w1O1l8Z7Xc+@&e`Ma`r?&aol7-p<-?y!uG(p?St9Qirem) zxyQ=*{d=$-zkm14K~sEj>33tB%~<)4%sn1oAGfWry;5$&_PjWz)`VvH*~To1`L=% ze?F&nc6Cm9Xx4ymO{ijad0p**85J`J49XcafZwXu4hV!R2XsOg@VkNx{S&diakI_u zAIr87KG2G%~Hs zBNw?Y%N{D6yIe1JD_$%5MZOb-cmK!ZU-o|Y!pr~Ot^bKKh z?(EfK-!nCl`@nH5`gd?SH+Ta2o6cX{;48iA#vS(VKbEvIzsw_T7$ls#J}r9GXrkXe zn#fzODJQJdvL-*H?LX@Ut^ZlCYDdj`T|4IdwHy1LeDzh{o%WFOrT)?f7mJ<8Xrk9r zO*!Iz%`xgx&3n=+ZI9_MYJJaqT|4T+ceD{#ZqP^0iH)vsy;=e?~RcLjMCeyyE&`%l^_xBvL%G1ZGBcOYH`?iUY1)j3G_Cq!O~?}@ zOCIbDnizbqHvP&sw9D?_pv}K`gLdX!8?+E^GrnSc9PJLhpt`~AzB z_J?;g?edrOyhX9RN8hi_zU_VOTJkP<_*3oD2S3%uT(dzt?Y7NY`2PQC^PkeRt6tT# zKfb4FSHIRQ?~(Tp5%}qkkAJ3Jv*I)D@_&4;ov?7DR=#Ajb{6_xx?0n&e@oMD{Y2Al zc)MBNfqzBrd)jr#z3SnO+9l{*eDw#~WaOT4->=&FtB|)=({5a^Y1gc6mbc$uwK{1p z`7VEGqc;D(jatDK>$OQYeWA^`=fB$7E0F!7rd^Bd%U{;>N?#dubkLnz)gRs>AAWvd zgLdv;H)umIcuO1i$Bo+L+mZbs_4A~rT|)lIs#qNvZDZd*X$7?}XqQsA=ic*?R=4yc zZP2-IX!%!upiTJm=h~E||ItFUjkBK6exADcv#XJr2S%IBs?&Bj>VB04RzE-;MQ+)o77P#a4FGtk;B|<*gCXHjkTc`=&Qq6hP1DbP;JQkkP z_L%mZ)~D`e?TE~LK$AYi%STK%3vGdbnr?4gtyh{^DKcImO(%5=g(vlgQ7Bj&EX zg*fI!dR z^)x^dE`WnD-t@=SpWnd1GMSS~RhS&PHurAT`zajdf;W zeXx9Xo&3#ryR?>9=aRxng=Ee%3Xqd&)3ZeF-KkN>_I>KfK5Gj5teKuR`CBc;k<%Fm zh?1`jWq9&w?Uc3$I-LjPpr^%>YG0zsC6+g`xC1!}-fn8447;{wzalEx98ZmIwmocO zblbCch8%XkqBGQD`)%mz{fhQ`&dzCjpc8mNJ}|MDEj^Ga92uj=j+&s4SonCinC~F@ zw|{AY9wQ&Lj4v%LX!ob9?Y@USrmU=*wSh?j(C^z2&sjAV#D)4(A{nllY%m@^H|G-n!|Z_YHh%6z`R#Cf2c zEnqDB7wDmU=d9TOcp)DnT&J%Wi9-o>vCaeK1Rmum8E@p>bEsTrVTQw=7wK#IY=mC( z^mTnULa!D2x)7%c(d!er&J%@jsHV$afaUN2tc1H^DJ+BAmB*AH3D>&Ep1THC!%Fwq8d;8TkFEC^ z;aRdww&#Aqeh*|ocQ_CZg#mB`l)!X2AO7qfTU%^UGnX(I3U(bh$lVH6wF}Zryj> zDQo9lcI~m-?tApybFaPk*>}JFeZ6`gkbU4mefl1J$f5oE=L{G)=&-{FA93VSxknEf zI_#KZkIM^8Dd!NLHobDj%&J+{q3{_swRN-4Jga`r*{7U(+Ub+aTHk-p+;h*H7ny(l z1s7g)@g;Jg$PdEJe#+z=wrD4&nx81&Y$sJ4oa_3!l z-}BeMExY%=`yY7lp}#Nx$HR}Tc=WNyS3dFNQ>&hS=GoQHJ^#-aUVQ20HLtw-+S=FO zSoh{z|9bnKci($|{Rba@wBh4VHg5X#v(LZy@~f{mfAj6Xzx)0_Km7R9e}Dev*Z(oY zF-7}_kH{}59C>`vsL^AJOU91#pD@03!tYL;IO(L54f|W)-!A*ZRr+~9ZT8C8ZS?4qKdzv#kFQU`@Cm~S{G;+G z^yx|th)qIi)o4|pBHr3=g)5u!u6;d)eo^30G&Hm2$xw=-qvT*Pz?d3s&WbS`Ve4&? z_vy?0+sh)AiBpJQF}G*QtPWKNW(C{*;3-an_K3Ih*dDMw(2@t_WPZ6g^b?jVkKq&! zcV|wH9&7*U;vSIaU5$l+Z@|cQF+hG*BR^ErzB!3q+?}1q{Ajz#6T-`xLm5(1TAcPf z&Q9a4u471CyKo!rB2YfA)K@w?EC==M7SeTELQ_lQ+J)2d4y$Q4d&~BK?SW420d?a? z`XePd&Y*7mB1^v$F>O{i{;Q#ro3FnlfN$b*ne_YfeZ`g4GlN^a8Lrd&z&5Z;d!RHJ zs41Twtnuk@N31OGa?W6Mc*uy^l~onvgSE4(>S~ABQ>eOJ8;Cd9E!45{iy+pEv!uko z)vpzK=rEyh(uAA@N9Fk0)e}lf%WEpba>7ab7F|v_I+dg4GD;@;{J|RQG%e>eVj~3d zMXU)fwLAD&assohiV~xTkN1t5CGTz9cg@bDbY0H0%Bfa@(jjMj>G&2OkXp%Y>%KBF zq@%6K`bi{PmovR?R{L9m(GINS$f-`c9Qmd%i-fdEOO-8mn&s+pW?41^3k{52Ny|y` z7)vB4f;#PNYEHTJSJM*JPh*MJYdL3EhOJVhDo3_A<6hv%+%c`6p!U_@ zJ7%Rw8upb@hWr*)8j7+kLdq#ADIQ^&h@HiiQ_OR%59cIG-SYktfAPqc_cX?)Mb5!$)b2&PPp9i07_L(Buq{MSQoird{g&X4ZG7--%L+`iUuM?j z)CT2G*|zG!D{ifCkkTN%j50c^FIhXiYDyxN{e>e_zMeAmxkKbgTXLwRL^h?e-*0Bl z30aqtj(&)gki)a&gwC^8#d2?%xwm~Uw6ZL^$xbQ9=Jatw6uoj#s{gb={tFtGf=a-O>B@l;v1=&(bHy`t~!8ax4XW9eY;i5PPKj-<#e|D=@w2DaZyyu@Y-2%5$#x=ls_)v__RzDy8~^dEhG}% z#qZnO={5OY=QiE9w_A>^zwPC|Jss^$x2F@`w@<9BKKw9WeeRL1Kcm}2z9%!$aX4L)a1hV)-+OFHB*&N{vS zILjYP?A_U(nUoZqRNR_Rqw#TV#c)w{M;pSDww*KDFQ;wuz1lCQZS%d_FQ+q(8(Uur z8~eu6Yc1UArv08GWjWRc#ri}k%dzZArqORn_E}T4XL}oGZKpl4K6tYC6e-Kuy7d#Y zC)s&e?X)Mt^`lLF{gmYxZ?0^^Wvfj|{ZV@oJQYhc&nnS=sZ?hhXO%X!p${%wLjQ3x zQ#+Op8~?1BW7!K&N54v%pJ;wmI^^JRen&jXs&hQBEB1|LLmX$`PPg<+tz`Nt$@GoW zA}5Kyaa!ag(Kk+uoX*%c?x;BxfexB8ZrwF*?6cC(_t7YP@+=c~PEY3e(fbf>m{XCO z9BYGOef^Z}NwTk>x*SVEX|rd0>+84EZ?V2{vVHxOi%JPKmuL*^NHFa}%++JJf%MZ;8 zS60RMJYh@|l5YZEu=#Y(_3QmF3k|4@lD{XGYlaH*#r{V>!a0{|+eS z`%~R-)z;4w(`HYC*8<2NO41HOtv$6*_J@?wuh{5~qJ=-H+9Ugka_O+&l!a?8XU>#P z4^~+^&QQ12zY%j!NBnk8ipsGzN2F?xX%2GgYAUOzwR%cOTRW($oNCpu=QlwnSX zy?e)=Thgv)RBilCqU?evC5MDXe9|yjQ=90Gd!4Jzo7Q0dxYEh_fto3y>dCwoXEG5r zq1qhFep6@i#DqfOk@o0a1b1h$%(f+U572iMvjUaXsh9K{F2RZJ!$ptCJkNz0xrZ#v zI6BwtK{`dor)MwAy+DY$PG^0Q>m^0*&kcwPx$}j8jFw;6xIW^831yz+^3K=p*5e|Y zHbxtfky|E2L=g*fdx-^eRp*jN6m9+3i15u-9ZMd6Li=$6FDbvSm$q{5d3(#2BsM`+4iVN94^H-DLb2?ebYNq}(FP&C|=JtXR48wasR^ z4RhZ;e9i?QF326aN{FEs>E&P8mokwvH}}bzxw@QNloNl$wP&lI9uIJpm?yu?wB_F{ z^h9(D}A#+XJ=-whIq5?Ul;2qGV4~H+!TgR!Ol7hOwfQ zFkaGM|FTdlNtap~KD#c||M*~auqIF!-0JTi*bZI$j>r_^d6-%m&TYI zd_Ezr5Ub3XJej7iOtZ2v%`HV)4DE=e&0?y=h1M~YyihEQPt%j3DREtV3?)|(7pGya z5M{*dBQE71yS)+PDEAz2-SUegVlU}o)|d4aN)M&M%(+zR?Gs8rWuY0fT*gciN>8Py z7)bn5(N|fnEN6X_vPtAAzc|W7t|ROSi#+`U;0tAXyi_DZhw$p&I$yXp$!%4TDlcA3 zV#54G?@JF3Pnn>Al!6z$Wp|PcJH|X1plkke1-MlD!zQaMcYZ>7Na#KNYuG^kK%U)(sen( z`nFFB?A)&DLe|X+l!t2TyO6=QskR4N@PK_BwBVGMqnwIdqK~tLydJll(2ngY9++NN z7alTTzzq8HIkmH^bIL=r283%u6|>9hY6r}ym^olj&Y%JOXr*>QAY3`16MCg-e=;xQ z=sTiDu934Jm?_XR5)i|NIj_Gi21H)2oaOLqT)&hnye=#=kM%|y9Z};5T`sxN zcshvfiY02y$L*$`Hg@iaC2qsY_?M*Gk#HKk%s z+d^tILKTMm=yuc4VVs*~q&!7OBBqA=oB}hcLJ65duQnN@uka@tnyK<+DAnQKUWgqG z#q>#Vf>8z*qX1c`xQICoJX|@@QH&;^ip}aQ57DLt8roCc% zpzUoS&Z*73*!r9ox854fKh4^$EKd?0D;M32=Jul_dVIy#qmfn@OgcHPzHei?CI>`J zPtwsWm5j!8v0bv9*aqonk;-naXfaB3MHe?;|H~iJ(WsT4Nt8!Y#od^$NqI53(Q@t3 z_JHjH+XI}e^6AZoeebjUe5$B+})RE|^5R0@6)QBAvV+ydjBn zBT2U~iF8GzThW-V$tg6pfnP?E?)D_ojV9gGjpyfB5kxm$7bs7D(fXMr(iM~LrXI#{zx*}MAm>#pksOmF$Q-;2tF+Lcm zGSda?WV*nVs$jR@g)4Xt?60gZ34}9C0yBf7Cdl}V;T08%5~z{sDk|hVHg-U=F=KdC zK{sBFQ5P()E3B@onbS>CYJ)PJ+>rXo*veq7_k@DNnwn6J%*ZRu^FxYKlOfa1u4hv( zu1;47fB5kSeHY;qp1@i~QJ2Yd!Md?QX}_c4R=J-ifBOE_`WhSf`2?Ac{b9p@Vx{z- za-K4exzLlv;bO2Xn{;I|oibNBm-v7ftej)6PmyVaW5<&5qvbE`D$4_Pm7x}XPSNB* zXjYx}rtJaS1GWcj57-{CJz#sl_JHjH+XJ=-Y!BETusyJ?J-{eUMoF+{TF^Icb~~Ti z%5Qh-@5yH%!4pXFZE8&(5_XJp(yD(NR34IsGTL3lHt>JDb=^uCx2gZPl1+B8ZSa7r zp!oPn>}5YM`QUL%XXP%_cHc*p6pAVFmoy! z`z92*`tC+q6L`nXlX`g#-}WeSjp-gMr>P!p^f)OAJ&uUwZ`8wW=rQF_v3-wwp22o( z8+MwE8`f}U06m_`)jTf|FLGB3SMK7f$|BcE_r&rv2zTTb&oi`_w`3f%!9--r6%*#} z=d90-9JfBVK`2G8BlUV*FwZ?=Si{sRqfGaR#xm<_C{wp%ScF++Q?BsKe7P5DE6MwW zESJ0^hc$#lX5NvFd1r^oyRV@Sy57PMnOqe?eS3fReuK+gnfNyMQuOLeLub7o;r{wN zdI%RfIR+kg?hi9vq;XU!%vPE z57xI?M_>FiPb+e)*;CG}%Z)5^&PNw!z6~pKO@C1L4f18XUAVvJvS?e9`DL3ga!tNk z*ZCIrei4^&Ob}j;8GQX@J22aa4_$JLTnBzH+y7EU3^UvN%|*>UB-sFtxns)U+3KJ6AHUexMln5N->U{&Nw+f`5IJ)C!e-6rrbro9L_f($OlzQgMpgz>23aD zYiIOI7qZFAgswnpzsQx>`jWy)at>ae;koo(>t%WJdjDG-e=~fs{rt7B_1jNfYzs}g z`n7&r_Q4{Yxq;3*EH)sA+l znIi^>fntz2O#daw5#mUG|C1|@<^(=e3=_wQW5sdY%?=kML_Y7CD-)ll3(cKXLF<11yDQ?DH>L-2a_Lix19g=sk4pqQ!mQTyn$s+n3xhW<^8qqgFTc z&h<6yI%OI0FTnC8HwxNKFKXyL z_{SwT3|ZCC`|xbGk&)YP(c(jvHuOGnQ$z0~e1zW-zYMNJhX)od&Qal1wg)!!KDM6q zT*~ahb_M$vQ1-Bf-ou6w4n`j3A3Jx^;)7PB2jvVKII4PTDEhTX%D)snkbUIpB{$?Q zq?|ffv*dZ# z1mCP=9iI*?K-XnUZWz3_q4&|m9lccC#ShEw>a&1#${ltE>%{f@(UblBQcw27n+faT zQdsE8{^zN18$XRwME`d;dHTOl#P(`W_VO~1-0t^6gme!Or+TuV3==MeGPC6*6ZH1< z`=&uJuirNdjPUD~=C;oWH|Lq#%gk*v-KU$4?N80^P3Hbh1C98NX84J@|6_Ci$63by z4Pi4BM);AL?juv~hi3W@2g?0J#0P7PbRU@dtY2?zuh;i``n_*%zaKH;-!s#{=QFn7 z^&8TP3XfVTpM)>+NGc?n$-E3^HU0{T-mKov8 zX1bTnbT66PFPYmfZZ^`rxWEht8sQ6T^sxVdE6q?CVRoJo9$=*J-@C!s?zPzrjr{$6 z5o3FQvz+~X#`b<@{Jv)VzGnPBVPpT^E6vc1-)n)fy{8$!r!cmAmKot5W;uHdG`4qd zFvBb(+%0T`JYUO-C+Wk&ux*g5^J4TFj zJNk^U`${t`Gecp7-5Sg=&j@!|X@;ggndbfsQ=beoUNzIHej~jqjL^Hx49mc7bBE1spAov&m|?^W2O6Psy&0PMoo2kljCT-^2OM0VjB!wLMO@+b7^Nyp$sW0Ew-OoZ>!qae&oir3%AiOR3(U+J%#Q6ML~eV0*y!fbIcx<45K%4)|2}#xJt|pS`aEY@@gmp4Fd!Fo~f7 zLV-jvqH#zf2M9q){2lUNkckzG*hdT(uw+?@mB^BiWFmt@WJubyO?z&S_FPJd41~HZ zT-_e@xPumla*(zV+?FPRrmgvD35E7j4i5T5^}U%L{Uz4yO1qI2dDkAz&di(l=Dm3{ z^JaHuHuWu0>6@=ai(X9dq{d2yNt>-f-{SUeRTlOZeVr=|rb(bJ^?lyzjM=+%~r{+YHzVoz3n|EJAY7S>Lw4fki`(G&>-XLsxM92$hG>=?V4b zt_i#zu;}33F@z5OdJO{*bdp(QW+KH)==3txfKdWINi-daZA^$x1d0;~1_XKxL3RWI z>=eT2^f-dS%-}-xlR0O4it#qLKRqTrB$W=_Ku6-pfZT*HA{Xg13T-}}K?}%aTYx9RkCQd)fU!G1ZVmy&7@^dThE~Dn(-z8%Qf!qBRAje_B-0UT6G^7SG*yx|o^(1)LWwa~DxGeZv;_og zH6p1*-zFQr0?Ts39Q?2)SVqf)JD( z;~b`8WG%vibUFATbn^Ai#N%eZ%8-p8fp;^q)sL@h3zlD|OAb2loV90Zs~&9YjK=zFlifOsZBA{ zm3>YjnGTzwgiR!w4%3vBQEnn_R;m0H+H=+%`AOK|V&@c+>73g7N$?|f9afI~L}2{L z%GQ@m$KxjF!eudTWdmx9^OJ{lSjG0770Wpb8ysuG($;J$Cj~Zd&p*5SnO3+Qb`_DE zfDJAO9S}Gix6#2ej+f0u+ZZciE1gbHwNp!sts=%YmW7T8+gKJlB5Y$>=oF)EoUb(% zjy$ayXPd@Jn-$NxK`HdOmr3cX#k3z{Lu9e0LK+=5LkU}7GCv}1ed%4>!TrPA>@^Yea3U>$>O`qs-PBpV&EbEF*WCjxz8R&yj7=&&RwRc@*L zu&PKtIv!?+Z43DPu0W_Cp7_G&FB^Qlezzypj!R2V8JALT`TFrNH$$Z1kwPaAb4$X{ zwyi>EM5Kx!r~8>Whn2(b#lkL>Ijn5_2%J;V;PdzE+kJYQ!@1S5IeXQ_&t~vtbUVGQ z7o@V$+2UvJk&}%M^9cj2V~}BfO62BczfY8nAAw^5@J30{17D`M_F^|u#=j6llwyh> zsr+C&5np-RQ|1pcSI)HcxI9e3@x{&f8=)!r*t>>F8QJV0sr*oOfKDjj_HNGDCCH`+ zA@?Shfi0B|>EB8@lkJVIuG z$G;QM@Xa{m2ZL&zc4DsfAQWL4}V|ZcY~_x{tk2tXvLp)sa1w;F+=#Ai?xT|P%n58l;_Y(pf`t(suy@bZ^9Kg<{kV4 z$)z}EJ_HU|(ijsCTnMcvjP>p?ZZ#MXH;}^@Vud|y0)-7X464huhs}T+>-wRi2p!WN zCVGG94TO#WEHsG2CX!(z&d^FSbPH!tX%BDI9**KxaSRh3#Xs-psvf8WCMUri(^cJn z9Q+Se3m&*&!6(mN2+8ohf#LG1`wyUXRlA1fRqYy{RkbTxUbSnqyy}5cjzUz=s=9wd zO9OKO!x7|wk`3b)7v2YV-yKwMF@zs8gdaDApD=`fU{6|ChPloV&hVTc5@Lx!F$O8Y67cv5p zj|}|?1N5tbBL45!;k^B2hW-je|7=75*@pge;z@H2{TCSe=NbAJ82Sx{{)LAAOG%cc zhW=%SFxGq=1Y6MJa&)@_L7@}(in6T$`>k!A4URyU&)W&d;&ehpz!x+!?WSUqM0*p` zVSezL=}3!zluMC7TmoP_qF#sF8xPM&UkzM)eR`+Q>vcIpZl5=(hw~ZT9!HnQ_10}; zrn?XtP^-fh%|Wirbm^GMf!a-j$hJ1t(3HHdf3@ANyV`tb>|qiYiN)5&>dQ=agd!Xo zHZ4Fy1EuPRp5S7W>mo=$P^x<9sr|7Jhv1O%yBEa92ky9_e>IGbb$6m_lj@e8raKTa zMGzXAhtS$s;qm4;58GxfGdG_Fm{~cjSl?kivGJ^8#1ka z2BFov2>zYbfD`Bpn4aEhB6Rk0*U!j$SC~$fKqWs!pbGp+CR@O@$sNuxO5#p~0FyC= z$H56_OC?r>_qZ2PE=2;oh2L9>g5mzmB6JQtsh zKRrVF09Vks#c|R)yBnKrdh3Q8ZA4UjzoFFrOxGJ^ZEJ2itJw=*Y4W;4`lhho179#d zNp}Rqlo*Z#;EFi88TJQY(HULg>=*YHv0P>rYrPJqFG${3&n&(bZWRfnN1xJn#{S04=C2W}5~#-j0;Me5KTx?-XO z=-Mz{RH6aSXj@P6sERWOeliyB@ylu4HJpvmRp3^Gs|VKrt`S@lxHaIe0cQobu8bh! zCv~ul7p__47ikbGBcD>yxAx`u30iz+D>)wl3Z!#LTt!bf62-+8BY z(y4G_?mIh!AqV*h>MQ}LXEC6izgqLbHY0Jj`U z`!x}KTn=#eB;nHPH_0&4-VcEq5Jw4Y>4?GoGCooJJMb^#?{%;@3tj#;LOPDq87Le? z>DIsxE)QI~F8H|;oEh$K2Yf4dINdPBT?K9)#D5Qb6F9hZFTu}BTn!Z8s;wNh4ol90 z_|@P5zGNx*wW{dXCIN0&* z6wVdGaCmWZ#XtJH;oYCj9kx3BA^^TiQ`JgFq|v2&V~6`MeoPy3&{*XdKIryMx=Y zBL;oJ6=*re4RCf0#|?1XA>M(ObGOs`E)0WIv#jf{#a||OI~{oO_X+idNu3;}#3&L_ zB%nw@k$@rrMFNTh6bUF2P$Zy8K#_nV0Yw4@BLR4nlzfK;^Z1qDit=UCRnm8&b0QAT zZqamqr)=fEF$?03MRuTa*m+%Q(QIwP?;}ZggxhG^&)H3{a_xLt*H3ZU=pck4ZIt6W za5{knnC%*58{Mn*u0DWDESj!;1Qx?U+5mcY&KTTdSPU}?`6H0mqH%r{r)SYD8Z-@Z zX7C>ybwECzMq^4DN(R)(Ha1^(FUA8j>IjVyu6@U^OO})JIJg!l-}M9g-kmeLpSy8j z^4&Q|v-7E7Nfw$V5a9nNlRtJ>hSyN1R5aN+(9BEAZJZ_8ThDQ>4qDULrt9usog^l@8H>R1cmQ*-nCI4$0`zrk@ff>#s!GrSKbtMIr~ERVK?Y$_E%z5A#V|Ietzvp_Lxr25r5n~J6Xou zmX4&o`a7A?@!0op}Z^#=T{l)3#Tro zqa-tZte*~<7ZOdW{d^(ul1o`6k0H4bEu=i9g;baVDhsTgo7Wa{c)J}QpVx(r;5L^d z;Oub);+C-!o5{&9d165lVS=I4F)ql7@|=Mz*%g$N0-#7BR|#-<7Q$y1YRk@CX(;&= z37ifRfVW%l6kjE-sMido!5NEeJi8`c4l53fJU07CwCbvR!95zST7mDQx)mOiZpGFf z@a^DtfHQ+z2H|RO*w^b;n1{ga0k<36PH+R@!r(&SBH%pW0EgKRu3NXF>2dJyRt=vy z0qy{}an-O2@LmmmDd59?$pr6LyaVYvaAn}m1a}Fz#o%hdT?x(#t_R$m;J&IFR=4m@ zH2^qwg4+WQ{?r!ueH7f2;9dd;c-5uN-c7#vx4n263`cNaUxZVQQn+gH`F$T6cu$n} z$LTl?|Cb&=b%?7+6RbZ?$H{-`@Ve`gf2!+Pf3O(>c0*u@R1BJ1l7HwHk$>?rlWl!z zEvLW(vOoRdRBDY@S5jJ5en!Qavu0PGb@s>RoOAAZbIP2jw`}$FdVT&|0>Mzq76_Skp6`|poF_Pzgj{6GKei6_7RgQv##KmEgJ z{`=YI4*clHKY9LzgD+0}^k+YR=@*9%A9?weUmpF{uYWW7>T9pR@!Q|MdF-vf8GKeqSJ@ zXoyt#p04>*^g_*?VV$lFD&@{r0$3%KOOXI7ee-p?TLeD-`sO>8sc&#f-+U!n^df@w zDCD!;dYi35-{SUeRTg6xeVr=|rb!^V9E)s9NUX=IZgh3^ZC;cHTa*EKHoU67&+X}k z{TO|oP;iy9gsLd(Su9gpRJslpCklY8tybG9-?(FjLbNw^v>To5(yZ_Ewzsu818zTl z@tE61==eR%%+?Khn=8PEVIhb>BL2*kz)*XJ9xkJ!kA(_dTen`{+>7V7xsBOoz)t9F zc5h-4l7r6rw)IoKAZ5|bHq8zhz*v5qDM4&iD@mW9rShDISeQq%2#ptb&5y$~L&ZG&XF*;;&ELUg32+W|pd$EdCz zW|8Y19*@uG)mvO1W}9ar5i=G-pV3g$uy}FqS{)yA7cXX&MLs(0Ze2b)Y_ZEohe=2_ zI`DNzR{hA4#@j?}Oj7x&1oVWC-^px1Qt3oe=&&?fK05574(LdpcM!P=%-GhuZev!U zP&&>`=mcGxnTTXagb#?ZCrC2Tumg^K*^FZ-h$1{;9fb z6tU6ArXYtMf+-l`U9zC?*hOKc+G%QThfFb1rzK)Kysb|@tCp6Q5-2M+TYo0nc&F2| zv3!kDh192~w6~mZMZCkC>GO7Y!I_(Qvz)i*PA5 zSu=GNHSwI%*636N!mW2Nx6 z3JW5|R>?p`cAH2t9g#MXWI9Y!C28YHr^6(a7;~l4fg={!njk%u=o|CNzjS>|*hEZA zMNT^YQ(8X>etdy$={3N{5V!GIP*U|R0?6ZZMA?!#>UEM%zHCXseh0dKY3rzn+yXAf zY88Yq5m%574Xlb}q`6y~>lw);DP^rgh8hyWfRGy&AqYXqG0tHcM%E%MNSA{jLMLDE zOgwJps|?xr5qLKvTmAUDwqW^Xy5yh(&slqxwlX(MTu^N9OstJJv}BTLJU&HNcoD@D zvhc0h^%=R-$!@(@?sT$S@0B~9Vtj7Q9txATF_zjCGhNx|6q4z%8A{khlIbu_Ng3rP z(q@&)PoX_$&5@sk4K8+0A(_spt)B!xV%K5i$WH{ukF0Ec$#gt!a&}!7<5o7Hwm3fm zZ^eYxSqY`%G8Nl%R?JftHaOOVrL8zq4p-iue|GmXt#CQ)D$Ksn`Y1W*fWYCnjSiM^ zylf`g##j+s>2!LkomygS6*0E4EObQJ#4>!T zrPA>@^Yea3U>$>O`qs-PBpV&EbEF*WCjxz8R&yj7=&&RwRc@*Lu&PKtIv!?+Z43DP zu0W_CUbhEB`UYRG-|b1YVo#--F-zJ5H+%@ApLq|nL3+>-FKZL8235vd}`>3$~8 zVdb!Uv9Jqe4l5f!0_RjT`279)cAwtnaBg*M&R#X~vl)CD-A*s-1*vRww)mNQ1w6#l)Gyi*yNq?TC6EZm)Dqr!fXKl)VnkiR{qrEHhm? zR<_baEl4#gd+W9_(_IJ+sMTSM<{*dDO%Bv<8br3Wv4*DPef_KLcHPzHJ7W)%xJWFv zHdbF|vLh7X(6DI%8X727KlB6_n_L$``hil_Lr?9GeK-V%l;6D|Ha>921^uhvfMVQ< zs!gg}cAD-$$P__nXdXgqV~y|cm@K){JcufA{My)Od$A9AEr8cd@B*#`-oe;$iZ?p& zt1J5NdTq$G`Wb{)?;`kjRs&9;GhljptBKIr%UypO*Pc>zc_aX%#18yrrjvgw`N0EK z;Ey%g0ZULdpqelZSzV#iAu&ZKOZS_eu)3o@PqH&Y`U{ zrIF4Q))hV#38YCtX#;8EPy&hs6bZ~g3E(+!^1{6$cIVS5<+)KpHi-d)zu~O#^kiuR z`+EV2%td%p*BA0Fy~gEr1soyQDL;*%@C4{_!E$E&0gKKShj()yEK})pc^zFIS6wg& zYs`WtrG^}V&92b(R&<&1GH{oJy8_&D`V|?91b_1Wgg;KlY52brQ>F@Y^=N|ir|CHP zFCAWYUGh(Low&bL44PY#f9MvGfAKPF3a_R!lt?wpy8c@HcFpZ{gxtO<9;GI_j0_l* zXhi~w1QZD<5>O-z8X5(i&#9Da9A5sCobsg{wC zgX&xRLb>sWzd}Mz{DYO=I)}u4;m|~LD@E?PWG#R0#Yp5_mAIRRn`w~$G;qlzIj!<% zmP|>^iv%={E!T87x4OIGtfnm++Te?#>3x#9Y~|PE1)0FR!C>dq+=v6!)~dgJciGC_ zX7in>boC_c)^Z@T3Ge#)$o*-!--GWh=*fQ54up4VJq|my#>h^sk=T2Qom$BqU}LL) z2s^aiz`MIf{!l%i?9f`WVY1|QpjR2WLwl}CjWk2^wC8S^)P0Z=SBc~BF0R4*!0kcL zSTz2!2<&E~JI^|Rt_{=0`%aR*A8@(yTTz}dT_xpfPQ=04Et>A{l&#!1W@A!4ea#9`#*8=6ceqi6bb4K@bHx5j`I|pf2pC6|IWof~*fu9me ze|sVg$0zyKSTv4TS&FO>g8+!oEM?(B>1wDGs7nLz*KW~Np98qs zVHe`l1Yh*sITp>@GZT2y%b|sG+#sSHS0?f&%c14V(e;%?+Ecn`fp_4o_9h%x8@<&5 z<#^T<(;b9(WEw{r(>{;eqPgLbM4l+pM)vBS1$}uL$ANyLNY|eBfqCk%DRT9sDT+9Y zW(BFop&i=#+UO<^k5gNp#2E?zPQpiR1oXUXj}7ND?Tn2>-u)Oa($2$t6J6b> zDYvLzxXuFLk?p|qAhhS`w`0KjqP2hf(slwfg4;OAN%g`a5{7#yFUV&{%%Yk&AGdW= zWK2B>Wr4O>Yti&PMq~r@vEB~P*L^0gOE^E)d5fm=3#80@weunxq-sYcG3Zm4U_GGv zp@*`VESgK+$NE3Yp<1f%_gRn%?bc|^DSrmOjL-)6YC~~4fd4X#ANbOzqz7<1f;I^0 zXA%0ittWM?h487l`F)%gZ`nuyfLG$B!0NLKuOK_0-=WOx% z=XU{fyG;wY2pL;o=Wk+cN$djtdvx{d&^Dc*Uuvvh+DD)*RG93sgP=Yo8W`{B0Yb@< zPX!7azcgei<`icPC4iFo5Gvu3rAEaOiS;`>gCPg`3FJ6S5lblp9P}7)5f;+;fK~8N zGHHd~i*n@EYL^1z<-fMsbA9JY*$1Ll&uVerG4knA;>znOh)bZjpuJ6(Oj~ zSd0&m#8(toCmyyKA?M=b67=mZIAXZXOj*`rDyJ9+IUPv^h z_Vb0rOD<)RJci^#w2<+6o06x9|KfHV8&gzLfjaFkODe)i9 zdK|{d-yrm{MsOZZt%<~wPVn&*pI(s)gsucS{PmF;p=i~baYQfO@+TrtmYpP6vS&!Q zych4FN#uv{IHX5W$zR3@%=2+M5bn^510#>kJ`%0E>Rxb1aD?yzq8r$)ht z`%evn11g{LL%0VV_U*dm&qLbt_kx4;=kEr$2i$IOJHbIoo*w`Qc%Fy6&xdr&e|iA? z2UWvoPJlZAZd^62GJ~WcT&m~&k_q0gcn8vT;L5irYq$%8UW)QZ($VoIa@SGV^SxEl3N}Niq(dtS{%gWEFICIwQ%CpY?*qn3DJ#X&$ zAHU$ji{{POFQ~fMaLFeYRxi4A@sg#+nq`+=e#P=DS6pRUxoUOo|NDol&5kZ-w`brGYxc|0)+IZ8=o%YP>w-0Q;V@G80&bxN*`sAl}fBG|@-SfH6 z|MSonzW6V9f9cErI{cM;zIyN1?%Nw3x&MI&_dWFW(Qkb7TMs|-Z{HsK&UgR)(Z{~` zACLd%e?9T!_kZxz`2MGV_{@Jl``m#a{rD%(zi{xyiJ$)L=P&)@(BUI5zw*nYzxwrW zCSQH+^*4U|yEl)$_4~Kq`NJRo=lGxA{onWA|Gy7D{PSP_`nQk%9z#<3ud8opY+7@T zrTN;mE!K6dw(Hinwg2Pw8#->dk>@{idQSZNJ>+Zlvhm+Q^-8z=g=*dM32-lh`&p%K z`5|yGfqUKqt~(A#!BIR6{n#s0xgGq4a9aF6CC`y~o|L#>LfosW;aO+!X|wFu$MoVb zaoWUJAsdVr{9jta%nQs}*wEV8v{1jWv97%iJX>=^`@)$d24NeilzHwp2dU+VEOfzQ zBRB99_o*n&*vF*cy@(2iBD#DXJT4NUrc1(t!LVsp>SA|o($YzT$tIv8WzHr?CRiwQ zTCvIZLNWoHnh&A~CHZ@v=6eoB$sB9O%dx| zLBG!nAEwq-+n5lY2oxs} z3^)Y~ED^v?^BbM)udE5xkJw@uZ*%)uuu|z@edFf>D@E$%Mb*XltyLeNvETWu|CrcO!5%0g#DLt_%-skyTQ+SdAO^+MRK zwhfZyW^3_v3DJ?7ZU+Q;9izH>m_@F4csxFzS8s87m~EbgM9f$SeMUn~!{WucYju3g zUA&l87WwF~yLI{Ku*EJT9VQ{!=)ig9too57jkk%|n56Pk3FrwOzmwU3q|%9`&|zt| ze011D9ng_H?;vs$n6a&Q-Nvjyp>&*?&FZ-h$1{;9fb6tU6ArXYtMf+-l`U9zC?*hOKc+G%QThg&gGrzK)K zysb|TUQSC(36vF^tv?fOywmB~SiZ)nLh92~+FQ=IBHrQ6^m#kH;LJ_DSh%&ksRbCEuy(B{(_w17;uQwu-EJ>kd6nsva~9iO*Lro%SRX?fW}q2#L5TDWpSR~IHGIZ%R`d3p>EQY+lzF=x`bPLEqIvrUxA%(6^*)w`SL8ro(0^VG~KF!!#vjl$%JKRVqJ)_MA0GeiAmg*g1t{I;XaN68wl=hm|8g5g0$R zvh^j?@wmx3c3F&D*?`*O{0O`i6Iy2_l#a_(Y|mLSPg&UDSQD1E;!HVQd3*lZ-Oseb z<*=(T`$FrZ8W;ViLq70*v7KZ5n&t4LPvycEDN1t zw2kw%roxe@HREj4IBBzjov#O_(BobvrB4^reuxc`#hMCfbl40fY<l zjh{lZ^>dV4!ZsFb>r191($<$s$K%Y;`yGLG46^B4FPo5Tbi~e)a;%>S^o3c?kz}C5 zlAKhzrSik7BKhcem>sq);Pblzp?-MX4xdbI@b&uLo>V(7Ej?viO1+}?>1__@R>$V-RTDp( z!I#nP^s-)%%0_34pSed)Hag5F46u$thWROxo0t7QQ8s=Ajs?IQB|#5-ncmuq-AGBl zXhU>jsvoKRU^@|CdD~Ow4>DKIwDq_=Ou_NR&G;LkDf!sDhDjOO>>#Q9PkP8A8yVRWa#dkE2_&Xea4<`c39vpa>Bu-!~gX$85 zmH|9MW`M`P6VUL@IO7L{YMpjsuJ#}lVHxCxPmw`h4Ir4%@_9=U>LGcpn%Kx)Y}hs%)0-FroZBwIk&olZg%D}jb~OZ{ShZS+S&q|=;V;XeAlCg)^wMhc{{uM{%n-hKY{ipLcXs4^#q^ zli-f&s_s7y{)egs4_vU|lV>l4Wcc2|aCz1J2hh5zT|@J#b`8&}+7&IY+BI5U^*|{{ zA*yFp-9MqFfw_R;2y#HlhH;Av?}NMV4yv~p!jBokj~l{I7{Wg=g!dc5<kb8Nxp{ zgkLa(Ck)}A8^VVS;g=2JqlWNr4B^)d;op)h0DsI7e%lcKqapk!L-;*I_ya@uFC;r; zfq%#g83D;hhJJ(r`qe-Y|M%-~-u^N}e}$ocwxR!QL;pGPq`8Lv3k?194E+lX{RTt- zLPP(hB+F7m|1v`uw*edkThQWiz~Prcp%eFtvef|lt!$Wk|T?h@R)nSX~AXjF(bj;*H?WRFwTN`U=O5WGM+HTifZN4-1Fo}!AVrygd zWhOg95e^NT7NDVlQuRYmaIwjC5u_g|RXz07{@8~@a7g*x3u5B~cU;iF8b-&uJ5jYs zb<0lE9SE5s2o23cXl<W-ZH%iDRF<|gFoE4s)EG3^6N`TC$o4US` zZ|OBIuPfjPxlZ|M1Vtj+82So|e=EC4SemhHsAgH$UyHwN;dVOk!^4vWk?1l`PEukN z2`Ca!B%nw@k$@rrMFNTh6bUF2P$Zy8K#_nVfoYcjtcjS7KTb-X@414;Esm3R;@#M6 z(_1&(Xd`9iXZp&MooTPQ=`Mh^t-0x}W-oll+3O1Fo5FsNFOa&!R8;wt=SHX*P7uJN zGrGdrZ~qe|WCf`3CD7||`hsLdd*PQ=DWM{PQzZeV9f+2!nk=`Z3s+%BG>pfh^D2Nv zDS@6)$iJ$lW((N!#$cb<==Am0_yfM~K4&Ocv!#1$&1J^RYT$T^V2#7?t|>whXb>tR z9|zU9_Jwle4}XP(ocISTy>$+W`@*4#=2nW_bIDr%+>4ROxhiot4L8#u|7qZoNpf1{ z&n%gem=_6X8e6XEaBg*X!&yyRHnhPPMbrBvbJ@zT#|tuncZ0#sskspcs;yOj`R=ln zyUpf1QR(VQ*sbM2W)t4^^^yD2aK8uNThNpJrX6Ib*5j~KYmDsF8i~D^*r}D=0XDY! zhpC9V?3;ayyV_kr7k zp0Q~BWsy2GhweP<0J=6z7w!&zvbP&RjHp+1wIGw-(%ytd3jqcTYS06wn z7ERYa0*hfFZ2-MHXAJH!EQT3{{1M1&(KtVf)3az64VnfyGx(2999#>O@A`p#@6H+B&)qmM`R*K~S$%$-29%`*(*}M@ zDE;k;G#sDgS7XsQUX9~7VZ2cO6LDKKtG=JepTw=9xEJtgx~Hq5PM|Igz+byXQ+*EL zYLD#DJx%aM-<@O8tUWV{KGV+FION@r@j~7;wNby1=3SG_ z+vkV87xCqRvhGC)%Hks7`PzBQ?V56st!X!us~Qv??w5c+pX{Ope^?D|;3@4q%s0{1 zeVTHM>V@kp03O*6JP$&9j($4^yf0e&w=ZocFeA8)gPc?^EFxjJhw_4acEl{IiSuz= zH$}$OgHRS|i?tR_&tpV3Kp*Sv@O<58;<|+MW1Y8XI=?{5yjMFfqCu*5R1$+eWeL^; zsvmkNi^-z7C zkbV}SkK1}u$65%Vnw#IpY4NuG4UV%Byqegb2`hw^Ea>CSKO(Rr5cjnc+uiSJ zyZb%p^i?AXdMD|mU7JWtmIe1Spqn?l1NW-pbu!z zjC;jfiTp|2w7=-ICeonXxL%X;zv(-q+|W<(eco#s`n=AvR2(!vt_P6qO}zvMsejHE zuYZ0QFt^*ZfQyi^1$O=>#+Jk`;J-&#zYcBF3HqhR`lWpY+CqiN9yw%CE@xi|Z#2(X1Xe;~Jcr|lcsMwSNTw^!@9bn5bDQodvkaun zGGtQ9(-e)$Sd5R8#8(DaCmyyKAwP}}PSBG@1a7Ck-4zVM=NSDyFPzF$jO;2rohX4> zCq|@)rP>l{LoC=`T8Fgv<=C#UHg(`PCT6~*^4S#_AGR${$EU}q4v)|4N;A0QW;iy< zli#jWJqgdVz#%YnI>rS#Ni(G+MO+-$!*TJSN?Wtv%q|!S4WP z2Dc2t)!?wN*R3!Qf!hOaH@KbP2Ec{Eg}_C?dB6bym$}>sWsXTVMnt6@%uM=qcO zQfsujlG3vBGb+xUHM{bxvp+WHoO92cd;Z5SxbUKR^Ysg=E;d~9iG|gRE?vB2sj+6+ zWtU&E{K^$qnO3e^UHkw3;cBy^%h~PPw7JK+|0d2!{G@-4^b@?VmQ@baSUY zbNcNA+wa&B8NBnZox48ysokIc%xCv}?(_dV^o1|}%iUl4^1lv$<({wJ`?dS_Mn~>{ z;K6+leSP#B-~86YkNn%W$G-F3e}D9`@BPQ)|M_1}Jo)_}JT<=m=^sAx-_JgG;733H z$@4E9d~xEZKl}Mhzc_UG$jh(%^60OA{hP^GUwi$H-~R5+V{iTb?RWn0$NxG0r+5GN zz4!m`gAf1wm%sk)qrbRWs8*b$J&zznU z|9%houDxvhH&A`ot*D!;ThR!v0o)p-TX8Ko3pn#eaP4upc=L#dp&xr?Dz}5b5KfEV zr{p;j&yy1OONe__H9YGKK5dpA``oGHv>J`;;lGBRDtJb{~o)N&Zz=tXYeC+^c|GxjklcwC@@p@=SI$7^6B z5xR0BzJLOQre74AL~8lfCFuZJhE$}?S!S9N9Lk(lZ1UxhDIt@QW_%l$5AT<+Yh71o zZ8Gw&kdmOvogx860@Ek~=(0%rj-OD#&ndNL*cb!}YNK%)%chVn7ztninj8F-fKin#7TZ`$dV#;Xr*G@? z`+b3sq9GE+d)j7C(F-+m)^*x)s1!U)31F2_E=2;U^v&1t(-XMh6NqoVQAI#o)zU-3o6fOCY&KD}7UhzPi!X)wg-k$vKKF2A&tM zs_%1qx?w*^pC=StrEC=|vKr2g9W5$d2MZPjGO4Xr+bQ2TWQIbtH+8fNeKmf4pSQiO z%^7g}bKjy%==eQMwyhiVHdlZR!$J^&M0}L1z)*XJ9xkJ!kA(_dTen`{+>7V7xsBOo zz)t9Fc5h-4l7r6rw)IoKG-c7vHq8zhr(Ru}^i zbdp(QW+KH)==3txfKdWINi-daZA^$x1d0;~2AqNgmIz>{`HfDGBN)t#OsIY`=S)vA z-sbkF$E1g((!u)1&jnVP)@JUYE=sOfPp22=u2_3(a*?^?diKNhBX|{ZH*h3xAkv#7paub-bt#{qV ztU#f3oSD!Gx;8Tr$&d(#DgGm#;8K-(^J}8&bK1o;m!1UJG|h`O}tso+jFOrr+Ir``#?uPz~aoUPEvD`KBLg) z(;2jYOtw=CKgB)a$H|&?z}Ow1w@aqOHqYdv!=8T1NQX&CHadkipUzPavYt=pNGAe% zPV^e)?JUifT0coT#WtUwqN7NOf@-M?_NGMSaNE()D)@XF!z5;)8FWF2^bDW3=O{IN z)^k?#_MGY9`YV)qyBzvP_$i`!`v$l7ip%w3(~8XR=*~-?T9dgowsviaKT85+nc-L| zysg55NU>EiP?6mxl1xXWO(dBP(^N^?c+%-G2_?o{sdV7j1-2$gPbK=seDW_{-x4+v z(^8R>j{lU_Pl6v`pj&zkurb7KJQkExeTx9{I2}>8WR7~Bq?0dOQn25Fu3y?ZDk8Ul zi?Lb-Axy*-q(cL%A{lA!mgag!GD%8V>yV*_gfJlFhD8WMP;!iOn1+$H2n*8X;D^x3 z*E7 zkHA|op>9|bA_M8>-l!XnBHDPHh&XmKIx96YT{Y)!d4!a7oFSI^N4mu!kIBuhZ zWgIV?iMBCT#8x_;o@%F-7+Xb*Z7d5N5w@``bVS(3vd}3;+c;lqDja!QGtM@RlQt{Z z`Fc>v`gAevhu9EVtf`Pjhs{vJ)|bqWNLybz9VQ{!_$f47KS#MGY-6#uzGONg zZGEY9JkI>Q-w{~HAe+ARvI)sXN9-Ia$NGstUzpV#Nd`JB$w`%4DnG0$l8=su*WA0u!H~Ye*XwtCQti03^ptTa^_H(64|6j_8XhTh@-Vj~{A}AQbVfv~2y(ig ziE~&v>|QMFLYc$L#*e@`6%9UrzrNk4w>g|!9hL&dTTFsBPIQ^4bh3Iex&k)?L>U#ZBLm$ z$Xq$o*5mRp1;-aR<8OqfILH0a$1d zhfO5IMx3FQWat*opwb@Rs68CTt>PFaI*NbZ(N#TA2~19cJEp6;|2X&`sunzO!Gceo zy%3V&djrGeRreo2>#BAQ&8ylqJgaI~w7hE9XnEBGr5uH*o>g`Kgq8;80)`{V0VNy8 zEiSwd?!G&y-eL$pW(YrS2tQ#6|G*I5ZwNnQ2tQ{C|JV?I!4RG>gnw=bA2NhrHiVBF z!oM+uUo(V%OR@m`F+=!mL->z|@ShCf_YC0=4B@|!?2rZiAunVEBp(_25eDd214aDb zufuu!%MAS$hW^=x{<97J=fsod8u~9V^v^T&FEI2Q4E+lY{g;v~OAYYMOh=4C>WZF%|B#HJWq{IA>1G2Tn#F@g2 zbP0g%hqynri#cQAIG;*Adc>WcomUK=v4eg>h{y9oZB)qoS|449tY zY9e&@a@X_xD!(&W0x(MKz+YxM`L~iEJWvJxSd%T_+T;#r7(a1$LV(F=!wZuW&c*{4 z?XQ-6$&vmj-zzEFdOlqkJBPN;lty~G6kRE&A^}+?ptJ#5#i<}uB%ny(G?W0I11B%s zD`Iy(jZ&T)C1jHrF!&qJ3Qtd#Hn6`JkjPwwH+6j>-_mPbURS^oa-H(i2ntVtx}{qp zx=(Bv(=ByR^8V%t-oHIc!*<@^M8hr$=UT?ccaQVFlg4*ZIL8N6eaXx(W1n=KO z^WEg=suou+R`eEeQL@83t`ze@dKJ0JhAQlHqdOrMFn*TFVKKyA4_h}lx+t0^; zYKZqgN#S-p4L90|wD_6+Y(;W2T>{p&=BBfnz3?4p zuPdZ)3j002K&-+=n7}Q{ZEvT6`;bGK(E8;3z8M>gsgo*@C zl?0S_AX>6&vfPp`T!kIcFdmD}s{j_I1bRXt|EijrEnv?ZgMD73)7M+$5BR$KoS|UN zmhP=Jml-dsf#WHHH4eYKrU*r#L8y#;98}-h7s`!4{1p;%;vcN^);T2Z3x_6}TPbqS zC2RR}FGeEgs>Izi+)RV~r-4f*$!V28vt&wQUL>GtY`Lbxxz*hbXEklv&<0-=P4AP; zWh=iPFUSPm4F)@>=0+T-wpRV+yUSMYHk1xJn#{cX18g2W}5~#-j0;MPN4@-FemlbZwX}-glDh{ea7r--_~# z=_)B-b0QATZqamqr)=fEF$?03MRuTa*o9VV(QIwPI7xVf+i2R)*-fuKt7&EV@erH2GqzlHeYuy#sf6!2#pc0eaEj$mXq>0xE3hi^#l9f zoini3%mnwwKw6o+UTtgD95v=nC>9NBhxt2nD%+x7R?QhB=SU&HnLau zEa=P2I1cm^MY{I156n}CO_8f7O;N;IG%H9w4(-s^*G4yac%0h$B+gI(a1uUhBcSJ9 zdu%wLX=iL4^6tlYA@7>nsNYBPu1V(Y^F!W?`0_wm_aX#kagp$R?Y!l7O*zQcv>VD* z4T=u;OTeE`c2R;qtcEu5ly)BGo9OC3O}Rz&!gUq^k8B5?2cbPjza0bK7p?uPO29ckucmtc|krqViwiJ`M9l{B4g@7C=0a3T8pOVF(MnFkM(wVzV0(|UBdaX z&RaB{Um#`PtDP6oAXPgmi9w&T1nU9S4?UE{WYJvmKGy$H4%Je9zt4hHXtzdNPWdzN zWrQ}kR~w4c0sNO?{J@t!B|U)C5wt-_Ka0@EZ9S=DErd_a&F|y1c-#I4$Jq#8P3+GE z7v$fu8`%IB^zr5&5!exk``U@^?)S9a{T_7ss*wb}lXTLqO{68ug8LcJ&70kE{ORp3 z4R^AiO5&#NuJaoia6`XzGihhg2efC#z2dDz{v>YNUvyd%X;5xluSxme^c_-e=qLC- z@3jnlUT0Y<4w@g=1IYHKUV?+vKWB^AKfeo@+ihCFMab9!JAV^nOJW!B-=nKvhqmbi z{ZeE7(mn!hp~7U39R&3$(ZF~|4-iU@d@4}b_@#kqLjoioiTwl+u_M8jPd|sKgbyc? zrsfrQ-`N=qImk~S$N7plIvJs$?|_T2kj4kBhli3$E0hLtJ>BJ6XourhCdP11Yl%85Hs~Mxinm0P^YOt6`gRu_ zHVofk^!vPU9FxA$)z!y5EwmUVF++Kp#pYY8Es-|Fg59NcNPAz7?FwsC2YzE>=1VT0 zU4ijo+v0S5dd$nBaDElLqHyYBI!ZFr?TYD;c_Go1+RqmfFS(RO@)(i}(L%~oT1bT{ zpt6)BbWvN#;q7*Kd|nqeyic&ZH*dx!dGgzJswd%j7C3D0bc_pfl4fuTLKh`~VtW*2 zwH6Lj9G->n#Sv=Dg;QvyB#H!1fdue-2o?N^y1wk!E%|Z;+}*k*kijS_lVqU0jR#*& zm&0KT`sU$k^v#3g=$l92egZoi#DO~)1vd(A4AN{P`<5OZ*|*3Xty=xk$i9U~9=d1! zgAd)a_Q`0~RR^L~CVli{U1JdcJh;anx@UD1{LzQ*X?q6Z-vAeS=$@A2Bl|8}7X6s> zalr8;q>n@X=OMo(TDAPchwfSROtk6>1B8KwY01dGOGl$sD~?60uGEA79>kAPPm~d9hdny&^Q8NDAQF~1`&|WINXCwmo-9J#~!+8`AgBNl@Pab6n$$cLf=|A z1owcqb`RV`+>#G&TST(Q{%&H}DOcSX;I;G76sU`u~>Otc|NsCSia$)BPG-;zHK@&5me)3BcRkDF;2$_8|o9H($^ z9p}Su(eN>very>Z|0eZ+N7H{x(|=pZr@!H+KH~k?DctLn?rRkOwPiSc6?%1ohkKQl zXL6DcPm*-qlHbtqZz6pBuPOYm^?dkOHr_uvPJKJ?|8j`;U!n1@P`H;T`S_P3yniT4 z{bjuWi!tg`_?M3J;g^PZ|7UjIf04qyNZ}6B@Ie}W;W!WX!VvYB@&5A@#9w;JIQ0?l z8_c|aF%Q4AD$0i!9H%~?f2lsghv!o~^Ywgq9*w_<#$QC^FZA>27mQP%#(#W>51&ut z&qsWCuATSKqj=6+#)r?1QooY-&++sA911sw!hLLrk3ai3_07D07R_Hd$%iXRyx&8< zYY%!qo5Iba>9Zm{+$=rspE*u_JM|IoS463A=KV9qsZYyOPSeY1dCF*fDTOPw@$jXH z_e;j8Z>N4G@9TzmUrY08{WPrSea!^*Bh+8U`|3&R)BI{0ucGlPh=-P~LN>D2AbtHo z7EQi*K<6xMXl-m-s9)Gv*Ioynt+}CnVezmh%!#m#RLVSon}gJHM3%f@V&n#X;yx{a z8T*(NycbcyP(&B9<25jm2wgc5UqFFD(=Q55BDMVLl5~J9Ln>0{EHh0B4rNX&Hu-YM zl#t0tGro<>hxg0ZwXUnPHW~R>NJ&uTPLY5jfoYTgbXlZ*$4@B0YhV#lo8hx@aL46s z4mXV=P)G|)04oK2HZ2C83iLv4G}hq9>xDU8FWPz>fth&pZ_(Cs zwv46d1*|Yv#*u90SFG?UdV%Lv%B4s^k$@rrMFLm?uttS^oK|nMHRxO1-mO`m52ncR zbeBM1$ln*zukkoG2lX}|Y!8@OdeE@O3LJL@9D#oQCSL%~$qTu>LHLG3P+v{9M=Uzs zD_EfXU=`K(xjo&mI<3zW3JO3g$;B=KEhI4YgRumH0jD@@nW03`!Bbacbo6CL z0x8$lt=BjA`h5X7=XEMTAv$K7$>?N%Kg_y8&qT#cOevUgvzu8#vhg#;Ims!-K(g_( zh1~#TqoeeJsnsX?-guka&jOaCeqavOv$T~tiCUIi=g%sQv@|}uLK>3I>IkfEWbVe#S<;!X@&Tbk=nj5{$b3mt1qe(2a*)|^=W6T`Cbv!S8! zM7(l`?SN1Y8$zgDm<}1+9f2vwIHCO6r1Nyb%swL`X}Dn(9X3Pq(P3AQjC7cUWTOKo%%51da)*f?FlRT=r%TkgN+>i>=hW(3l8&D# zH`(~{IGHx99CbzH*2~5ul^=|qr^6;B8y&xssc};2MB@Aa9hSz)NrxplS?Mqc$w$Y- z?6BcHQokz@>WAkD@LagT*XwtCQfHZI=~%g@xQM>N_cK_8((p*3BVZk@cilEc36oJm z=@^pmvu&%;^RXn-9Pi)|aQ4JMN4%-o>=2OSSwI9v6v<)t5;`C}Lhuy* z*eG6OcfZHom6?ca{0KZVY4G{`;rXZD=5TIxY|dUa6a2WHUe*f$+30NXGf$n8jgFU9 zI|#`}$Cp_LaEfetATYI;?>V>!I{eH$NtGM6-|%Jh`+bb&!vjh9wbQ&tKl_2Z)aUJy zIGt16nI`yQvm?v<1I&^!7z(((n=^I^vgtv{y@_RDOQnPRH-4Q1`Sb*4ZK`XI9M9;4 zYzRA0mg5x1n-N2Y*hF1SQyt|$-{;aojChK@2lkKn=mb-Vtd6!F3L~XtVL%Nu?KhAY!x;c z#fCUR3zIekbFF0Yz30pBz1S3QDUhhK-@5N5gY;i$idpYv-o+4kiB-4^i zc^@*Rw!!lQ5$~^w+?=eZ({j>bGn9C4ESVp+2`?iZCL!7A6x#c@9P6hNnljNh=GzeI z`j(_4^8K4sei-jUaPK9X9`v#a$wo))d$Anl7K!VD*!N;G&|yhVs@zifVO5cQbd>jE zphAgD%v_oDI~ku8@~A+~>VgbK5%Y|Q(8wTi~o-fhoh`F&*AY*~N=aZv9z%1o&zl-voPmG?$ zVIuSZcN_g6LZo&jO{<5&tYu5}iU@ix2buT=>B) zh13u0ffm5Xrq@|KbIgUGNSvQ6o;gaT!+4Y=S#GKPu&PLE{Uqrq&m2L85|`*_j(K<% zSZvSeu#kn;;>%(85<2;Mb|$bEUp9V}XLN}+D&_{y$?;`m?vx<&cFFToLMLC(0*h|l znX(q2D5}UGJB7=~Sw5IlF|Ebl;Pzf|xjt-Kk@%+W}FUd#hz3>NYD{DMhxgcSDE>YHZki9+}WL__h%(IjlTUzAdCtq_USD3jENH%^1Y<=14M_}KrZ2IQw z+QQOp+34hLPQiY$EZZKFfQz{z40mp>AZ zBp}DLCadz!_ynnn3ljS+Pw$Myl}Ldqb_p2aoiVg|i_6jN3IvVpNq%t(1K4kE>uhiY zx_sVFIMuTgA_Bgkk$osY5y_&x3F+{c4T*`MEh5GgR%DO>{@iY_!|jy;)#-~u4Mq3* z^iH4G>vD$3CpO^hY`4eJ<-yZZ*=8O2m<>RArJgK(`c|Y;atRWEQDO)Fs_DtUm2`et zfj@L=3%EA9!x_d;^7U>!(xgmCIpJ&)N_{$H)snBE(;wwqf2Tv06?GZUAvf)JO@r*aT6ZC!%D8?bE6sGUnT?q^QGDN<6;f?`(&=5 zaf*=!371yB zv_Mjt%AFzs*(AX07-xm&?Mun1Asgn?3Q=8O$hY(wm)8|=gj}b5eP-c_ro8;~$GLl- zLExKt|8qmUzlX+uc9IW&2J!yxG2Z`V+^6|=+WGjqDtZ4-8b3($MWTHC9pltr#{1ic zs88VrY<&233V%C|zs=9b_fJs2lJ~R0mqGc?~1Dclb!+|yA${b?HBKgoypN2sso{qb??+o_Lu|EVbT&Ak7E zyb54Z zN}wkc@~^6?*#hgejKMyy(dp~0@dtd}ea=v@W=r?hn#+ur)xc>d!5W9(T~mZ2(7>`P z@{w_UYhMVV@`~r-9p$gFZ>B!HtHj}-z&lFf4^iL7`#&C|zMuLu-;a*-;UA4te~9{a z>Mx@{;{5{?)Q?i1!XKdU2Ppgj3jZ91e~!XG=jZc1N8z5MaL|~ zrO4HAkuRykIyIex|5iqM#m{N_&*DDK|5FT(3zQZ*A zFoi#ic(_A;>eKKq#`y5$5cSR2HzU*ne>QXg{w(MySAopjT&@LKxQ*OK)WLXxhQMi9t^)TdurFN=u?%r1;u;dQty*>9%giH_)mq%cA*qKKxuvQPO zEA3ni12+~uLJ>5KVDT6a`S#qCV=8f(x=(-^K=-D`z|ymBz?6aqV%pKp)EI*K!DP&2 z6k_g14<}>Zo@=Wlc($ND@OLwCJ{R4})pE5c!tLaCqMh6*H;O*b{SEm4qDrsQqkGjR zwF!Mw9Z^S6RQ;g(LG)eqG4(O@15Jgd0!?Ua8XG#Q328#;j|i1uDWFwQ1CzTy8A0bF zxZpmr2j3uW39@|-AsY=1qaFzL@F7(tia;n*K_Jz4qhXL&BjgRkBE!TzP5m1hOG!=K z^QoF{bRZ=)abH=9%;IAXcAQt z9f3*%Dp2>ovs5GWBsv1|Q7#Jj6Y&iAN72zF=C|iA10GXp{q{KI-2>G4`Zi-t11iI4 zJT(`P8bYHd#CXzm0$@&v0nEcFh10j^0;dC@Cn>rGxw;`PCB}r19bzs-yWtO#p{DUF z$9PSHJo%cgM%)~vZEU%w!@1SnZ43CeY-qDJ^g3@1A~ixOJq0w5nSHIA+3YLalaXR( zi$Q4vGrRu5oYJUZ;2?h|hyvpIi?oyY7KnHxD&g~Rc=s7USa;@iofTiHB7n6$uob1n@3P#wFF3NE>3o`qDb!6hs$; zpg_{8v@7DI<4+^w;jH~MrnAmn6d4q|qA02{XC`L4U6Hfkl#Gf5ia`RU_{lw7@yR*~ zoYCkAY;${8ffq5*Q z=5@{OImtn>J(On}IVp~kNwfqwJPYBWh1#-coC=^Kfzwd}c$A0bE$FP-W4QPu*I)YH z$n}ecqSbD2zGKnqE%s=2zwQ3*tHXs_2K)sUm1>8_fCR4zzu%lI5)T; z0{1UPtNjnd{V>Ggyd4N#dHZQ37W(?B{OtWi8s!yB{M^@`hIL#^%+#;s{l$~iAELgE z_b(lzzMuLu-=gDuc+oiZhp2C-{xa$#-mjjZew6wYzM8^UQ+QZ;LCUv~!Y`!o3;i^n z!Y!n5pBUrgKM~>mD{1=j<9v8|q}jX4XN1Evf@EFWa+-bxO~3sAvv(~3R#jDd-^~EX`h+TK4)Qd)c4G|E+V@dF*rc-e=#L>xjcWuxEdFuW!HiUTd9m&pFqNIsZxg z`K0Y`{*%1)M)&g>y&N~WaSiiM=X|HL{B-7@E?nN!0>}LMw5{&v+6^4nQe0b#`?~J< zb`Pc<^?l#DugkfYi|^}#^IM;$`?|m(oRRPALXLO}z9$Rc*X44;_jMr!_uho>>vB`V z_jP$C2lsW|@lY@73g6cS&Z~7_7q0&J_jTbE;Esoa`?~IUsPFf6-SJRxUl(xXzAjhl z-q+yAgG z_jQ32xvvW;SU2JOy4;lTU0li(v?_dG7dVmoy4+mh`?`>#y|2ri=|c-`wEIO5-PeV? z+uS?Cw$KjA;C{Z8cW2`^N?9hH`w6EYeT$fo+LqcTrlej>y@)$3Ur9HKnsgyupp6P6 zvyt~$J;iA;w8s5h@gl{;1X#8H-Dhq}K@=$E#vXyU;n7NTcoW4$Fi&;<5n}@Gk=%m2 z)ad>+@b(=|JQr8o72xzD!dzCG%bI}Pks2@)r;6E>ibgj&cO1~Y3VZo6_V~Jmv*#S2 zIezxcB{LB<&Z}E;{N6Ms>BP=@*G$yJl&FA!f^#?kf4rEJYbmCxunN<-7(h1`(^VK} z1@a=)uf?#Wa?e_`)U;6Z@V?i+93$F0+OunOef|kE5M45iV}nZ=dO9ICvvmxpaQ0JJZ-$msyZ+Uz6(` zyqzugtULno5m=P#Ds;5B<~lRZPlM!J;*`Oig6qZ8X06M&wPGZ2yIR+@@@-Xvd%_cX zS1E>_+tHqMk!46jL*uJ%D{9fyC3BW7sZQ9Gi`KO-S-iNVGhgWY)38oGg*M~hG%U?D z<~q$BQvp2?(ofeVnA(vU9M#!%rlQ0-GZ$s%t)+9@zS-}novk7)(Xu}QhS_wR&}rK`$}-vfvFzyOPzY?-B|`A)}*n?Mj@B$)U(#O3=A!B zMb`E7bB`s}(}U)8c6BCbjKuCF5}KG%-9o=Zsi!B+k3Od?slGB_kW=JO{jG=An|m%W zwPwCvK^@_|$xE)fQBK=NN$qSAYNcA){ESMEZj(uE^Z)RPa!QjNiRtXJ_3+5&f zOH$9$y4gwf^jF@50v66XCzDi8L*vqZk6Yt{j^?C#`YUfj0Rw8InpYW{JS*GQ*3r?P zsn4|;_q?eP8(RpSQ8&5n#1s3zsy&-~;)$lVl&&7LS}$Ea=3y^kJ;sVkR!?rdu^!Xh z*~(m>$jt6t`B69>=S6r;3>rG0zJm9c0^NcFhBzVm8gNf!Dre6acYd!vu{db&0? zP&R5$(&?f7)@Nw(9p2u4-i|Lg`!>8qp11eCo&h>`8h^s|KZ7%M7SJ$u^ubRXA2(dW~BtfvR- z+*#K+Z#S*%{?1QO&tN;BRw#;45~gKlS07(5>=m=DZeil}Y2x^2pqjPdm8XmM^7HmS zjvBq|xv2B@KG#F@wx^u8mtws+{TW2(?Mw6Rr<|NwUo%zz9o@mfTWfT?5Z~W4ulBAb zp)ftqByoKe6C>oSXlg0BPt@;vEPbMW*JE^50w1sc^%yJiZEk<-Y0dTT1_JXka-`0B zGhX@c|9bO%BBQ5L%6bZ~>iqcq>F8{=7~+0WJRi?gNPpK`4+{6{vGOJRxUPeG2FjOA z)H_i9{kxCK8Mn@y;cgWnsFGY4n+DTF8f5te^JW>ujwp1mgJy=3N)RHCn-G#%rLc2U zN44oDEXYgIAE%yy`p%>+Z~Q8wWc^9_Zl+}O)6u+|#!q*Yq8?mlT{UT;@nMO>2HSTg z4YMy?pc_*!J+xNbA@T%8#0|yijK0@X-2GmCucx^Cz4~6yV7zW@Hidz2Orxea(=GWq zMZfDYcgXjN`dyFFl`P@7S^BK~?a!Weowbks@qKXHbBcb~^Xkrz-yhriu=?1a9@vkf zeEojci;X}*3R$IbVRZGHWI*JJ7H_qU$5mVx4)6$aeLP=fPQXWZJ&-zS!=KMAh|pa&(f z4mxz-+S@)z>HV^evnUjQ`r9A!6X_VRXUYm)#ye*gugbL<2hMG-_HPeM2IAQ@s;b)D z9ns(ZaCcBocW1tRrB)@BY#nsxSC|~^{?_B@w_&OB?~`~Fi}>JeyEeEVD@Tv}_w!95 zkvRCvf7%}YOt=*H5KuWqT*vP7y)jNbukyVlR7d$3TqDqg?hM$wcgpvc^yxdQBA-bl z7tg955bJYUybH4{>5Y4XYt3_S(EWvrm)B)Gn>*T<R+S_w2-T998t_5sp_oS>vw3bgh#_pFl;3eg^9KCz{Ro5JL#q{T4 zrLPwgYSM4Mq2?MPYI^XakVgtJfA{RaU%PYYX|>mheJFkY?su=H80FTZ+*+qxl;xLu z-R@mnZtqpUIOU4B{dz;q^oN9){&uJQ8z!Jk)N@tMH`mlS_1u}dV6U9_a;L*104s4V z{W4Sh?`YR}KvUqyni@NEEAs0#_LJ0!Lw2l9HbQ~9KuQE>qh?A_y)rT zsRmp>gAkUsNETnnwZ+iDcdMznMjRG zjT2LlzC}z(ZA)zfelhi;s7<|+ZW1-=Lb@Po1%84ry^;7BrFx3fVyH7(*Nci5>GLoF zR&BVINGaeS+I?e>ILd94!<#53f_bX*j~Ej~4@T~Aryt<$JDSQvpMWdw3UGQ6VJ<7p zbxuI;NDUZ}Q^o9XOQ>!-Fecj(HoBjj&%Ms)^r(hK=g_xn`IanQJoE@b?5m6*sf@}a zP#%Hu2$V;lJObqrD33sS1j-{&9)a=*lt-XE0_73dV@3e?MC?aDPU>9W%XL++&c>d^ zyJ%ixreWztjZT~0GyVPcnD&<&@*0gm!{T{!_Mg{|-*Ilwb!XGM-g#$7I6|)zdy{u(Tz7_9kTD^2Jeo<7`Nwvez_=MMx9mDZ7 z7@nLO*OL`9W(@i8EyK^askZidF>Lxy-2RglwKeqA*MIWw1iqPYy?Ee?nrodWwYK3& zt*y?JTAOzN?cAQ&6JT4Xe+y4&Jx@<}ZTiy$+`WQ&CNAAM^sR_T_gqtXP|XlgvEj(d zgO={B`e!I@G^No~T-SXJ0e2eJR}_Z#%oJn!$+Kmsw~^|l=S~9chhCoXsb05ZRin9I zV|%ixruvH3&ksLi^VWKida>tPF#=Da4XdwMGl$A_K5t50QuB>eQ_ZufC9bUb2PE5a z9X><0C6!u6a)%4f)fJ*~%bk_&(;pE->noZ!J6z&Gc9nQ(>{ejn5@!qY_aJY5MazG@ z`syoAxUS~9R4wBBw`7setz&km4MVRQA{w_IRdqX+gF0q9b!5(NApK~*@IONqwnb3j7uqPM(^cWwBzBL-BnK@UDRwB6*ZgN^7R!L ze#Xz!D=K^Lta=#h<)@T}^(lIqZ(efMkwezk^qjS`rdOouE2cX0xZ&E$Su=W9w7F$g z&I-!x?nIeRKW6k`oj2dyNcn1R*u5Qjzd_|9@7x)^g$~X;H^{rLfV@Yz?Lk}b#LZHs4u!WKV@iFA+m`L#Irnb-?Q*gSOPv zR~+|uTK`*8Vg|4GkJaPOx|=F0M{xg+b=!zNcxPp|R}acRiONU6G9h^<$p!Wx;WZvGsHVy6=9^`|kHx z)2DCp>x-?)rb=HHw1xBx*5+k-uY7smNo5CmDkz)x-ST@iWkWAr=Ik@*fF=5}r~b~* zAC%4dVtIovL%V5SgYm!g^G>^=CtRKPYmLrZK0I6o=cjqV*j~EUDZ`onRHHZl?o+_2 zn`(|u^*FW#PyYJ0#eWL;zrE9+!`^Jc`Wiy(tMW707yHyS?S2BwXJ`fN9lt;*uzYDO zSnsI7X@gJk5-IjcjvonMUVabJP$w4>`+h86-qoFT{&e>7e5LPoJ2{~{a5!C%`hol5 z(X#$EszRhj%vqmnS=UX^nL8B~`^UHE0{6r^2ku`j(c$%Xzo3nMJKZavWr*@wMu`^X z>$I43^i=DzmVmyk^C zP?4(4_V4?0@+%tVETeBsYTcHX&aa^I$hUYsUOjflV$b?} zNp<#D&+kcn=}TMsZA1D}>7JBl*q+pwJ*X{ss&{|<;(4K?9WOr3ICt;nTe|WsExAtb z23~T>gWs+rkHop+Q7gowhMU6NE7p13*8RAXiOmP;@@@I-!q7HAq zm?P$jvqim_C(aS`#RAbF&J_zqqc~425{tzWalW`fEEUVdh2kP{vA9HBDlQYtMN^;h zzZLHxxmsK!uEje%uEQ%--i9}xyj{FQ+=w@myi>eOyc=&$d5?Incpu)5@_z9F@j>oC zk3-cl^!cfJ@yVwqh)+JTU3~Is;2jhik%sU@FTxgtt&lZt+C1s!n>L?N+dF>xlbbdl z|MWe#E&BL9x6OZ`cl_y(^p3B|^p0uXiuA`2w%v2v^j^eU?zwI8Lr8xfq5GcO7VO%z z`J^emV_LSMj0Yg!j{J`ye|_)xQ(wO4wrLObjz8sCd`2BL6E|&sQ(x>Ie_96d z-;ll);TE*vzD=8}hap^o&r^EGpSd2m24#-L=T^u!pzImF<7do3d@AZg`Db3WY4aN% zK|4^+j4AWlS9Ey4W`pu?LOW3R)JN{Qt!5+2=|OXbU<|z}9f5Sbu&H5Y0>XF{@)Am7s#vwoQ%tE`XF2h-acqMwRsoncp9J4rqgK*dN4NIfib32W}~fJ@44;NCws@A zfwVKWh)+!t;#0?O07khpZU#o$#Fwj%{qB~kW52Qs@p^=t5H?mF`@KsL?iS>MP5Q-) zRg)g8$LB|?j@{N&MV}`=)&t&sNE=pl>^BRDw;(j}%CU*YS55p&uT$Q{zie>hKX2#H z88_Zl%b&OMXXgEGm;3p5{P{(ezc|HB|1HPAVfhOze_^yMf4;!6aO3Bg_Z-*zYv%uY z3dyI5XLq=~XSqE)ceT%P5$^2ht+|R#gbmO0I=eWs@f40GmpW*ap znD^72Zu(Dq-1sNG98YoMA8+ND`A_b0KR>y_jepqW#@}b&_nG$ue|~~LKfcT5J-&hC zDQ^7O4kw;;+;)zI8y{Qi#>cq)N#lFn&qwd#*v&sF)8l?Vipx1F<9&E+Y{?R+#&!e66LR%MoD~@%)AM-}Bd{mFi8orSIp+3^ zVEJ%v&u~s3#=K#TE`OMC`<4Vp~S>VqZH?G*haSz8++<3@NjyeAj zPET`s8tE8;H0+am_knW?XS2x%D`)KSbqiD6IV2%hEy=y{apMu^E~HT4 z1@0YtLuzRQ1DCJq4a_v`>9x=r*^L7cU3Dn0i#>h92B>}Bx~L^JV)CK}s6FLs$|F!7 zf$|9K5hH-_oSh$=%rrLEWftVy*OYJa+#}|t+=u}ifw`U8m5XvK^IdrF)!ckrZb81e zYjO9w<^gNKYh4k%qFL+mZLPTXWL;ag_V?vq>t>egAD|H!Mppy}sIGD~uj>(juFT9^ zTj=QQ&cKa`|F(DCWc+${LJu33*3C|!^jqA75*969RM+o<5(=PtmdshUq&n;0vX_vf z?_#%}^@YBdV3(8Xv1685gM9?53oWa#(d%G%(( zfqcF8CH}5@+=Clv3EXK=ey95({yUUo?`Uqh+LvuAztg?^PWSRV-RXVr<#)RC?^?w4 z#q0K+?(}4a_1i+|{SWj$2YO!v@>Szm_)*{8C5GO(qiW2M9b$hmwCYf#r!M$Dl?pro z;VQ;GRfi7QR&{9lk4Qs|--J5!xLc|YJprNmWu(7cHAWz$ocEMf9Xg|@YRswrt3;!p zQ6`R_ql7r^tuDsjPxk%&9!?pjV1o<;aI!gC1EBfNm{ZiEjYybs|PgbyL$-9A+}BWyr;FT#frZbkSA!fgm2MYtW| z4up>(+=LqntYia{Nx-|4kn0^R^*EUPis z*_K(-(V=6J+*p8LPuD6FZ}F;Jo3SEOX2^P2UTv;M^tT?WhJMaH;qjb(OL2cI)x+J= zMJeaL(^cJTttLTJN>V*!Zk>A8uF#ZVmz-1&mD1VO*5hOEbozjKWY4s#oTX6I8PPtj-$sQ_1?X+PncuR!><6 zhE|b1tiCc|FtJN9KkkDJ#s?y&1n=NAwWfcvCS;#kNLVQStHQ}m^)$@Ba6wX0C3)6k z!Ms@mQcuHzfvKl)!Q27qPx%>{q+wRd2l5%2q^BXEo1Dj?oF}F5xOKF_HqS~q{b?~j zQdPQo%x+QX>M^QxQNntR6_u=>+`v4OmGs0(fBWP6;HzHU^%m4qcva`eucxiW=(9%m zWnVtK=Pu@){hc2w-K}S>v7(aIgJ-`?74^5C9nbt zt?)vwv-=8MN5D_I)pe{b;AID)vnolhnP_Ld71}yX6-njvr=C#TRLxnGyHb_xrzOdW z==A3;Ym%;y`DOMMw05+ZPyG8@a426Ba4D{3go;!`s)vs1geyg*IA3t;!QeR+zDj=@ zx!mg3E86nS`btXHpM+~BbsdE(aQ!pWm~B~;U0M9n^!t-tstbhgl;ryZ>QZ%AcW1tRrB)@BY#nsxSC|~^{?&LOB zxJH-cL(m+S;u@VzJ*D`5$EF^~Cu%mj`eIhLt*xV@JyV}+D_{uvbg(aJwCyGHB?pnu znk^KHcM2jiWU~%Rq5f>@=~~s+Z?ln9kJtUA@2g6oo2ahpvhU>$xmE1@tb@)cG8^}j z`6}jW%05J76$U+#ls?e}6OsK+#N{T;V( ze@s(Ux_Zjri(v`*ht0XNqj$7d3f-cx%w~2O7K+U^BB!1K`2KBK-NMBCw1_j;dz-Oh z583o*uzk;Gb8V(^j%n2F&KJ5Y-nUN!J+Ye*DbB#hGQH zw%hCJ@8s!U*=~trovfoMO+K)Up@BM z0{d0Zo^~%j&inh)AKEF-{Fp9ifm5<|UBorVzVxTZ>rWBa9Q#|3;Zjn+$1U6+(-if0 zeu8?+*Br5g{KNX1;{aR>9BkL{5>(nz)*Ul2|#V=WZ%Gc=pJ!%8!oScqo zRRBSyYH-gFTS%-E&ntzZy?^~Lsbo?d-0d%+fO+;v%Y4k{sl#c zy64I~$ePIay-<{jQu1^1e%E7pPTuc&jHk+hZ`}WSj1`q^ep+*;$3s+8Uv!##u58ZM z-~JSRPTt4)3F@&tC+}}P9i6S_k?X(InIGG8*gp2B2hH^Av3j1ozx5=%Mhxo>s#5OP0iu`)bTh~ZwIAq7BxSdQTXM0_ZM)8d$EmA(pBkHW zkpZ2_0j_N3y6%oiXXn~;o!Rc(tNtC?0C&!&x~IkHRBx(R;48`Ng{Vp0{+s%Y5D$r+ zNbgQ{htnqrai93Pz^-zce;7AKh>hZvAmxQ!$a^z#yPVq8?SB}DTpPvqfb)lOsA_}Q z5=m(jH<(jU@W%o!)}`+v8DQ&4g%(n2k)5uynxhl`u=58|Oc;t(2^)a^TOdPk2q zlyT1lks2#1XD>K=S<9MyE8bDJdgq z`q^LS`d+T9TJej!7tL$TG%UTS(P^)HroZ1F(e`p9Uc(VsI&;xQuVMA&s>>rVI7YzH zsB5z=9bM}OM~BM2D33s2N1(h9tVeE0iVp8qRnf3`-W=<^W#XV5fmPkzg=v!~uZBNg z-L$;ZiuF0!g*GxXC`lQKtPOWQlwveAZ2#26Th~du9!(|q(!*3ss z7vnNG#f^J<-1yoZ9B1748cx5O)34_As|v3Ct=l=~^eZ>G zpReHbD}?)beUlr%h0A%%6!-Jxy&R8r<8=i$?q*&$^SU;;>7Bbcu65%#bN<3k_j954 zEF8{n;6XQ3W!<-&ju_d_^7bB=*Pe0Xwc9yv;#jzGTQA48ZoFnY$K0OPEYEX$@|?bk zd8-;-{wm?dE4Omo#PMi1Ua`TAbDS?%;LjO1Zr#Cg564s7xMe5DoWF(Bn>oGNNf)Fb zo%zr1a{1X_jvF}^ZrtSXM~rOBxSy9ZZ#jRyjOCXx|I&iXyJRQFT;9c7-Om^8;+T0C z^5+X1-SlO(ZhQf!U%1H2|B|il=OvA9ylp$jquuz+oc^VP`}s@4jql$u zP0;$kpUb(g*G<2VKYy{u{rrU;9CP~TIeqI+H+?J1w~lr{e@?jZXQ#OFXKLN}(;M9Q zQ=I>k+uhIi?&6qvTbkU@pD4KTJsfXl{^rqc`rVxGZsy;$)lJ{T>69w@w{twjjo-6@ zW9Hr5=ze}T^WV+s?<%_+_MBmJP*7tncJEdAi_-9k+GDvJ&i;)D|%oTBtoE_QI*!5f)# z4AQ?zc%OOgZTa?GW_GU7y=vdqe0SUO6`dXJ-MRMGp>x_-;-%NCM)@~oWw<42dV(#a z0VBQSI`*Py+FsPxa#s~2Ko)Q>KgxSxFJAy@F%%~T&bcx+L$Dvxp>e6e2RfM!q%8DF z&Q9_Q@pBG!^Q6S`e0#n-pKZ%u>6kvBU<;Oab!R&bzt^5r&hqYD=h}QbZc$ERYUHq- zIGp}VgHS0%Kbs~*g-DHXzv6`nzAy#=iYL zL|NO4vbHVJqI{heldc|&7nbZNBQ?{WTV|F=V9<@gD07ec>q|%`7T#B7wmRXBXy*33 zntp{>vv(cV`&@J^b-L)>gCaxOuNV~5*ym2{?S4g{2d8tj=;l27UiR@3BbP68@$z0brgRF+ zcgdf{en=4i&@1aJ*Azq}T^{|BMp* z;XOM0ivz?MF;*NX4iX28ad@ZBq2e&yUvz{xQXC~R;%G5m93zeu$B8$Hcq7r<#XH1}cyrJ@#k<72@n)m=J(wFNwd3zlpz#e~5oVkN!*ipZGW4-i_k zh&QYAzy4{J*M07>$JZ^KJ?Hq$@v~E#h9kAQ9j@QMPw<54`2rt?YX-acuk`5(Ze`_*5t;DIWB z8}QtMsc;h1boL z2dG+jzkFa&jsn~mlqd20#)6jt?=}nGc<_#};Nf~eYG}aot3q~ZTu#TwLo9e_fOlxX z)Aj==BdJOY-gNM)0-m;gGr&76iAS9(ALS9S9)S|wbL%`yC~&^*9HiPxb9lRnG`99eG zFU_*8@BbLSo=e!*uxH+Fkl5@}63RBVl%rWLD>ee5`#qEPax8B- zp*#YEX$0uj0{@u$$fo=Ed%Ac0>9-@?*E@bH;ohn-%eGaGS^6U4ors@C*p9FjpSK`T z49=v{2>T!mL#U`4Gw~IKml6Jk&jRru5m3g&T?o%tjXAjn>0{Hk{$~fmBM95mx4u*h zS#A2(mosks&mC_3${!)GLKu#)FT!yMCn8KnI1QlzVHLvl2p>(~`qu?+c}`D z2pf@BkN7@>2N0e_K)HV%7P`X`a^g6F%J6VP{L&WYPC#7a45xH=CT{Aec=hhk^zPLR z-(M+q1ovlFCETCsyr<9Mgi8qS&#bV$Kl9Cg{^0%Qbnv)C4n6GfBaS>ObM*LQjy>)T$4@xnjVDf=R6TjhNhhCj>S)&?6+uw2HP49fyyKjEad*8R={U7+?Eg$;uts6gb+edG|<70RBZo2F4k8i%` z6I<^6*v4l#ry96(wDdW&sV5GEs>+*37XD#ED<6Mv5Iq{s7ihxZwJ>={+K);$EtJRHjN zw3jE8_A{hCo4)nGBVE~ln<%FGJdRhl6WiQ}RZC3oJ~cNUaqhx%XEw~Kc3%&2y+q6MrarY4ni%$OoUkobqKQ&<{->Ps7IKGa1O$Jghqt(aMNxr!c2r& z2z3av5#}JwMW{!Zhj0$Ue1ruElxNY*a~IFc)OECXb+qL&4eQ#v^V90qb#-^F&CJhT z@s^Ix)-Iu-%xr1Nb#-MLv)!wd@+H~MmAUTB;#_xkzI~;VLhohBG-L|}9bwkGd|PW~ z&id}otg?WqHS^K%)*Rk?t!g5PXXm@JU0u1g&26{=M9~f6EXcR7(M#!P?Q<>Ns?2qh z>rOloEm&JXPn5;GjHTJmc3mIq<~E=Ws?z5z$}B8&=hx!>smikHN9yQT9SZni2Sv{u zv7zyf{DKBm)`o1$!o?Z)XCoT3?YTA;sWIEtl5I2go++m8H#XLprjW!*Q%D^B6q2-Y z&Rk^=NTi=#08hgJq-@b@Cr(YfGYo?kyPhx$?Nb0qo&O+G zm(ulHm!#T_>kG9FR&H9#mat`sV%)GHf_l3;1Z)_kzV(FWY0_;}FL!L4`U2G^Wuaz^ zl2pGzNov}jB-d_EPpDgQ;f*P+IP~AV4T+%Nh9os^Ln0WqA&K?dkfhpe=n3`n>oHV= z;>nb&nuI1h8pze_(vd5+A*JiLAxX8{&=+bOtlWl_EnyoH#kdVa1bufwQg1^iH-zSC zqU^e9B3W!3dc|so0|d=Bq^$aFNK(@_B)N7QdO}@SwA*m*%q8Y+NCf>hB&l&562Y(y zNvz+7B-L(1PpF??kD(eAPo|tk5}LMQAXl$TN3Ph0l&;@~B-L(1U#M-cavM^%gl$L^ z<2DQt^xee*RBuBkw{aVK4Ba$Q(XnmlWzr4@a%r|9Wz}y(lA5+5$+g?i6Y9DW--Z}8 z_gs?H#>9$E8RJNU6qCvmD~Xj`Q^AO=R+$mr zo{~hdK_$6vJ4qzXjf`(9xdipLigEMG*h#HOY0anv*5Nj1YvV)IfD7`j#DFqBK3B&MZK^2DW1q=cm& zqL>;98rDbn>Xo!yNSrj##L*8lNdqeqE8H*#$QiXHQg+i)Cxein)rn$S>LgEC>O?Uv zbt0ISI>`-7-4mN;z~iWAz>{i*nZ)L$9x!yP$YCg#I!R1Ro#cs2ok$5wJw!1zl2)Q- zsXHYkEOn2gA85@|_c9vhz_ipUyJD$3*L;&)mLQ`)9HObuE_-2TfqTUV)mTCCq zEuqM4oEycqh!kPo7|5@&0-<7vWZoFWNZc4iO4t}gF>efF7&nGTFmDWxrJET~t{r?L zByEgu3_=#m2ev$`nk)jIY+#q+< zHHs8r-WYV!8^7ccD#9mdo8dG3Z|@@6l_&ENPW*&kLC_5&wIF_CA1VN&WbUO;(Du@2 zsCr4lq+Sxo*h`O~>!l}`o9H{jFkGA>Ta?)Oj1s_dP_i1#ffmUVEUs_zsdg}4e{uMMdo(v1O; z{AT4%xh<48(Grz+h-7XqHVYP5_-r=PW+EgHKe0^1PjaR5b{a}T>AS*s8w|>uijb7| z;_lXrdxwYT?azK^)#us_Z0} zW>iPw3Dt?cDO1TKu8^MMuTCY!b%azRX)CD+VHK~Ho4VkAOQFgf-=->7be9@9#$gEW zQDqs?Evh3KCe+)Yx~!&=Lf&9|stQQloe`_4DkE$*b#g=h!fsDAJKCS9ABbbr4@5}P z55zI+2VxlYgF`Ur2d4_Xeju?)KX`JZe(*?|;r7J3B}4KA{SZ*J8^fWf^#h5F`hf^0 z{ou<@UC`+V%ABkph?S%tLLB2TfalwA)!HU`;)D{(Frg$S1FkFIG*ZZ`*AEbytv3-8 z^aD{$`oWVM`e)J)gYHS$EY8Okfa}oW7ZGEFzN?~V9*av6?*+ZVv~OGBQcgtK^XluBZ4bTS;*@xJo2#a1|lE!L@Qz z7rYIw${gR|DpquZ8#u;c2ybv@8PN@{BN-;t+u*varje31xT*kYgR2OU4X&buH@Hr2 z=wEn)Q(u!dxQZifa1|lC!Brf2gR2DMw*;u6fp) zvn7{V(wWP3DSPLSz@l~Sc*IRTOwK^!`Kh&dwI3}w1Iyh-{=+YD;?L7XdlsnyOSai#MNLs4kquCgE}5Mm*{_3lMIe!eJV*`?bdVZ7 z!44#0YbK{^#z?s_Fs3c*kH%V&My3nX>mr-rqGpBPmJz96g%q(o@~BuQ+5B`J@ulolJ_h#K(8 zZo01pf^@#__1IWp;0+40A|K-G3U!uF;r^h$HFOv4YpBmZvL3(KC zONr_^Qto>xUv!SVT`!Sh9hLUHPnEX3pBmZmetLMrONr{PD7U+mPrn!`Z?vP~z3y(d znQd+IW)G63-5w!{c(YP8t6|K*19%@N-s|%6TxSA4C>1t9~z3Ukkn`VuWSe#ji?%Tf)d?}SR% zkHTg646R}C8NLoy*#)0vG7COQZxwus)+qQ4zdZ_@;FGi_!Dr|!f=@6R1fQg{2NFTM zfFzc2QAkY1_LJnsC1EfHejx@+5OCs#z?Z4*fG;+i0hy+>0y0H!1Z1AV2FL{CxS1?~ zUktXG!2tL)t^W5J2K66sG}>PxB(J?h-7l4AT6-$ZFJ(3kuvYK;MQGK&&oF3xpQBUy z5+Qlm5>>ubp55lwD150T)ffgB5gVOl>U?vBcNvPX$`6r(g@ZRcwyc`8&_Hwv-o*-i zfy^^0WkciqF47v}3(2asNj!biR5Wf`aAFl{PDl`MgDjLQR?4_G#V?$?UrNa*ZF-ty zpY=4~D0)U(Pjy=syz^7fl+CrC=MDg$$mwZ*8xzMyvflav!zNtMirR4lQ+_BYiRfPp z(h~zisOQA3p`IIAvU;LZsBb$Hc(MUaAhQ)sV0l`~ObdTGu4iJmMU1MRD_K@OEn;2` z#6T4YE0(9U%w);Dn5i+#VWzWvHj`xiWyMY0v!3xcmzKOPBsV}`nB)uSX%UA&PYinn zW|rmLDg6U;$wf?c$Wt(v=?yrhJbvXcGrM`EY>4zU$q?yjzLNJ0k)9egM0%!di1a+k z5b0@t8+lRdnUW#W)53;G&x#r%15-9cNkrce>4||M(sSa5NY9NJB0W(yLnv=i>g4rLw6G!4R|`qR5b3#+A=1+#hR8q+86qo|XNb&XNtv0cG3{igD^3*U z=^=xGB5?L$Jh^bDGQ>^Xvz{R`H!pczNQOvXm}H3bw1^?n6T^nc%(9$2r6DqxT*Op| z43W7^Z@>d{<(;INPlzHc$qZ@B@-@6?g|y755z-Q68>FR4CP>Tj+sCU}OOyf=!vrRN#Oa$N6QOrkCqZQJz8qS@@RRo;W5)>yJP0Y&VM2cRI)n8qQXW;OT?y% z*c>fYGC5jS#Nz0AA%kN>^6ZU~E2%6aGp2%!?ATdPWKu%*qX=r!=JYI$v1y5VPqH)G zq9ik;WksxvmKQcMMv~oxDNT&AT>I$_85m=U-Z+P?P-GP(ejCP?NU|}8@_a4sSs4R2 zYG({o+0qz@lC3fD{C4t!Hc%ydW8j4?j)4}nIVP%XbrPAr-7zo&%VQwLZI6K*u|5W- zY=4r7vIRbfv9(H|TG=G8NdG$__2TqsHulKl8m*B>N?h#J;bcpM;?Qs_I|QnQ z6#~o31`#jO0?8z^{b8G_lh8kd`Ic7cDDhU$o4q zebEwS`=X^u_C?F`+s8{-OO))3mKC-yT2j=$=!vp@N#ObRMav89iZ#Oc_I5^L-Op4kt?YyBQvIejBK_q zMviP>Y^X__)3YzevJ#hqWM8yJN%lp{ir5z|FKl0oB)bVy+81NF_R|}(FUAs0<1F7* zpejZ_Z&5OQo$Z~oC|Nf4Byz5zEGN;91m{qOWh$SaC@V^`9kKHgWhs$!5(CF*FGA-c z$|CISMD!d)UzE{4xMw}dwn2JbpS~{R0=2ORigOKRZHaaudXAwig_N834uvFwN%uP^ zDv4^uVOc2!hiTCFjyze{hiRq_WYY0Y8OCW%)a~+lgL0fLbh>oDpe%&6X!0aj>v5+- zyXi{M-%d%YF_dr5(4HK~H0{}eOf#x-nQGGKGE;woAoH}EU8ebMRIE;!sXa%KX*xwO zvrK0R5vKk$K}A&ReVM2}QII)i3m|h1rwTIBDO70-6g<5_P>>a83kp{9`Zw7Jxj^#? zgUr=h3Ylg&Wrz?pHbcu&8ICwvtGMD+qfUy`6?P;}(i;;kH*wFZ%u2kp@GFf}rfl)B0}h_fu`F46MDODR*+@j2D*2-`soLK}-N;Vj#bBmxHQh!GM;kee3C-2mYfQm|1GERAR8pENQ41+_k zFbZLw$sjm9dXt>KgXTF-HUj$7j9^Y|l)v^wBPfP+t;KxUEBxeh4Y_jT)KRtmV76tu zy7DdF`)djv?XA(*+0`j2@>zzG5?As{T1?3+S&=gfB}Z2BN{ZhmZ!s!avXWO)q%#R6 zBc|lltjHOJffOitB`I{~pp+|=ypk6^V^ETu!U83)=S0pF^h8g|>lx-X8&mSif)r;4 zN}jCbm6YfiftnOn@<|N;Oh8MNm5P=YSMpk_rzy0Ah>}m@8TS?0DM!g`OEJ%}tmKu2 z$X>pZ5>@g_5-CaFI%^pg^PyJq+7d0Nj!HF7ouuSLUpEJK!r8(N+4lU3Tvzv`b7s!= z9zf5`YhTe}FvM69^x)z$zX8hPX|8>yKdowNXGbB|*?k2H?CRE3?99i)xw3=ceRt0O zq*Y)1JW>O_u?)3OO;q(HU}`(!Q~Z9o#){r2nJ^InQPpCN)N5u)EVV62_Zy`2yn@H} zid-q-hs2UsOPuX>K;J4bl2(mVtg?gNd=%S1DW7US!tK?QRlSqh+RjO|pl?crlCZMW zo}IE+%Hk!a-z7Cw?vIiabY^Y?R6%oRw)2X}^@fl@c0fY{-^KHOj^hG5G4H2VCukjC8?=;Ty1*6&gH5L(s?@|mGrttPcIb|O&B#3%;VNuwhQ>Xi0 zTtqDU3K3!4>_-Tmfr=#i7B)$)8am#;p*s%4gBOx2RyRo|o zcH=^VsPh&QIr@WtN;zDMQm;^=c1B1T`w5U}k;W;b5)@NH$>9-N+`Xm^?n z+XB8sxx9U8Xl?sxDqR#8#zrQjXEddTp01%y=$Gl~pCEyMA}P$0HIGY;sU0UdrgNNB zg~D;tX)F6Dh+eClMCU9eoT46N;wqJlU%Y+_C~tJID9s<-kb|`-mvmLnXUIB7RYqL@ zs8}&wq#{N1l8PehC>6tRkEgFxBw2T<7?K`S5n?(`Ns8z#n zp^D=a73fAS#h@qkd1Rfb<$3y3OE50#m|j&CqtLM`nyhbCjHvEaa>9Bzf#B<89Zyyr zI#yg!=$M|;(8*&uJdtRe9ffW;6d2a?I$HGdp|86fht$*mn%p5SJp4}1(+Z|m={(=aur3Duqb@#9Z4A};$%J6l*?*pq? zF&m^JMQo6YBHJJp!*7pggH$Bh2B{d54N?(eHb_Z|*dRS8ut6$L$Ofsz3LB)NMQxCZ z6R|;Biopiy^T;+x%kylImS9}eF&m^RMqz_gG}#8J7*QLfQUII-aa*bgZ}n z(J?(6q?5;NP$JPdI|>_QC@^e;bTr2XX@#B*(hx#6NXd|GkcuMPAQdBOgH#;J25AYA zZ(IU3P1%zdhS~<{YO`BHVH>0?$y*SMyYU;S*|v^${HQ9vWR=WR z-yC@6$xM&hDKlF(SZ1zdxy)3*&1}NVY{{0HsbQmLW=5@B0$Vn7He}!4naP3SGxOpW z(9DmRLNi&mi56VFakR-SSxJiwY%VRBb{jEjK+OfmEvcC=nN%}1Vp|i)AtP(e^sH@i z21&an(__jrISU)&WMb4J7m>qm0mN;zRTUv)oy;ezZNXymoyZLAcmh>2=w_~D+0E35 zi8qtOw%&qCMn9QW#QIda0m(HKxuipmL2}s%>(a_MkneQY)EV4Yp5PC}Uh|_X(8>pp zU-F|X%F^!3kI?E0v#|BC2WfR>MV_TK(~>NE=>B(ILDtqhdJnv@K>y0BkyW(qR^2{t ztif!r!2W`cb;X0cogiPm0IiQed|GAL}nlBRY66jZGz5aXI0f+*SK7eu+{)9NVO;3X)j z3THvZD`vUcxDKj1ie2JVkKGct@h6O@*M4)2=hp$tx57!@L(&;`P^6v5U_8eT3iI2S z;3XK(vV-DmJcFe3?4UIBiFTBi=m+RewS%JUZGXbacBDw%*>;3hyrs7|-;UIgVBL*p z+;UNdQ|^FhZ_AA*-9dqt_S-qb)0}pP3Jc78lFfDsv{HW#(^`o+>;(1YaBOX}Oir@O z9-GXoWh*Q!u~Vwa2D^j=>r13$yK6oL59Zp+)+U^U2c_BASM6!IT#VhKD$1H#or%k> zNSb_&?F>Jt<=^lo-AWZY8CQ~ZXX8q;Z?F^wRhhwXKCa|z&8w2FJ0n+;{kF%At&*=h zCs&d+rdP=|oRzEj+S76aU1g7zbmfV;l4-Qg${hMrb0uASZmwrrnQTLL-PyT5OWc6# zxeD#CC2P#Nl5e!?${e~=bR}7D)Lu*TGtX zzx{D1M4dtBg{avYH$+V~_#v7MT1UjhS9v08zS0#@Q;oieI*Z;JQS-Ilh=Fe9j+pZ6 z{1HQ*xI1`t#L`z8H_%OI*ZOJQM2`4iH5IsOA^TnzeJx$r`q*7Oln%6E3T*Y zT!U|7k;!_67`+o?MQZm%pC#g-7>IEPMN3wCC>plTMN!jrK8l*HcT&`RjhA8|N8A)W zv$(~a7=*+aV<#X-bU77BVOk2KvO(wfCn{V&xmi-m0 z9FossmBY9Dp5J1XE9$#gWs?0Dt1OZaW0k}25Bo7znIvDvDo5C#vC0tjY3wq|eoYa1 zeBZ_@PvGBJ(c?ajRknzqW0gntb?md~{hcDpkbEBdeC+qwXDCW3(1?WnAFFEOK9E&5 z$q%y15%GoW@`U`MVl#L?k##=FlvwAA83yZ|>>pY4qdrpc8H-v?abKyJ3Pb*qbvE*u z?8)pm*=7iQC%YVy|74X#@}aD9MEoeLJYiqTJ_Gqv*6iXpg3_W;ROQVDLa%t!>@5)@d?}~(7D{~f=UuDh_cdE?%m`7!% z`+W$xQ|25oU&>66I#On~%8N?K5qF_%GD!ZDIfLvxnYn7u$(%>wHkmWTeI^UO*Ct#Xpg6{tNVbDo%cWTq>8ql63*$Huk{FujMrYqbXbB3s|W5M$z)3$7wL_zf-BkA zxSUOCT%cSuQ3EMui`zm;ESWt?60vZyEM&}d0?%&6$BVzLMEL{|M#L7?SjiKNgak)*pNNkmOcvam*u3rP9KQuPz0RH?E2<|zy* z%2-a-#Q5LgOE(;bv-%9Jj`SJ6?o#PWpJmdSK1r`TeTr6x`V7B43SH`xv^v#i=ya=3 zFzMKUq}R0(j#B6P9JTKC#bzDs(+s-U=je5^OtI6=kvv)*E%Ow*S|%75wMlpT#h7)t zPt)phpJCAH0Y{_TRRooemv~xLBC(7LLt-j)za%%=05#D#J7${@El^_{BwEx)$U=p! z2ouycBVcH4hfmSk5T9YNB|b-IQ)EKawn)RkjC~%C3y=1x3qV5uPbEc zojY%d?3YUt$>vBBU$=XAV z=GzEK7T5+UIc^gqal{r#GT8>iMS9(@;7U3_E@xdI7bq7^RJTjn;yPUtOS)W=M0B_; z3+Zm1z|+|=xwIu?(%1%zF<3Xp(xW4m;sZzs=;RNjI{iR^CXC z8-61_;tM2m**!=k7PWeHPDElYMK5{T)krMgU$wbxXUnQwr|&^XR|FIMi)dcoDEkgY zlo9hFqJ)Uc5EaWlLR9M4?>Plgg6suErIOi?3S)LYMu-@8wLGxoQF+L8M@0%-9i>E# zb5tI&#!9il{Hpn~y;X8N1FIBj7h%kVMzbqyW|Sfu$*44H6=U+S88ireyQkvFszSwx zZ*vvPGi54S%yt<`+7VY+Crv?NbEKj;_D3o3430t=vM@2JY*M0P*@i@=QKJ!+OIAWD zbj*TEVxC;J;#ZZEyad9kT~&fNsdNLN_Z_*T3gt^|aj|r^7#I8M)KlkiN>rc68L~o; z%O#B-7yE5sl^$nEIz289EA_ZEs?`+?S*_~`zFvvJe#Wj1J6H)DPf~?y$a=mib zWstPHhQ#V!Bh@d=sDh6d5ZCZ=j-=w_;)sq{5JF1cDD||wnj~ogH6^ym)ihT0YGG8< zC$RK`AJ_HTqC(1E%^_{Ck+8Z~N&|hb5K9U_E|)ZZTpUsPaY9(L$)Nze95vT)BQep zCl85ZDp{W73c{8snH{xE7CB@~Wt+jbT*(;%OP0)yTejpp5lfexLAHFBe7z;KEkLr2 zmYKX0p~O|?^vQl>M=fb`bK;gYIgey%lj#x5Ymp&jiHl-;mf0?oq`mF3#MHE1Ht&=z zak|+UXBIBbEK|JHMY<;|Q0G!E`^6Hs{C2&;k_mYNc6m(KA?1z~)&9V9uxN(lNhI?n z&mx%~@idY%ggp;Sw&|*5Fp5-gk!xG&sn|8D4_hMS$=KDAxF#HPZR^)C+0LtBvhNQh z`ZS!u+M{84D5@MLIXlR?nsNmB2+d}^mg$4>BT60;StZW6s4LuGmg zN0&+A<7laBH%CL)dOAwJ$=Ok5F!?(wzS-qbNf6=uCBgp5xo^Xy_-@|M>uZPL@ zK985d*5i@*7XA+NZM+?(7US!17Hdx@T!n?76Q=jY%aQV1`Z(b{i5^avY`ux?y&EpR z7~h7oSb8?hw)Sg0zLi%KCnx(fp*)sqK9r*fWgW_u=*@(<_P&fNll2NI#*dLJvhrd= zSNb93k(fIJ)QGSmu(9f@PkVg15}aUXCR@>gN=j zt%!9K_jT+m3wb-1naJO;|0Vma-1U>;J7$qZxw`)c{NHsYpNzm8bwWsDFQW(O`}>EwS);Q{ou## zoVKWtSyOY!ifJTlw3O1oMk&OSNs7xQ3ltYe3{IR7wl5kfnG>}rVXl?NLS2%@k_eds zbs63q&+W{vT$Edx@9NHVlETF^fnAHc*EP!zfoNHh9ni9TRqUAqEi-Brv_#oBXlas- z(6apYv8m7!C5xeDg$;+66ty3EqHIPIc)m5!@&coxrNnKEmKrfJTApla%rw2hNysPJ z9W$5BkC~LXNutI`TU^{GX{nNF(y}5JO3w=!DjSk#uZ&ztl^K~aRb^ze5i@e4w#<&4 zIFoV9mQ--az!|Az=gb5)cP3I`_4F*s_-Sd94b-wCrclcZTSPO745N`@x2Tk6(pawj z5)2tlV~O5kTD-1#$rXhhJ$94plntgVlB}dG@=cFt8f9_R9?DYL2+A_a^2s8%r^hXnERUEaSt#2gg-maNRAkBOC;ziqKM6i2t$TKEAgyDoFMJqxHz^6;}q{JOlecGT9%)9(=*fq8rSXd z+A*vlg&vnX-!aB2*47v3>qt?Ol9pwXW|l<}bu0_RI#?lbzTT5sP8kgIrBt!;f)l1R zq)g*^{c#KKftE?qw{D?_2xOM5S9N9i>dsfOI%Z7U>WC4=tE0&}SjY0)=c!{IQP#{l zmZYS0q?o?e5+kbH%nP)*ju%qsI*LNK>!?vRuj55DzL927{^oqL9WZh|lVBtzZjzXx z&=se!89J(LK6I?8CDHQ2M#X~U+ZPj8R$V4$Tt%7Ko&hp(qGumk)g&3N#FwxD0kL<4NXa`;cyboYE@Y{j+ z?dZy({?umRQTetu!VdxOw|G^@TY#Sj9$lYrrSf+J@88gIIdJ+|-~-Oib%UQi8+Z&L z+Lm4oJod_5T?guwtfJO}u&wspDTz$<|dZyUZCWe#}<@DX%64g7K7BWL64H1M~8k6O~1Z2|r< zaAqF}fd2t}H2TqlAs!1nzP&Zq3Va&yF}S3SsA38J9GmXFV<&JYFtr0m#ryE*4Gt6) zpGQpNi=*Ok{F&fDQ9*NY0z!tS4e=Wha8y!!V!HR%(ZI8SCyJ_8j9+CF@FWBrl~?0W zwF5=vM-fkU*F)ufz*F4yQ27+_N$z^6d>Qy;cRf@c0(^?Q9;&7SpX#oMs%5~Zx$B{d zIyco_4^{60KHXgpRi6Z|ao0oDBfw|4>!FI~WtzJlhE@Sjch|$vV}NJ4>tX0wz;ANb z!_Z5B&vfT^DD~wmcRdWHzSO$wVdw+EGu`nY`ZM5J%>Nf~ox2`}9RxhvT@S-*fakdD zVHnNJTz5STdo%Fa?s^#Ze&Bj{Jq)9Hndh#DVUGfzVV#VidAVrb+5+~`h=+kM zZd{s~2mBoHCG*9sEVeZD<J=7+%719$!bg9ZFi;I5hHF2?xmyA8M-T}OTU{sed(#uj>P z-@gH0J}*OhX%x-NTh>gQ*#b=SvVK(;t;p)yfCv(74fO`Zo11|x-4gtr0q+6&DIQDx# z{=ChBV!tmUzJW36uD3J(Gvar+c=UdVZ)AKb;+q)LntCVWD-pko@kbETeBc;O>-J^` ziqSOJ)HfWX|A9a6b)eY)K*aB3OuBIcV_M79Rvi1U$Da>4Q0%`E@dp`y74a>Me~$P= zj9)?gVHY287~)$QlMbQ$I1acJf2gfE4!8<`ZgZeG;A4nsj&U5Y9e-{o1pEyC&^pF( z!2jV7@o;!TWcjPGK+1@YaCA4dFf#?K<&?BcN$-NQJ8_!Eq4 z5mSB~W3iqDt#uq@X+CKW;}}c%KIK3$_B)6_&G@&7X+7aMunK=Z>p*ef8xenwaXsR# zj8`K5JY$;YFEFOEY2DyB@Z0!vp995#FCf0(#RrW*{3XVd5Pz9*BVyV!I1cK-pZ{^7 zIEd!+D~zd5npYeLJ%&FII8Yq)2gJ0Va2z}mf4=TOaqx+VNvGgAn9AQy2$!A;$kfO#Q(z?il=e*nwhP9by_s9OGK?=Me{raW^4;lrfF*cNvrX zdyIdNnEHU@kp1!JaR-V+rXqfV@x_S0&zRQX4#u=ze!!U8{zJyULi{5aA4<`ajK?GX zG2_{Yf5JGA_$kKkM*K8mn&+P~rt+R){CC7ZbMavZA*M0Hao7y}`Go_;VV5BOCF83R zQ=4!cM(ceiAz&IyTGKcVqxjbj6o(H%{2XIid(Sgoh?v$8j>Eh0=Qj=%htr;>wTt6$ zS{E-mP#jKe{2k+$5z|=XIN}ie`GW(+5z`U>k@2O7|HSwj#Q)2<7xAANe+}_2#?@%yVkAi^n5hiuMeS@h1Tvz<3ex7{=>?$1>gsd?4fdfobjH7>|9BI+!uWJ~ht8 z$6)iMXszHl=1gFGcJOlG!x&?nQ-?FgIH!(a{4ns5jDG`sl#7qW_@y$8j{`oM@j1Y> zZg3on{g^t2@jHQ!W&CMionXI}w=H3XbCz0Kbtj z)=3I7I*xlU@I=NmkCPbFm{c>yI!#S>@f*)RQ?%^X`SFR9mih_Je~2~z@&F@9RF?LH!-F$$DGkI zp$hmc#x&-&jOPH)WV{-97GoOoI>z?`&u07>F!Inb0s0^{*TpA{06v>BjXCxV9Vg5O zp2xTi_#DPJ0YgT|37-YV+&K7$zzvLP%+Gc48xI0r$Qb=jH8Q>k_&mlm=2-W1yz#@p ziy40zcnRaDfzM}5V}5~)Pdp5GDPtP*WsIr(3mIPnjL&qOcqj11jK2j;x*o@gF92Wa z;)yiom@_&iz5#eSV;b`&#yQ|D<97i!Go~?bVf-j?E8{-`BM%*u_5)tQ_>I6T8Pk|! zebX^%4RD_E&A_V}Zvlpkj!91dV{RP$5-@Ze9o01E?Tn`acQ8i3Qw7FX0>7Ctjd>^I zF9CNkehRpo@yo#LTs)b^{Bp+Af#1TI#(X_vD*p<`9|Oi`Iwn5|{8q-#0bk|fDV4xi zGo~@eoY665F7UOCX-s+;)0kh!_>;ibGkzTSZH#vT-{9htMgqT`@kHQvFkS$BBV&wT z3hSGWlimr8;NZ^zqdo`!2>9KM{|St_q2uI(f#1Uzdph-A#x&;dV~lZ1ZD4#W@cS8W z1O5Qxp8|i7@hiZ$xcHRAfj`8U#vF4-$0<}k%5pHqDYcPtFYregKLmUm?^G}2&jN2^`~%>-82=IYZWo`nFYw11PXgY|_*~$7 z7-LdB(p0{sQCw0DsZNr_-3D9dw*d<=@X3x*>&qLdWS?cd0Kk?gie)n8y5n z82ueNLx3M;d?@gD8J`CHJ;n=xA7i`{_;JSA zyQwD_Zv_56W6VQp2V=A)g>^&6v|j=Lkn!Jv(N8+2j|P5{@nqm1GoB6n6UG+-KgGBU z_-V#B17k1IG5rqUXBcD7Q$J&j_NIQ$_yu6B13G4;fq%((95C8P#|&!2P6vt^^MIda zoCE$ffelMJ-@;j{YYW&(Q#G^_`i&4ZSQ7$ zhI7f!`EXVPFnDyFg*K$qjIRVnALuyi1HcuG?*gu5d_Qm%W30#YP{x>t^e`9KqKx!# z##jUC5sa&WM>0Me81qC&Ewy1^#_NGcG2Q^YALGryqZvO4ygy^CyYvB!F(>IUE}n@# zrN=Tp82CWOlYpT|=$Kgt44H#321Z*QTmZ)WIrv?`hcd=oq%o#WI@V$OaK_I7AHf*w zC4HoeXJMSuM=^#xonc%9d^F=lz~dRO20n)Ib->3m{wVNqjK2W<2F8y7AJ6#Lz!Mn1 z419u%>o6YaH!?mM_(aAu{u3Er3XE}~qwaEG%#(w$4%3qv_X1C0485K{iSZ6#luJk5 zi@>M2csABc`c%dkv-D|buK=43^4N0aqg+WZ)1$LkiLO2 z>Pf$yG4029FsAvskui+}#)yuEDd2Z9J^}b$jIj^W=rbJ)X|LVP`2E1|VZ06ay^LuM zV13ZB@V~$tT-G28g>^tjSHV@U1RBZ+~F) zjgIq91IC_oF!oCNHpVm#A7y+G@a>Fu0N=rQ7x2eiyok!YlkrsGUdGFTH!;TerSD?= zIbh5g9gAoUe4H`usm(54JRTTz(?Q=er9Z*A8F&k0%z64=#%OQ)lZCZE!c4CjwvE=`Nzv$xgGr;#Trg^`gaToBH z7-Rg>XcHahZwJQQ({cVEf&a(F7aR!u6~=D@{wm|uzz;Cq0Q@z^)W@$geirya7cbox zcspZS=igv_DeyNLW9_HE#Tb1}Kg9TFz~6T9vJt=!GoB3m9mW>}Kf;*i^ijq%R^Mg( zecgSB9kH27y z`qNljbX@!j@UL8a$#K9t8P5a8+|Y5!I^bV3#=c3T9dukmeSDtrUx8n6@ui0V|Auib z@NXH@dU%oXhk<{`80#Yad&bC@{)3Az+XooyjE>9B0R9u>R^b0-{6XM9GsfCX?_!L0 zrT^mM<)eXLVoYO&w$ibD8Svj2(|*J}(Xsp-;Ee&8XDF^5Ac zT-=P+ps?! zyasp_;|;+3G5!)T#+r_nUjak6($R{thaA9o3h)@l82=$-8D9;Iv7n>%Uf_cm{|NYC z7w0e@L(nEVa#*WFu+ALZ2#kH@;I{%F#`t#N!x?`A_z1>Wk3)`h@e1^5$We?>1OET^ z-UQC(^8NomXU;l{H8HdpYa+}bWZ%ZVg{(7W-?wZb%Opt%Nm(l-At_r$LI_2uq>@Uu zC|gRUl>hU3U)MP^pWpv$i23yR{662`^{Ds#oY#4u_kG{jb=}wdK4*?OamH=oe8ywp z{KhNc0>+2ng38^*|BOkjoA|%5@xAaJ#>3zu#xKE~C)RB*T-2DH@)T3@Sr!8Lx&L z8_RW?DED>4L}bEWTpRi1q6X zw=tdsw>6frwKM((!CPU* zA@*PmxQlU5xT~?$F5)Wo;M;I_<703SED$apI}*!Vm=M0t?-{~=@X|4?Jev0=uK!^Bl= z&(r99{sk9vuDvCdLk*g{LYH zy#;>Cn04uyW{mGWPaDsGryFmEpD{iS6C1H%nPJXzxHLS|nB4cwGRA+N*~Tx!_)cut zE_kl-FYt59!wbUC8?!DwFBpse=NXIt=NrERGiR~k-@q>_KkS1S8W)9MGHwJfGA1`Y zj7jWa=IU8&{06+l_%nE^@g?{bi4GUHnCtH!K94{Jbd1ab4cW{l=}-S{Jzc#DmY zb+J-;WFB~xaV2=QvE=g_V{*j9_{2ssF3+3ByWzKtCDv<|N9BUo8CQniHtq(mHzo%? z_)%<>jB|tWr|?E&S$CV1N8bd$YkW6Meu#~31#dBa7~X13oIURuOD=6Qmi4k-IVC5& z!?-N`zOh{M17p^uXQwgo^pHbhDSP1$jem#nh1etc;XTInV9pbJWFSlo940qC9~*Ci zKQaCu-lsf9=8G@H#z>LNC}Z&)a?thYpYXTJV++8Cjhn(pjK%*)jU`Wx8RHMnapOPW@01_A13qCa zzB*~#55^Z_k39?jU`)Pvh@aSFT+ee_d0Y&9#<(0zE{cuo3ZFB62L92Q-1LxJV&e|M zKRM`n+ynn?To}G!+z9@~SmOAr@eKGkW4R{lO6>7N@b5x=yi%^ooE!20rLGG;w_GaD06Zx-c=Sz-Pcn^+#sX513aZafqw z*Tp8zfpZvB54<-UlV9GP%9CWAxs30EV~tzExsAubd5m9#Z!vxkzSUU9d7JW+%+Gtf zaVa>jacelvcm$l!_yst>v0Sr&v0SsD@?`wyEo2-I6DP6B_rc`7!^7bs#!^~4`DzWmGHs*0J*A<)E z7QWl~5xA`JGPs=ahcIy!n|cAhNBJrI>aAd05$1ofr;_1H#wl=RV~J4};~j8SV;N^P zjTyU_btpEyJKWTG9DJ|wOK>yeci`s6d*K$!&&W8*6|raVx0e_@ z+!ZF*9i9v`ZiiREZH!OC#82#5){VEFF~0F88TW#ThuE`E!uK0X?mb|93U04FBNyDk zSYq1ISn{NkF}2a#*?1D%#drzKxW#6?2NN@gB`)~L;osmM$}@AoJ&nn6FJl#(Nj$y1 zjTwu#kFl)HzQ!})e#WoD{f&3R4;mkb2Pn^?@iK0)S;gQ%#>C${*qFI`hZsK$KV&R@ z4K-c^v-ZSh?S_XNODuSf*lahnDOoKIOB5gj?A^0WZD)1s>>Gx%0^3=Q7So&RJ%ox2(jo*S_G5#1{W-LA@zr|kg!pn{G!LJ!t zg@`_lBy&yhcWsIM^tBpC=yT*72%>2b(kh#BU{0q$Z#O6iAYmIM%*BMK` zy={CSyxv%9#XH6__YKA}_l?GA-c81b;CGeh`{B*TvSzm!H-@(w_lMszCXc+^j2XLk zyYc5Ra}%5YC;Yzh0`cJo#)Zoq z%z0ujJ_vteOpbe5&tfm)8}Fya-@yBo7kc2&j0?idL2O||_zU9!@Bw4-=a^ zCyd3PCymKF@At+s_aBTe!>5#Aj)PAd*MQF$ON`DMQ^UNh6S0?HgMT!Z7@ar%8U9Im zac-EO#THkDFBm7mt_DXL_kg2~pN3L$qQd*W9H#w&SJ~*!dZ=FoY{}m;qu1hxQ}>){(aSq z+r#9L*y<-?*1W@S!ZnTMI<=J7$XpVQtHHI62f}rX=fZW3x54#{e}L;Nzd`=`8W>By zH8k!3H!^-4Zfv{^ZeqL-ZfblHzE}B8^3K=Hm@)dA8~27=7(Wf)XS@b(Y5WaLj*7kI zhg%zy7rr*eo#3{{;&bvq>@8}GFUj~bINA6zOq|5ll7l{S*J0-5Yi~RlX3k=3XTcqf zx5D%%w)QmKS$Q4n&)3D6IQzO9_kg<@KMQv^-URcQ*t+jw@?PxiD7cq#2^b%Vy^YU& zeT;{}eT|=o`x(Cn_cuNXKd8K3t~tQCG(6C_Jv_+xNtii{t$!UJVk|LzNckNa-%w-m zKjRR42Os-}8_T+T*!V4Ygz-^$r1FNF;8DipzHhWKKJcX&i_af1-VTp3z5qX}yzzE; ztT8$0d(4=AeB+Fl!sM&i#uM;(1A9E1fG!LHSplj1dFg_C7^e0T7 ziM?A4o?=YS`lcGwkMAjC@xwIZQ}ENun{&d`jqAbB7|Zy{6|v3B;Tgu?z%!M%$XdWh zVp|g6*~ZE69AoC>W6g_gc>{jVSYkomh;7XRzhGPro@Y$X`sN#RJ>LT3J@AXh7vY7< z?-hh!GOiCVGA18=FB>yP-(uq}@Dk(i;ibykvcj(z-vci*egJ;en3((Ui`X_a-)qL7 z!sM>lHi^*+%h0#SjM)-_+uDEO8{y=$Wet4&GeR!8~Klnpq^4hoCn7R7M39+3g;l0Yc za={-NSA_AY*sk{QCl0!Hv7UU?7qMOV&&QY?7GLdG{_qy~Gh^}j=f*?eFO11G-vMK; z?fcUBGJH^ZcS-mw6ULHz^dYu)9{jz7uD$ru$Na_i{sB`j#6BtkpEj1YaK>2j2KDgmDGfW853|8b1g7jCa9y zp-&l?KcM>lVsOy74IF7a4vsP=2mH~-#LZ9NVxQ%L@rA=R;LOH@;Vi~4!K`hu&&X>( zu^0P12b|rwE_{=*JeSABK7SFu*;t;NQ~3+V?x%0DFYboPMTfhmbLGs9dm-1H$aB<^gnE8l(H32SZydI7>W_|d1j@Z|X z(O=3~;$GT#5?sc31B`#gzGi*+%PN1voc!gCTf*gyC&Kp_v+n%lrPw#}T-J%$A$;a% z&JIhzm5rZ-s~B&Bt15p>F8iw)i?65`V&C?G$vKB#fNL5{p43u4OwRffjp@f<+xS7a zj`0GR{1ZET0IsKegq-!)H>UMBFdhy!G+qTaGCmDARz51eZDQOGZfZOpzSnpy+|2j~ zxViE%a?npci5=s4{`-u_!uVS3*c!N%@e#PS^6}g-J{3F8_59?t!{S@U;qW_flCk6i zc_a4SZSeiZ%+LRT@o2cc@d~(u@waeCio*kq z@r{3wvG{GU@!Rka<8$ys%BNWO{-MVB+ds_sahO~YJ0(7U*jUCqLisf7-apb<`WmyzHD(@uaz*USeE2crT`<=bJ98Okd}3!=(|+>O zVSMR-!gw}3!FUHe(fBYt$@mhCkHpU9fhQYN8~js@$#MTwW8&t2%9wuq(~Mt)iLuzZ z_3(7#FW_g4FTl?#|CkM)VJto*#$rF#foB5Q^5MFC6eXUcz5C^k<#4gl^iLt}1X+LYjVRFa6!I<&- zHyVEmZ!-QBepmUISa`E>Wtee@{n7!ZMmS9F``dqL+oN9_%q`s@aM)uVAhP-#pmDy#>Bz@r7^9a>xx~Jaek%zdmQ|=aSiwz z#?&qUdE+IpEJWE)j{;CGMjqisej8kBb@k|)Mi2cP{4ET({g#E^hD_|GBoEr|Rez^o3X5PY-od>FrD zZWrq;kjq%ENi7iFT@q$K4kyETj3>ai7_WnGHU191O*w-81GgK?c=8$#fa8p142&Cd zN5~xW8xzAo0cH7YexRUnTbQ+qxjnPs!p6JcJB-PlKoMp6tWe-i(Zh_*;^81zm@e^I%7Z2bIhX=y(#*5(u<4@pH%JNz;P};Z>T*kOBe7EsDxUBIG zxSa9NaCv3%P2e8mCU6B~$(f4AufX(yxdYU&KxJh~!9W#b#u%t-+!d~7JO{3BybWfa zm^*kLCT7BsSn}8YoAxCIStOiAkW5 zF^>lt8~1{lKjw}a2h+F1)S1A&#_z+;j8DMLm7~e!Knr7u1$ltEquanOjUR$r8P9}U z8@~;69_Ehz0w(_)z6iHdj$sW2l8lK@fOUeoW6%QB6o<#a4;ViWw>RDbcQ8H)cT~<4 z19vhe?*g5T@oS)qF>QcYV(v_j!rhD)z}<~E!99!*!99&-ec@BgojDiW+xQ;1k1^v7 zkgu3K^J8#7frd;uP#oGlN`b1-+d z^6*$=awLE+9G!6m$VG>rgQ+PFuZPDQi%*^~{sW$%oIMVnXe?u%WK4VlPa1Q*z+~fB z;VH)ZVAdGs&i)(xl=4lt!qbeYD}kqt$^F1|V`36`#+b(g&l<0UXBdA1&ourKo~4{a za&oq@_CUm^%mO2FP`X--2H-{tlj}d~;5CzHvR6*kkUSiEZFT<5yr} z=I96Dmy~nLcorE~gkLr$-vf({8Dn6H@h*6&@t-i)#oW2>gqIm7!}!|K@qJ*qvE=z{ z#+TvOm1E1mD~vnB_z`o*5|h9x<1O%N<6q%5%DD@}Zx~Bny=g3K4S!+o-0R@A#;0L^ z7Clcv_-*5RVRFvVC1>6-mNE0cqo0R2D&HdgZZf8h2HrIu1#dQ%d}G|0`%#|)hr;*^bH@?Sz}Lp#!`~?9lbklXJonLbAgfa06Fm6Zx5oR311q#7G7`KJ-ucJ?bPa8}AoH3@Kz**&j z+2M1>Rbj4+xeNAz&l}H%e=?RF_}N%$EdOKfLbt=e7&n1`H69E9W=!q}E*j&5!0*b1 zv%-HESA#DZ_lN&9UI_nXOl$*}mG20;T>K~IzN0*hPaP(QgAvAaV2?358}u4;?I1aX zxr>y9{l?@^FknpG2x#`rjxL%G;p@Xf~UVR8|37n=#^GA3t&vBuJGZsoh~hVvNr zgjoZa`>q$@TaCq6w;2<+ATh?=#qWgk8k29qIO7R$K4WTFFuyUm87!b&f^h{48aIH+ zUCdqLQ5Ziu{5E`t@kzLda!KY9q;Je!vNl}Qco1C7coB?WFn3AvBv@QI-Vc{BE&~&9 z%pEU2Ck75b4JR0Hf=d~*CW57v6PRC+=VR`KdtvFd12OCSQi6!Q)LY)XQH;0K^aHz5PYMAk>FxSG|RV2?D zlWdCYWy5L)_52EnDGzr zIOXb5@Z-kC;qk^2_a}_yniGsA|0f#14o@=P13zhe8s4*^jCr0haSqNmW?aDq#&5$f8XtxiDku8kmyCS!x9b?v0aDy?v4{kKx z3~w@)9D7%}-pw$NVeWb|wk^i2-QZT^dGLG2v_a;Hx$8;Xw=37r0q-!b3cqhGb0k+V zcl{^foyIc9UB+L-A1XHp!n=*j!h4L{!h4M+CqFXAufdOv55b=(H;jh&8Q%qeYAkuU z-&lP0nK3yV{M?v%2fr|uI37@Llno{xn7dJF_@Hqz{FSlf2J^w(jfi3J8)F_19#U=` z0h522yYX%CVdL`f5#v_yQRBhzG2^G;It5BL}3C*WU= z7s9_8lbgYd#*+WP8()I|P`>vTn4dBCy;a~pjb$!>84rV*Kjyx7uFDnaAiN%CEjju@ zn0PqMJR+%&!p&k}ukjtQ&$uS+H@+VZ7(Wb?yO_J#Gq85%Zie3?qfFlkM;qhgNczCs z&B?3COvXjw%*M6gEXF z4d*h(|B6c$kMe!wPb7ZF-1k+3Z#C`(^BCs7?`imUV;NgsWASaAa!VeM z%x5hA%x_F?L>4ff1{XAb2QFkRbENiR?pF9RlGr#bb1Y(<1m9^q4#uaLyVYvAm@#WT z@-F4p#5S_HG3z?Agt5f8r129lePHg^OW_3LJ#Z;w@fFX<+->kpWEtZc@ZH7|$Fjy# z;Bv+*VAen8Zu2F4k8)e`D6)bv=Q`hy#W`);!Id2S?;d1Axa+Rlf8Z~M<;40_b z@Wa)(LW{)KElPAs|5AJdaEs>6I}Pa3e^B}-rt_%NPygQOoZ)gcY8BVC%YgLHN$&zF zJgDDU+oNx^5xFAqx!Opbe!36pbN!j1d5~T|Swr*bnf`#t$#jRD?t?Y}XFh$> zO@s7?>O6MM{oLRhyIMafjAp>V0avLQSDnsPPiSvvl<3sE!@z-8KK*L(x$-mr^^p?8 zd-VTT&jXp;?#V=?3s>kIR#teg<~wmhOcz z;K9!QI;H;tDFgaw&o%z3z2v~|y{~$oYnmwE5G}9yuD@ar95nbJ=||jOf*?uz@3N+L z;S&AQ?;#`hA<{#M{{8>0o00Z5jS~Onq?3MhuivQ4fNocOt0(fmZLj~oagX-ndSABn4&I!K)y+STrt+)T>uiLNFYwRWe-s|@3?nmPHpYpmrBl69e z&wtVD_EtUmmM#@Hw0zks@AY$4#jdsvn{@0MdM}BKUG41u`ElK={Lhbt)NoC|LN6J< zC%WN&u5nLv!~LYYRguq{=pOIJ`$@N>4C|+JmmB*4K`3F;X`Osa_nrSVd*kc$TX;{D z?oK5m`|1C0jZb(!{RVVS$F&*qP-u^r-oq1|L{sT!(UU}a6jo!D7+`S;eOKHN@k$nu5r(Lqx1Psd!O|>=M&z8U+Zg%8}8@d z8=vreuJvZ74L1K2bzZFbCutUUF~yLS9@Ig`)_-n zRp#_J_TZ8U#hb{JuEYMZ_dh!iOLrHZ5&J5c{?$Gol@a~m@FvaUTBo-;j{o1dHC_xd;5Pw!6u%x@qdweWAfM#_MG((~Mn`Yr8z z(hE_>_0v1ug57jLzy4hY3>wBez5@rv)$G@&e~;d2_i_L7c$)iCTGaUTPWK-}{L6K2 zq@RD_wWTwkhk9M{9}c9Q`;GAb#@DbJvF{b1|EIi$&9M1gAu6@{5zl%{|8l;|U4);Dpz}Am(%J%YEY_~?Ig$Fv(|k@N zQ;J?ctzP&Mm&+?U*H5dvWw>_zf6<4!ToHHm=|7Nu)9Budj5BZVK7+b3{xrI4<0Q_n z*0dq->U&Nv`~X>_mrO^~eU60cM}=J*Hv zT(Hle-tsApRNYnYUVb+J>}(0!Up=WeI@LdYm z%)#PVh@5wkmtqqRajB2UK_d ze!8CT^*xAwyXn!icK)z2=y^?-Sg79k2Xe#xqUwP|wCbP6ofFU(sqXxp!Sc>H8$War zeX;7!-+9cWdhfC5OYHnlX=D2E_g3`h(9tWox2#Wx|8ui(tODyU$+=F27a5y-qdw#N zXBkd}>MQO--=uogGPF(l^!q>Sr1Q5O{SCSdE8ue7!gxZKDGL-@@Axfb?FXk>?`d@J zAkGM__cXfm9mb*co<@)4I^p%6M%R9Nkz9SF$>j zM)%F*5zU9lcX@7vdWf*IkEw@Dnl*iLKXeDeT$5!1h<)%6?s?OOCZO+QAf`5zVi zHM-O(tiX>PGV3^3evDqsk=+fLpIgTnxt&~!AQ#1=E}&P_^Y3qnZudLqySnW<&$mU_ z`<=TX<8mL+^FR3vy`)}e_;~z3S@mA5-w6CJmV)1W7RSCF(OHvXnK;L7^PM#ey_D*E zx>H*Z>UGA%qL)_P-=4Ot>Jzu1%lg5#PU2oTR`oVKH{x#9TaXtXyMCv$=w(%(h@X5# z_57jK{D^X@-~1Ep=c*q~L@)2??&wqGPfkaVDnTuYxJS=-z6;axq)}gV*1wqZU5py9 zsJsi&$rVQ@S3~j2*&p5R@3C@U`at_#^)h;8=X6(mHvAK-dfVLSRUF-&J%T>=s=j?2 zdR0BY)Tf+p@oKsNy_)KGbKi*2Jc;Z+8LqCn^IeHH{-0S}5j9LdM|-1E?-&>Ov$=}EMf&*ht-<2y0uyXdUn5sWdSw(2nwpXc@Z@5y?sqxv^o&!h8-%EnrW zsO#wdsgg&v*$3h?Ha1~%zPCo>>whUL=Qr1fobRPj|N9TKMk897F6&;$<3BM5{XWwr z-_>vaw{Ak0Jq?zt6aLio;=4qQB3RpE1&NX6zkdd~9KqTUi+Y!Xji(5?88OO{_4{LN zJU^{KAFX;6@d}OSA<54a)nnv7Z{xX7;zI2ZOV|@0kN@py=*&T^PJ`6(+>9?H9#h@< zt}KnCFX2se#x3T2x0A-*H=8j>ELL5DFCpGrk_=_$MKr+Tg3NdPVLhw%N~gd%Vk4Pw#69ZR5}zseY7vu>D5-I_j9OvFfLIuI)Dx28~76xVz6TLf5*K@Hp$* zr*YTs2GjjU{DB?l8h8EP7p>0;Lpq{c+*_4quzKH*KSN#eY25YuMYR4T^!^fE>!KLbRtK5-SzzVX# zi7UhVmV`$W(T6y?D^mPy^(3i1`b&-;QEyDRp2Y8xd|RaYy}!U(pX2vbhF?~_l^?%Z z-k!i`&g%;LP0o{APi9ijeM|KGMO-ITPfngeC-1R{*I1jOdQ!hQ`YWohBv(WA{>LZ=Gouc=-@){WJZ#iP+*cl3xg zRbZ~*AJ!0d&)OvD~ zn(AAl`j&&K^(5c(=x^wC3R2TT^`!l2bYhG}eDYSfo+QlLjLy%Ze}&IO^<<0GhqbC7 zq8?j4i9hfZyw1^GZ6!aop2TmLb@8^Ie~f$y)svwz&h>hJ!REA9PnO<`{*LLAccy3h z9G&_lmPP7=?lTigO+(-4$gTq9OemjQOTKMV{Xto;R!<_QqB94v1jc6dB>vARbmojj z{4gn8AL2ioh`zS?E4R41MIEShczwte%V}hkYM9va7(Wv{p~L3`F0pdYNBoL+e7WBk{lr z&fs9j^m*3ha}9}hkm!}DT~l`e-eK-7kXJo*YBy)dXg}JwdgOWdL?o@k{@vMVt)46(ANuaf(Wz6SPklCAPZDnOpjTCWIyJ=hC-DcfqgQiuSJ#WQ zx<84(&;`A^p1+Xs*#0D861m~8q57+T(TVLFWgqCTsrm}8Y42Ag+C3>kZD|k3U28WV84|@(g_g z`D^==`1kKXmvN(i#X7cn5`TCLEcF+CBk>8VlC3y;D?PuPtYceu17u#URbNM6p>l1|K0M`dNQBb`tMi$T>fx9 zNqBN0`U9$8CuslzlPqy(Y;N%p4F3tY&+09s@^(;8BCHfFYcO|?>YxSh`^XRNeu>q3*md_1ZqYrgt@63XMNLEsGjV63Oz;jm1AMO|0diP1wW$tThqh!B>qxe z^f9Whn@4N)q|0LTM@`>MYxShvR&=h5dDk7{X!Yc-J?M{#OkJAI!Rkp3eB&SI$gX2? zw6-qZiA8^0^#u|~^?&@Aa$g?r=-#)p(^@@QgRlHgnEpCJ-m7uBiRbz!IJ$q@v*eY_ zeosV>g{-y2IrM%}8hsCQ55I4dM$dK`J@md!8a;a!^c?nnQ5rorYuV~p;_yn0=R1DJ zKO0jx)N;m|xZ^PT3DvjYL)#bCP9Qh@QkOY($39rsOX4iX;Q!vyeN_&G>sakMxW**E*K?OdPu8F&0Cv+V6>K7hi&Y){(uR z{7$R!O6*q={hXuwBwkj>5*smh|BsIDu5lNw*0IF3J<%B-=6i8C2i+GX&a8s|lj=)? z=(;aT{Bj5S&yMch$M`MZ63@swlJRls@~q)HmUxM=`F~OUwSo-auG0|T`hQh@72~lw zmN=j1`l&~lZwvie9jl%D9rTN)Gd8PZiDPdsaDUYGfc<^-C$?I#&B8YEK|W^}i<3n%~;K zf}Y9r<+NJI5}&viUHrv({@TS+>saE&Rp_$LMVI(!9jl#3{4ah)znqh!t&4VWAe*Cm zHs_%=|DUH01hPB2?{Yk?*0I_JtD%!ynD6p#^2+?Mt1G(XJuZ;%*bLVt9lPlM8PD>h zP<>0)T~%2-;rfYkB|A>6O}2Ei5Rvmi~ssk*D$SRM`4 z)iio_$)ixeX?2<3tyVwN=-Hn@kFwvdrO~TyLf3U2Q;Hl2)Z}OU;CUW-khAWhcifI% zOZ6E1ZFMzz4c81Ls-BI!wYnPpVL|lTs^=;je*Y?ZisV}z)jMz<+aE?RqlN_Ps@|gv z@zS~)eUV%X)Kk583v{ikF_~Cff%=Z_$|U)#`@`rB#bktS0CUO&>(? zb$=MWVJ~_kNB8bNL92B&`Y3${8teJ}S2JI&t1-C272sN!e@G2<^IM7W=uK6BSjMIK zA3Z1^I%@#)Kir*u^}ZAR;Q;hzs*e~EuB*}Wi=(sV<@~wiq1DysO4Ql_^A|lu@Qk7P)z#>>aG<^FZ!U*V>G^fpTLn6(K6MnW*43E! zT<9HD-#9z<{H5jLPImtH#7pZ^^zjt*&Z=)8$E3BMM7L#*fi9}=#@|*~qc=%@$~x!N zJv`6qYV?$a=-pKRlsvT8o6%oAf=+(O`BNBA$e-t={`W9_J+0-{xcAX}sy;1(R_kiC z{N5zc%g#SQYvX_BYxLfBei>R@7a!e<-p9^APpkfqx%Crti3j8PEQO=i)#zhV&*=m6 ze|~$ou14>Y@l!i6ca6#%g~3!gV!OcU7LoqoMkiMh}!i57oCcdX?n_WVmr}|OiZ*?r`I5`%WulmUWJW1DS(j{3( z3sgT#-0k;zNo&7Ef6>tc_i!DnW68ttZGaeKf$C$!bu8(^X@r+luf^k5$CBIaLto_R zu58@9t&Szv>xcfbqx+{drqy^Qz0WlQiyb{sXDQCsI+pCB#s-$y`I1Lk$C4hG^~iZx zpf34rbu4L7O$6@QqBkCju5~QAI&%svb9Dc-A+(xrNk`@SuR405c?$iif0EnZjLv$) z0xhP8>sZoid=+?2^#SR9s7Z_r;?z4csLtz*ggZ$hW0V1d>jhwE6{1wG-Id(R>zX|F{i*As&}nH ztMw|mb58U(Rqu5utS619Rt4U2bl20>XtjN8pQR>zY24@Tdl`mC||LF-u3!qVvPsy?p;=Ue{lxD$P|>hn8tzU~i`)?Ps0qWa6M z7pr5*Wn?^CRbPUiL;kGx9Qu2v&!g46N^UU&UFJ`-MCPG&Ea@wJ8Q5<6Ia;k_$=S)B zzz)@y25EI&Bv(6zPEEuDeI|2M|0jKv3E>0Pmx|xCjwSsmdArl}P2oD0^kGr-U5@Tr zH-=W%Mbg0$=pX9&%cq9xNz%*2FtA(oH@)F{l3ck8`X1HS=SZz5KXgLhtNP|5;d+uB z7?1vuqX%QSj@6T-H>1&64_Giq;naFEOY;8{)pONOttYWkAND!AE4%nx_eIHNWxao@ z=jUliYxU%Vr_lGC-ZizJwB}wP_)PU%7SUQhd?b1Dx#_D@>&eH&F7Sow`D%pgNz$)! zzTAfxf8qKZt)4tN2b~(~=-tEhB15ICv&eJyFNo|L- z)OwOMeWaYD`u$Pi^OG*KCIY8b@A3=rvh|o%=5qS0st>vpt|v*8YoU|tSn#1BN2|~6#m_&PUX<4ANhV?#_}TQj zv|3M+hfhJLW?{jHhI7>MCrv4c&U_qQ{Augr0I_yHzb@x5LpT55Ep_8JIUW5ij#^KW z@0p8EE{Z-B|J&=$q-V3B|L*94KC|Uf_5aW((Eo7s;E2M=`Z_DQMGkc4FK3SB{Lud7 zz4hpSs{X`M{AT-;h0^a|s!!Pvt|v(+??u0?`WIX$R8Qux9>tAV@LR5Fud|Xfmqc)@ ze)J%^zRpU{M$Cf|j_%5pOsn-IY4t!JiKCsKvcpHS|b+ruhkfg!U(O zjv|o9qF=}!t|v+FGxuP$>KF5*))W6n=rN8S8I-)RdNQUJdM4E)?@FyFKjV`id5=ZP zy(zRmIXD`f*o%JiR>rUUlcWnzqi0n;&-b*tKS^#?0X>`Qw`?Z9`Z_CV+;VirjYa0e z525_`??u1K^xsqU5{=QxAuKZgtsJ$UBz^S|I=SoU6h9hdg*6V z>q-6((PiA|Wte-Yo-C)<25(cn;;3*vNqTBE`t7P$nv_~kW>X)6c~!45H?^Kjn1ddt zdM$F#{F$_kwH(Z6`UkY3yc&j{-}Do-wm$<-c3zen|Lo5S@a>Fjgp6;$8BR%PMr{hDDw|z|r<6 z4R@g@s(w!Vp!FnaUU&4`rvIK=AJ$R>f^}5??KrK~lgR_o>#BZ{ScK|{+_Qr996d_@ zy_)S$l6I3D!TPF4v<%miWchq-uz~84g;VRvnxW_oRgWT%Y=4s6OY*sq>RFjz==Dws z<{rcsSXB0=9BqFxM8?y^^Z~S1PclnAk@MvIsT{4IY`q=*Uen2+(CeL&tcf7=#GV@9i^q*<1o|IdT-a_@8q|thkoX?MbpXvBNv_IMTCpvz?qH^`+X!YcQSs?d3 zM;`)PJtE=i1nom78@xrgdWjf?2~Ea%T=ordbk zzM1Gd9t-}L3Qwm;b;b>l(P-=fugPWojdx~w10 z{BSGB5429UB{zfA0?~I*=4ktqAbkY~nNEIMeMr7J1)Um)MeQe!q4!Tl6-8%0qJLH+ ze16hzQV$q+0T=p$4=SB=){^CHF&9BI1bqur;@S4AIXdJ3(*-ic|CKHBt^wEB7{xz&2~ z6w^PTwR-aLQ|OPFPE14fRoW z9vwj*T0Kme7epVgdhoCC{wn1Wsbf#5-i+&5Jxm$d7@nYd+e_hkn6m0c^ogq9e`~lN zrc9ye%Nh`?BG1!$m{MRb`jd_v-621%#yuq;IT;i`x?Is6DuwIe=#PIypQ3ul5wuzl zM}Ia4eX8kP-(Ih!kN=+sIqdWx6v>+7}A=VV^9Ri9pzR_kHPc5*C;FR!}Ty_ zS4s3ms=sFmLuhAE)zGhdr9;Qs>-W6P;`r8M?^)Th7V(3d% zf9C|P*29$7FQUJq`i3Z4%d4e_(3hEBgjVZeN*~r%@Kw{vAFGEcr6oU?n=byeb+L?` z558vlBwD>6rR3|6{<`UsM_Lb4dSpUp4P((8@xRr>6!|w*!Ih5gilXL+>svO}IJz5? zpW*tJMsJQDu5YQjD@y8gsJ^AuCBMA(d;e5DdW#=j*JJE3i7)qBEP4w$?5*Y8Pjmb8 zq7yUGw>M)vTE}AdF!taY)pvCa*Rj|=51_xH`d>A|`?}b6qQ9y7$3tL^SM1A8;I|w- zraAt%eO>Od#5yST7Z0}_9w^!1J& z(+0Nponq^eJHdAxJ^FyeLF-uVeAMzFd5Oie-Nbw+JAQ~=TnK%m>g^k&tKV{$$cN5( zSWK6e95vr!XX1n4yQUAKUwvOL_N_?t&8l~$-rM`wvF)e{LDn!9({pOLj>SI3UL&|w z_2CkCeVv#)tK`*trn8Q1Ul)5o{IgBV2-Z3RD2{b};S)?Mt#|F7(e-Uwv!1j>SI47=oXx{)W^gTNnMup?_g|eOk+_2P>l=Fugmi z*0I>O%h10xeKxI*KlVT`^n<2Lp4+;Z&OI>rmFbKvlQPkQ@mfa&*tgB-+q^iMfX?o8)z9|CT+Zx?E>ir{VgRMt@hv6Z*Y>8a*3p zJY3(>=v7*x7qt48M$h&Wx~@~t%ad3mS@;?IiGFRL=B>#3jm)b0CB|&^&C~u#bgh+c zH}SH4n&;AQ=-Nl-mG_7DY2Na=&~I|C7!|H>p6=9!NOB8{KCmfV-@I|;U}T)gTz@GCt#6(^XVHlb<}1Zou=?g% z^A?=n(WAeg8?JAjVO7x!sDAo&o}~LUPqPZ>)CkO1DT<@kH_se=9!X8dqR$f}d)?}J z;8}F)rr4H494wCS%t60HRJK+T989k#<0<0EuBy+`YJKx&X@Y*I>9XI^`sQgY{T6j} zUnSV~Y2L!I=*9H>tukKSr+I5|50T$1U@@C&hwGJR3ppEE+|k|9ooThcd0r;RBdJqZ z%#Kpw^S$L;qL*}ZU*$#N`sVp?3Oacy=kFzMR^L3USnrVujvoEnsc?PswB{O-TvyKD zeRPHlh0~k{waTP z)cWRGwGO?Uqr0Q`(rSJ493aOd$wkapWdlcB7p{-d?{Re3))ZRxzc-e87)efG(U<%j bwZ3`UF}BExjvjN6eyzTFI?DAciT?iq#uBft diff --git a/mDNSMacOS9/mDNSLibrary.c b/mDNSMacOS9/mDNSLibrary.c deleted file mode 100644 index 6328395..0000000 --- a/mDNSMacOS9/mDNSLibrary.c +++ /dev/null @@ -1,63 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2004 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. - */ - -// Define the required CFM Shared Library entry and exit points -#include -#include "mDNSEmbeddedAPI.h" // Defines the interface to the client layer above -#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform - -mDNS mDNSStorage; -static mDNS_PlatformSupport PlatformSupportStorage; -// Start off with a default cache of 16K (about 100 records) -#define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord)) -static CacheEntity rrcachestorage[RR_CACHE_SIZE]; - -mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) -{ - if (result == mStatus_GrowCache) - { - // Allocate another chunk of cache storage - CacheEntity *storage = OTAllocMem(sizeof(CacheEntity) * RR_CACHE_SIZE); - if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); - } -} - -extern pascal OSErr mDNS_CFMInit(const CFragInitBlock *theInitBlock); -pascal OSErr mDNS_CFMInit(const CFragInitBlock *theInitBlock) -{ - extern pascal OSErr __initialize(const CFragInitBlock *theInitBlock); - __initialize(theInitBlock); // MUST do this first! - { - mStatus err; - THz oldZone = GetZone(); - SetZone(SystemZone()); - LogMsg("mDNS/DNS-SD with Macsbug breaks -- do not ship this version to customers"); - err = mDNS_Init(&mDNSStorage, &PlatformSupportStorage, rrcachestorage, RR_CACHE_SIZE, - mDNS_Init_AdvertiseLocalAddresses, mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); - SetZone(oldZone); - return((OSErr)err); - } -} - -extern void mDNS_CFMTerm(void); -void mDNS_CFMTerm(void) -{ - extern pascal void __terminate(void); - LogMsg("mDNS_CFMTerm"); - mDNS_Close(&mDNSStorage); - __terminate(); -} diff --git a/mDNSMacOS9/mDNSLibraryLoader.c b/mDNSMacOS9/mDNSLibraryLoader.c deleted file mode 100644 index 584ff9e..0000000 --- a/mDNSMacOS9/mDNSLibraryLoader.c +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2004 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 -#include -#include "ShowInitIcon.h" - -extern pascal OSErr FragRegisterFileLibs(ConstFSSpecPtr fss, Boolean unregister); - -extern void main(void) -{ - OSStatus err; - FCBPBRec fcbPB; - FSSpec fss; - - // 1. Show our "icon march" icon - ShowInitIcon(128, true); - - // 2. Find our FSSpec - fss.name[0] = 0; - fcbPB.ioNamePtr = fss.name; - fcbPB.ioVRefNum = 0; - fcbPB.ioRefNum = (short)CurResFile(); - fcbPB.ioFCBIndx = 0; - err = PBGetFCBInfoSync(&fcbPB); - - // 3. Tell CFM that we're a CFM library container file - fss.vRefNum = fcbPB.ioFCBVRefNum; - fss.parID = fcbPB.ioFCBParID; - if (err == noErr) err = FragRegisterFileLibs(&fss, false); - - // 4. Now that CFM knows we're a library container, tell it to go and get our library - if (err == noErr) - { - CFragConnectionID c; - Ptr m; - Str255 e; - THz oldZone = GetZone(); - SetZone(SystemZone()); - err = GetSharedLibrary("\pDarwin;mDNS", kPowerPCCFragArch, kLoadCFrag, &c, &m, e); - SetZone(oldZone); - } -} - -// There's no CFM stub library for the FragRegisterFileLibs() call, so we'll make our own -#if __ide_target("FragRegisterFileLibsStub") -#pragma export on -pascal OSErr FragRegisterFileLibs(ConstFSSpecPtr fss, Boolean unregister) -{ - (void)fss; // Unused - (void)unregister; // Unused - return(0); -} -#endif diff --git a/mDNSMacOS9/mDNSLibraryResources.r b/mDNSMacOS9/mDNSLibraryResources.r deleted file mode 100644 index e3a200a..0000000 --- a/mDNSMacOS9/mDNSLibraryResources.r +++ /dev/null @@ -1,197 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2004 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 __TYPES.R__ -#include "Types.r" -#endif - -/* Format is : - * Two-char BCD major version (0-99) - * One-char BCD minor version, One-char BCD bugfix version (0-9, 0-9) - * development/alpha/beta/final - * One-byte non-final build number (0-255) - * Version numbers can therefore range from 0.0.0 to 99.9.9, - * with a following build stage and build number (e.g. 2.0.1 beta 219) - */ - -resource 'vers' (1, purgeable) - { - 0x01, 0x00, alpha, 130, verUS, - "1.0a130", - "Multicast DNS & DNS Service Discovery 1.0a130" - }; - -resource 'vers' (2, purgeable) - { - 0x01, 0x00, alpha, 130, verUS, - "1.0a130", - "developer.apple.com/darwin/projects/bonjour/" - }; - -/* We need to load OT, so make sure the system heap has enough space for it */ -type 'sysz' { longint; }; -resource 'sysz' (0, purgeable) { 2500000 }; - -resource 'BNDL' (128, purgeable, protected) { - 'mDNS', - 0, - { /* array TypeArray: 2 elements */ - /* [1] */ - 'FREF', - { /* array IDArray: 1 elements */ - /* [1] */ - 0, 128 - }, - /* [2] */ - 'ICN#', - { /* array IDArray: 1 elements */ - /* [1] */ - 0, 128 - } - } -}; - -resource 'FREF' (128, purgeable, protected) { - 'INIT', - 0, - "" -}; - -resource 'icl8' (128, purgeable, protected) { - $"FDFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" - $"FFFF FFFF FFFF FFFF FFFF FFFF FD00 0000" - $"FF00 0000 0000 0000 0000 0000 0000 0000" - $"0000 0000 0000 0000 0000 00F6 FF00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" - $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FF00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" - $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FD00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" - $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" - $"FF00 F6F6 F6F6 F6F6 F62B 07F6 F6F6 F6F6" - $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" - $"FF00 F6F6 F6F6 F6F6 3A16 402B F7F6 F6F6" - $"F6F6 F656 F9F9 F956 2BF6 F6F9 FE00 0000" - $"FF00 F6F6 F6F6 2B08 4116 4008 F8FA 2BF6" - $"F6F7 FAFA FA81 FAFA FAF7 F6F9 FE00 0000" - $"FF00 F9F6 F62B F92B 163A 172B F881 FAF6" - $"2BF8 F6F6 2BF7 8181 FA81 F6F9 FE00 FD00" - $"FF00 FFF9 F6F9 F9F8 2B2C 2BF6 F6F6 F8FA" - $"2BF6 F6F6 F6F6 F781 F956 F8F9 FEFD FFFD" - $"FFFF 00FE F5FA FA81 F7F6 F6F6 F6F6 F656" - $"56F6 F6F6 F6F6 F62B 2B2B F6F9 FEFF 00FF" - $"0000 00FF F5FA FAFA F6F6 F6F6 F6F6 2BF6" - $"F7F8 F6F6 F6F6 F607 3A16 39F9 FF00 F9FF" - $"0000 00FF 00FA 81FA F6F6 F6F6 F62B 2BF6" - $"F6F8 F6F6 F6F6 F633 1C3B 1CF6 F6F6 F9FF" - $"0000 00FE 0056 81FA F6F6 F6F6 F6F8 F6F6" - $"F6F6 F8F6 F6F6 F62C 163A 3AF6 F6F6 F9FF" - $"0000 00FE 00F6 8181 2BF6 F6F6 F72B F6F6" - $"F6F6 F62B F6F6 2B2B 2C32 F6F6 F6F6 F9FF" - $"0000 00FE 00F6 2BFA 81F6 F6F6 F9F6 F6F6" - $"F6F6 F62B F6F7 FA81 F8F6 F6F6 F6F6 F9FF" - $"0000 00FE 00F6 F6F6 F7F9 F72B F9F6 F6F6" - $"F62B F756 F9FA F9F7 F6F6 F6F6 F6F6 F9FF" - $"0000 00FE 00F6 F6F6 F6F6 F6F9 F82B 2BF7" - $"F7F7 F72B F8F6 F6F6 F6F6 F6F6 F6F6 F9FF" - $"0000 00FE 00F6 F6F6 F6F6 F681 2BF6 F6F6" - $"F6F6 F6F6 F7F6 F6F6 F6F6 F6F6 F6F6 F9FF" - $"0000 00FE 00F6 F6F6 F6F6 2B81 F7F6 F6F6" - $"F6F6 F6F6 562B F6F6 F6F6 F6F6 F9F6 F9FF" - $"0000 00FE 00F6 F6F6 F6F6 F7FB F7F6 F6F6" - $"F6F6 F6F6 FAF7 F6F6 F6F6 F6F9 FEF9 F9FF" - $"FFFF 00FE 00F6 F6F6 F6F6 F7F7 2BF6 F6F6" - $"F6F6 F6F8 81F7 F6F6 F6F6 F6F9 FEFF F9FF" - $"FF00 FF00 00F6 F6F6 F6F6 F60E 3A16 32F6" - $"F6F6 56FA 812B F6F6 F6F6 F6F9 FEFD FFFD" - $"FF00 0000 F6F6 F6F6 F6F6 F640 1640 16F6" - $"F9F9 FA81 F9F6 F6F6 F6F6 F6F9 FE00 FD00" - $"FF00 F6F6 F6F6 F6F6 F6F6 F633 173A 332B" - $"FAFA 8181 2BF6 F6F6 F6F6 F6F9 FE00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 3232 F6F9" - $"8181 FA2B F6F6 F6F6 F6F6 F6F9 FE00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 2BF8" - $"F82B F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" - $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FE00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" - $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FD00 0000" - $"FF00 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6" - $"F6F6 F6F6 F6F6 F6F6 F6F6 F6F9 FF00 0000" - $"FFF6 F9F9 F9F9 F9F9 F9F9 F9F9 F9F9 F9F9" - $"F9F9 F9F9 F9F9 F9F9 F9F9 F9F9 FF00 0000" - $"FDFF FFFF FFFF FFFF FFFF FFFF FFFF FFFF" - $"FFFF FFFF FFFF FFFF FFFF FFFF FD" -}; - -data 'mDNS' (0, "Owner resource") { - $"0644 6172 7769 6E" /* .Darwin */ -}; - -resource 'ICN#' (128) { - { /* array: 2 elements */ - /* [1] */ - $"FFFF FFF8 8000 0008 8000 0008 8000 0008" - $"8000 0008 80E0 0408 81FC 3F88 83FE 7FC8" - $"87FF EFEA A7E3 83EF D781 81ED 1F02 C1E9" - $"1706 61F1 1704 21E1 178C 33C1 13C8 1781" - $"10F8 FF01 101F F801 1018 0801 1038 0C01" - $"1038 0C09 D03C 1C0D A01E 7C0F 801F F80A" - $"801F F808 800F F008 8003 C008 8000 0008" - $"8000 0008 8000 0008 8000 0008 FFFF FFF8", - /* [2] */ - $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" - $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" - $"FFFF FFFA FFFF FFFF DFFF FFFF 1FFF FFFF" - $"1FFF FFFF 1FFF FFFF 1FFF FFFF 1FFF FFFF" - $"1FFF FFFF 1FFF FFFF 1FFF FFFF 1FFF FFFF" - $"1FFF FFFF DFFF FFFF FFFF FFFF FFFF FFFA" - $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" - $"FFFF FFF8 FFFF FFF8 FFFF FFF8 FFFF FFF8" - } -}; - -resource 'ics#' (128, purgeable) { - { /* array: 2 elements */ - /* [1] */ - $"FFFE 8002 8002 8C02 DE73 D19B 5299 5451" - $"4C61 47C1 C443 C483 8702 8302 8002 FFFE", - /* [2] */ - $"FFFE FFFE FFFE FFFE FFFF FFFF 7FFF 7FFF" - $"7FFF 7FFF FFFF FFFF FFFE FFFE FFFE FFFE" - } -}; - -resource 'ics8' (128) { - $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF00" - $"FFF6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FD00" - $"FFF6 F6F6 32F6 F6F6 F6F6 F7F7 F6F6 FF00" - $"FFF6 F632 2233 81F6 2B56 81AC FBF6 FF00" - $"FFFE FAF9 2CF6 2BFA 2BF6 F6F8 FAF8 FEFF" - $"FFFD FCFA F6F6 F62B F8F6 F6F6 3333 FEFF" - $"00FE 81F9 F6F6 2BF6 F62B F6F6 4040 F6FF" - $"00FF 2BFB 2BF6 F7F6 F62B F6F9 32F6 F6FF" - $"00FE F6F6 F7F8 F7F6 2BF8 56F8 F6F6 F6FF" - $"00FE F6F6 F6FA F6F6 F6F6 F7F6 F6F6 F6FF" - $"FFFE F6F6 F6FA 2BF6 F6F6 FAF6 F6F6 FEFF" - $"FFFE F6F6 F632 3AF6 2BF9 81F6 F6F6 FEFF" - $"FFF6 F6F6 F632 4632 FCAC F7F6 F6F6 FE00" - $"FFF6 F6F6 F6F6 F6F8 562B F6F6 F6F6 FE00" - $"FFF6 F6F6 F6F6 F6F6 F6F6 F6F6 F6F6 FE00" - $"FFFF FFFF FFFF FFFF FFFF FFFF FFFF FF" -}; - diff --git a/mDNSMacOS9/mDNSMacOS9.c b/mDNSMacOS9/mDNSMacOS9.c deleted file mode 100644 index 24b03f2..0000000 --- a/mDNSMacOS9/mDNSMacOS9.c +++ /dev/null @@ -1,687 +0,0 @@ -/* -*- 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. - */ - -#include -#include // For va_list support - -#include // For LMGetCurApName() -#include // For smSystemScript -#include // For ConvertFromPStringToUnicode() - -#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above - -#include "mDNSMacOS9.h" // Defines the specific types needed to run mDNS on this platform - -// *************************************************************************** -// Constants - -static const TSetBooleanOption kReusePortOption = -{ kOTBooleanOptionSize, INET_IP, IP_REUSEPORT, 0, true }; - -// IP_RCVDSTADDR with TSetByteOption/kOTOneByteOptionSize works on OS 9, OS X Classic, and OS 9 Carbon, -// but gives error #-3151 (kOTBadOptionErr) on OS X Carbon. -// If we instead use TSetBooleanOption/kOTBooleanOptionSize then OTOptionManagement on OS X Carbon -// no longer returns -3151 but it still doesn't actually work -- no destination addresses -// are delivered by OTRcvUData. I think it's just a bug in OS X Carbon. -static const TSetByteOption kRcvDestAddrOption = -{ kOTOneByteOptionSize, INET_IP, IP_RCVDSTADDR, 0, true }; -//static const TSetBooleanOption kRcvDestAddrOption = -// { kOTBooleanOptionSize, INET_IP, IP_RCVDSTADDR, 0, true }; - -static const TSetByteOption kSetUnicastTTLOption = -{ kOTOneByteOptionSize, INET_IP, IP_TTL, 0, 255 }; - -static const TSetByteOption kSetMulticastTTLOption = -{ kOTOneByteOptionSize, INET_IP, IP_MULTICAST_TTL, 0, 255 }; - -static const TIPAddMulticastOption kAddLinkMulticastOption = -{ sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 224, 0, 0,251 }, { 0,0,0,0 } }; - -//static const TIPAddMulticastOption kAddAdminMulticastOption = -// { sizeof(TIPAddMulticastOption), INET_IP, IP_ADD_MEMBERSHIP, 0, { 239,255,255,251 }, { 0,0,0,0 } }; - -// Bind endpoint to port number. Don't specify any specific IP address -- -// we want to receive unicasts on all interfaces, as well as multicasts. -typedef struct { OTAddressType fAddressType; mDNSIPPort fPort; mDNSv4Addr fHost; UInt8 fUnused[8]; } mDNSInetAddress; -//static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { 0,0 }, { 0,0,0,0 } }; // For testing legacy client support -#define MulticastDNSPortAsNumber 5353 -static const mDNSInetAddress mDNSPortInetAddress = { AF_INET, { MulticastDNSPortAsNumber >> 8, MulticastDNSPortAsNumber & 0xFF }, { 0,0,0,0 } }; -static const TBind mDNSbindReq = { sizeof(mDNSPortInetAddress), sizeof(mDNSPortInetAddress), (UInt8*)&mDNSPortInetAddress, 0 }; - -static const TNetbuf zeroTNetbuf = { 0 }; - -// *************************************************************************** -// Functions - -mDNSlocal void SafeDebugStr(unsigned char *buffer) -{ - int i; - // Don't want semicolons in MacsBug messages -- they signify commands to execute - for (i=1; i<= buffer[0]; i++) if (buffer[i] == ';') buffer[i] = '.'; - DebugStr(buffer); -} - -#if MDNS_DEBUGMSGS -mDNSexport void debugf_(const char *format, ...) -{ - unsigned char buffer[256]; - va_list ptr; - va_start(ptr,format); - buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); - va_end(ptr); -#if MDNS_ONLYSYSTEMTASK - buffer[1+buffer[0]] = 0; - fprintf(stderr, "%s\n", buffer+1); - fflush(stderr); -#else - SafeDebugStr(buffer); -#endif -} -#endif - -#if MDNS_BUILDINGSHAREDLIBRARY >= 2 -// When building the non-debug version of the Extension, intended to go on end-user systems, we don't want -// MacsBug breaks for *anything*, not even for the serious LogMsg messages that on OS X would be written to syslog -mDNSexport void LogMsg(const char *format, ...) { (void)format; } -#else -mDNSexport void LogMsg(const char *format, ...) -{ - unsigned char buffer[256]; - va_list ptr; - va_start(ptr,format); - buffer[0] = (unsigned char)mDNS_vsnprintf((char*)buffer+1, 255, format, ptr); - va_end(ptr); -#if MDNS_ONLYSYSTEMTASK - buffer[1+buffer[0]] = 0; - fprintf(stderr, "%s\n", buffer+1); - fflush(stderr); -#else - SafeDebugStr(buffer); -#endif -} -#endif - -mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstPort) -{ - // Note: If we did multi-homing, we'd have to use the InterfaceID parameter to specify from which interface to send this response - #pragma unused(InterfaceID) - - InetAddress InetDest; - TUnitData senddata; - - if (dst->type != mDNSAddrType_IPv4) return(mStatus_NoError); - - InetDest.fAddressType = AF_INET; - InetDest.fPort = dstPort.NotAnInteger; - InetDest.fHost = dst->ip.v4.NotAnInteger; - - senddata.addr.maxlen = sizeof(InetDest); - senddata.addr.len = sizeof(InetDest); - senddata.addr.buf = (UInt8*)&InetDest; - senddata.opt = zeroTNetbuf; - senddata.udata.maxlen = (UInt32)((UInt8*)end - (UInt8*)msg); - senddata.udata.len = (UInt32)((UInt8*)end - (UInt8*)msg); - senddata.udata.buf = (UInt8*)msg; - - return(OTSndUData(m->p->ep, &senddata)); -} - -mDNSlocal OSStatus readpacket(mDNS *m) -{ - mDNSAddr senderaddr, destaddr; - mDNSInterfaceID interface; - mDNSIPPort senderport; - InetAddress sender; - char options[256]; - DNSMessage packet; - TUnitData recvdata; - OTFlags flags = 0; - OSStatus err; - - recvdata.addr.maxlen = sizeof(sender); - recvdata.addr.len = 0; - recvdata.addr.buf = (UInt8*)&sender; - recvdata.opt.maxlen = sizeof(options); - recvdata.opt.len = 0; - recvdata.opt.buf = (UInt8*)&options; - recvdata.udata.maxlen = sizeof(packet); - recvdata.udata.len = 0; - recvdata.udata.buf = (UInt8*)&packet; - - err = OTRcvUData(m->p->ep, &recvdata, &flags); - if (err && err != kOTNoDataErr) debugf("OTRcvUData error %d", err); - - if (err) return(err); - - senderaddr.type = mDNSAddrType_IPv4; - senderaddr.ip.v4.NotAnInteger = sender.fHost; - senderport.NotAnInteger = sender.fPort; - - destaddr.type = mDNSAddrType_IPv4; - destaddr.ip.v4 = zerov4Addr; - - #if OTCARBONAPPLICATION - // IP_RCVDSTADDR is known to fail on OS X Carbon, so we'll just assume the packet was probably multicast - destaddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4; - #endif - - if (recvdata.opt.len) - { - TOption *c = nil; - while (1) - { - err = OTNextOption(recvdata.opt.buf, recvdata.opt.len, &c); - if (err || !c) break; - if (c->level == INET_IP && c->name == IP_RCVDSTADDR && c->len - kOTOptionHeaderSize == sizeof(destaddr.ip.v4)) - mDNSPlatformMemCopy(&destaddr.ip.v4, c->value, sizeof(destaddr.ip.v4)); - } - } - - interface = m->HostInterfaces->InterfaceID; - - if (flags & T_MORE) debugf("ERROR: OTRcvUData() buffer too small (T_MORE set)"); - else if (recvdata.addr.len < sizeof(InetAddress)) debugf("ERROR: recvdata.addr.len (%d) too short", recvdata.addr.len); - else mDNSCoreReceive(m, &packet, recvdata.udata.buf + recvdata.udata.len, &senderaddr, senderport, &destaddr, MulticastDNSPort, interface); - - return(err); -} - -mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port) -{ - (void)m; // Unused - (void)flags; // Unused - (void)port; // Unused - return NULL; -} - -mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd) -{ - (void)flags; // Unused - (void)sd; // Unused - return NULL; -} - -mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) -{ - (void)sock; // Unused - return -1; -} - -mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr * dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID, - TCPConnectionCallback callback, void * context) -{ - (void)sock; // Unused - (void)dst; // Unused - (void)dstport; // Unused - (void)InterfaceID; // Unused - (void)callback; // Unused - (void)context; // Unused - return(mStatus_UnsupportedErr); -} - -mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sd) -{ - (void)sd; // Unused -} - -mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed) -{ - (void)sock; // Unused - (void)buf; // Unused - (void)buflen; // Unused - (void)closed; // Unused - return(0); -} - -mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len) -{ - (void)sock; // Unused - (void)msg; // Unused - (void)len; // Unused - return(0); -} - -mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port) -{ - (void)m; // Unused - (void)port; // Unused - return NULL; -} - -mDNSexport void mDNSPlatformUDPClose(UDPSocket *sock) -{ - (void)sock; // Unused -} - -mDNSlocal void mDNSOptionManagement(mDNS *const m) -{ - OSStatus err; - - // Make sure the length in the TNetbuf agrees with the length in the TOptionHeader - m->p->optReq.opt.len = m->p->optBlock.h.len; - m->p->optReq.opt.maxlen = m->p->optBlock.h.len; - if (m->p->optReq.opt.maxlen < 4) - m->p->optReq.opt.maxlen = 4; - - err = OTOptionManagement(m->p->ep, &m->p->optReq, NULL); - if (err) LogMsg("OTOptionManagement failed %d", err); -} - -mDNSlocal void mDNSinitComplete(mDNS *const m, mStatus result) -{ - m->mDNSPlatformStatus = result; - mDNSCoreInitComplete(m, mStatus_NoError); -} - -mDNSlocal pascal void mDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) -{ - mDNS *const m = (mDNS *const)contextPtr; - if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); - switch (code) - { - case T_OPENCOMPLETE: - { - OSStatus err; - InetInterfaceInfo interfaceinfo; - if (result) { LogMsg("T_OPENCOMPLETE failed %d", result); mDNSinitComplete(m, result); return; } - //debugf("T_OPENCOMPLETE"); - m->p->ep = (EndpointRef)cookie; - //debugf("OTInetGetInterfaceInfo"); - // (In future may want to loop over all interfaces instead of just using kDefaultInetInterface) - err = OTInetGetInterfaceInfo(&interfaceinfo, kDefaultInetInterface); - if (err) { LogMsg("OTInetGetInterfaceInfo failed %d", err); mDNSinitComplete(m, err); return; } - - // Make our basic standard host resource records (address, PTR, etc.) - m->p->interface.InterfaceID = (mDNSInterfaceID)&m->p->interface; - m->p->interface.ip.type = mDNSAddrType_IPv4; - m->p->interface.ip.ip.v4.NotAnInteger = interfaceinfo.fAddress; - m->p->interface.mask.type = mDNSAddrType_IPv4; - m->p->interface.mask.ip.v4.NotAnInteger = interfaceinfo.fNetmask; - m->p->interface.ifname[0] = 0; - m->p->interface.Advertise = m->AdvertiseLocalAddresses; - m->p->interface.McastTxRx = mDNStrue; - } - - case T_OPTMGMTCOMPLETE: - case T_BINDCOMPLETE: - // IP_RCVDSTADDR is known to fail on OS X Carbon, so we don't want to abort for that error - // (see comment above at the definition of kRcvDestAddrOption) - #if OTCARBONAPPLICATION - if (result && m->p->mOTstate == mOT_RcvDestAddr) - LogMsg("Carbon IP_RCVDSTADDR option failed %d; continuing anyway", result); - else - #endif - if (result) { LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d failed %d", m->p->mOTstate, result); mDNSinitComplete(m, result); return; } - //LogMsg("T_OPTMGMTCOMPLETE/T_BINDCOMPLETE %d", m->p->mOTstate); - switch (++m->p->mOTstate) - { - case mOT_ReusePort: m->p->optBlock.b = kReusePortOption; mDNSOptionManagement(m); break; - case mOT_RcvDestAddr: m->p->optBlock.i = kRcvDestAddrOption; mDNSOptionManagement(m); break; - case mOT_SetUTTL: m->p->optBlock.i = kSetUnicastTTLOption; mDNSOptionManagement(m); break; - case mOT_SetMTTL: m->p->optBlock.i = kSetMulticastTTLOption; mDNSOptionManagement(m); break; - case mOT_LLScope: m->p->optBlock.m = kAddLinkMulticastOption; mDNSOptionManagement(m); break; -// case mOT_AdminScope: m->p->optBlock.m = kAddAdminMulticastOption; mDNSOptionManagement(m); break; - case mOT_Bind: OTBind(m->p->ep, (TBind*)&mDNSbindReq, NULL); break; - case mOT_Ready: mDNSinitComplete(m, mStatus_NoError); - // Can't do mDNS_RegisterInterface until *after* mDNSinitComplete has set m->mDNSPlatformStatus to mStatus_NoError - mDNS_RegisterInterface(m, &m->p->interface, mDNSfalse); - break; - default: LogMsg("Unexpected m->p->mOTstate %d", m->p->mOTstate-1); - } - break; - - case T_DATA: - //debugf("T_DATA"); - while (readpacket(m) == kOTNoError) continue; // Read packets until we run out - break; - - case kOTProviderWillClose: LogMsg("kOTProviderWillClose"); break; - case kOTProviderIsClosed: // Machine is going to sleep, shutting down, or reconfiguring IP - LogMsg("kOTProviderIsClosed"); - if (m->p->mOTstate == mOT_Ready) - { - m->p->mOTstate = mOT_Closed; - mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse); - } - if (m->p->ep) { OTCloseProvider(m->p->ep); m->p->ep = NULL; } - break; // Do we need to do anything? - - default: debugf("mDNSNotifier: Unexpected OTEventCode %X", code); - break; - } -} - -#if MDNS_ONLYSYSTEMTASK - -static Boolean ONLYSYSTEMTASKevent; -static void *ONLYSYSTEMTASKcontextPtr; -static OTEventCode ONLYSYSTEMTASKcode; -static OTResult ONLYSYSTEMTASKresult; -static void *ONLYSYSTEMTASKcookie; - -mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) -{ - ONLYSYSTEMTASKcontextPtr = contextPtr; - ONLYSYSTEMTASKcode = code; - ONLYSYSTEMTASKresult = result; - ONLYSYSTEMTASKcookie = cookie; -} - -#else - -mDNSlocal pascal void CallmDNSNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) -{ - mDNS *const m = (mDNS *const)contextPtr; - if (!m) debugf("mDNSNotifier FATAL ERROR! No context"); - if (m->p->nesting) LogMsg("CallmDNSNotifier ERROR! OTEnterNotifier is supposed to suppress notifier callbacks"); - mDNSNotifier(contextPtr, code, result, cookie); -} - -#endif - -static OTNotifyUPP CallmDNSNotifierUPP; - -mDNSlocal OSStatus mDNSOpenEndpoint(const mDNS *const m) -{ - OSStatus err; - // m->optReq is pre-set to point to the shared m->optBlock - // m->optBlock is filled in by each OTOptionManagement call - m->p->optReq.opt.maxlen = sizeof(m->p->optBlock); - m->p->optReq.opt.len = sizeof(m->p->optBlock); - m->p->optReq.opt.buf = (UInt8*)&m->p->optBlock; - m->p->optReq.flags = T_NEGOTIATE; - - // Open an endpoint and start answering queries - //printf("Opening endpoint now...\n"); - m->p->ep = NULL; - m->p->mOTstate = mOT_Start; - err = OTAsyncOpenEndpoint(OTCreateConfiguration(kUDPName), 0, NULL, CallmDNSNotifierUPP, (void*)m); - if (err) { LogMsg("ERROR: OTAsyncOpenEndpoint(UDP) failed with error <%d>", err); return(err); } - return(kOTNoError); -} - -// Define these here because they're not in older versions of OpenTransport.h -enum -{ - xOTStackIsLoading = 0x27000001, /* Sent before Open Transport attempts to load the TCP/IP protocol stack.*/ - xOTStackWasLoaded = 0x27000002, /* Sent after the TCP/IP stack has been successfully loaded.*/ - xOTStackIsUnloading = 0x27000003 /* Sent before Open Transport unloads the TCP/IP stack.*/ -}; - -static mDNS *ClientNotifierContext; - -mDNSlocal pascal void ClientNotifier(void *contextPtr, OTEventCode code, OTResult result, void *cookie) -{ - mDNS *const m = ClientNotifierContext; - - #pragma unused(contextPtr) // Usually zero (except one in the 'xOTStackIsLoading' case) - #pragma unused(cookie) // Usually 'ipv4' (except for kOTPortNetworkChange) - #pragma unused(result) // Usually zero - - switch (code) - { - case xOTStackIsLoading: break; - case xOTStackWasLoaded: if (m->p->mOTstate == mOT_Closed) - { - LogMsg("kOTStackWasLoaded: Re-opening endpoint"); - if (m->p->ep) - LogMsg("kOTStackWasLoaded: ERROR: m->p->ep already set"); - m->mDNSPlatformStatus = mStatus_Waiting; - m->p->mOTstate = mOT_Reset; - #if !MDNS_ONLYSYSTEMTASK - mDNSOpenEndpoint(m); - #endif - } - else - LogMsg("kOTStackWasLoaded (no action)"); - break; - case xOTStackIsUnloading: break; - case kOTPortNetworkChange: break; - default: debugf("ClientNotifier unknown code %X, %X, %d", contextPtr, code, result); break; - } -} - -#if TARGET_API_MAC_CARBON - -mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel) -{ - CFStringRef cfs = CSCopyMachineName(); - CFStringGetPascalString(cfs, namelabel->c, sizeof(*namelabel), kCFStringEncodingUTF8); - CFRelease(cfs); -} - -#else - -mDNSlocal OSStatus ConvertStringHandleToUTF8(const StringHandle machineName, UInt8 *const utf8, ByteCount maxlen) -{ - OSStatus status; - TextEncoding utf8TextEncoding, SystemTextEncoding; - UnicodeMapping theMapping; - TextToUnicodeInfo textToUnicodeInfo; - ByteCount unicodelen = 0; - - if (maxlen > 255) maxlen = 255; // Can't put more than 255 in a Pascal String - - utf8TextEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, kTextEncodingDefaultVariant, kUnicodeUTF8Format); - UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &SystemTextEncoding); - theMapping.unicodeEncoding = utf8TextEncoding; - theMapping.otherEncoding = SystemTextEncoding; - theMapping.mappingVersion = kUnicodeUseLatestMapping; - status = CreateTextToUnicodeInfo(&theMapping, &textToUnicodeInfo); - if (status == noErr) - { - status = ConvertFromPStringToUnicode(textToUnicodeInfo, *machineName, maxlen, &unicodelen, (UniCharArrayPtr)&(utf8[1])); - DisposeTextToUnicodeInfo(&textToUnicodeInfo); - } - utf8[0] = (UInt8)unicodelen; - return(status); -} - -mDNSlocal void GetUserSpecifiedComputerName(domainlabel *const namelabel) -{ - StringHandle machineName = GetString(-16413); // Get machine name set in file sharing - if (machineName) - { - char machineNameState = HGetState((Handle)machineName); - HLock((Handle)machineName); - ConvertStringHandleToUTF8(machineName, namelabel->c, MAX_DOMAIN_LABEL); - HSetState((Handle)machineName, machineNameState); - } -} - -#endif - -static pascal void mDNSTimerTask(void *arg) -{ -#if MDNS_ONLYSYSTEMTASK -#pragma unused(arg) - ONLYSYSTEMTASKevent = true; -#else - mDNS *const m = (mDNS *const)arg; - if (!m->p->ep) LogMsg("mDNSTimerTask NO endpoint"); - if (m->mDNS_busy) LogMsg("mDNS_busy"); - if (m->p->nesting) LogMsg("mDNSTimerTask ERROR! OTEnterNotifier is supposed to suppress timer callbacks too"); - - // If our timer fires at a time when we have no endpoint, ignore it -- - // once we reopen our endpoint and get our T_BINDCOMPLETE message we'll call - // mDNS_RegisterInterface(), which does a lock/unlock, which retriggers the timer. - // Likewise, if m->mDNS_busy or m->p->nesting, we'll catch this on the unlock - if (m->p->ep && m->mDNS_busy == 0 && m->p->nesting == 0) mDNS_Execute(m); -#endif -} - -#if TEST_SLEEP -long sleep, wake, mode; -#endif - -mDNSexport mStatus mDNSPlatformInit (mDNS *const m) -{ - OSStatus err = InitOpenTransport(); - - ClientNotifierContext = m; - // Note: OTRegisterAsClient returns kOTNotSupportedErr when running as Carbon code on OS X - // -- but that's okay, we don't need a ClientNotifier when running as Carbon code on OS X - OTRegisterAsClient(NULL, NewOTNotifyUPP(ClientNotifier)); - - m->p->OTTimerTask = OTCreateTimerTask(NewOTProcessUPP(mDNSTimerTask), m); - m->p->nesting = 0; - -#if TEST_SLEEP - sleep = TickCount() + 600; - wake = TickCount() + 1200; - mode = 0; -#endif - - // Set up the nice label - m->nicelabel.c[0] = 0; - GetUserSpecifiedComputerName(&m->nicelabel); -// m->nicelabel = *(domainlabel*)"\pStu"; // For conflict testing - if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Macintosh"); - - // Set up the RFC 1034-compliant label - m->hostlabel.c[0] = 0; - ConvertUTF8PstringToRFC1034HostLabel(m->nicelabel.c, &m->hostlabel); - if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Macintosh"); - - mDNS_SetFQDN(m); - - // When it's finished mDNSOpenEndpoint asynchronously calls mDNSinitComplete() and then mDNS_RegisterInterface() - CallmDNSNotifierUPP = NewOTNotifyUPP(CallmDNSNotifier); - err = mDNSOpenEndpoint(m); - if (err) - { - LogMsg("mDNSOpenEndpoint failed %d", err); - if (m->p->OTTimerTask) OTDestroyTimerTask(m->p->OTTimerTask); - OTUnregisterAsClient(); - CloseOpenTransport(); - } - return(err); -} - -extern void mDNSPlatformClose (mDNS *const m) -{ - if (m->p->mOTstate == mOT_Ready) - { - m->p->mOTstate = mOT_Closed; - mDNS_DeregisterInterface(m, &m->p->interface, mDNSfalse); - } - if (m->p->ep) { OTCloseProvider (m->p->ep); m->p->ep = NULL; } - if (m->p->OTTimerTask) { OTDestroyTimerTask(m->p->OTTimerTask); m->p->OTTimerTask = 0; } - - OTUnregisterAsClient(); - CloseOpenTransport(); -} - -#if MDNS_ONLYSYSTEMTASK -extern void mDNSPlatformIdle(mDNS *const m); -mDNSexport void mDNSPlatformIdle(mDNS *const m) -{ - while (ONLYSYSTEMTASKcontextPtr) - { - void *contextPtr = ONLYSYSTEMTASKcontextPtr; - ONLYSYSTEMTASKcontextPtr = NULL; - mDNSNotifier(contextPtr, ONLYSYSTEMTASKcode, ONLYSYSTEMTASKresult, ONLYSYSTEMTASKcookie); - } - if (ONLYSYSTEMTASKevent) - { - ONLYSYSTEMTASKevent = false; - mDNS_Execute(m); - } - - if (m->p->mOTstate == mOT_Reset) - { - printf("\n"); - printf("******************************************************************************\n"); - printf("\n"); - printf("Reopening endpoint\n"); - mDNSOpenEndpoint(m); - m->ResourceRecords = NULL; - } - -#if TEST_SLEEP - switch (mode) - { - case 0: if ((long)TickCount() - sleep >= 0) { mDNSCoreMachineSleep(m, 1); mode++; } - break; - case 1: if ((long)TickCount() - wake >= 0) { mDNSCoreMachineSleep(m, 0); mode++; } - break; - } -#endif -} -#endif - -mDNSexport void mDNSPlatformLock(const mDNS *const m) -{ - if (!m) { DebugStr("\pmDNSPlatformLock m NULL!"); return; } - if (!m->p) { DebugStr("\pmDNSPlatformLock m->p NULL!"); return; } - - // If we try to call OTEnterNotifier and fail because we're already running at - // Notifier context, then make sure we don't do the matching OTLeaveNotifier() on exit. - // If we haven't even opened our endpoint yet, then just increment m->p->nesting for the same reason - if (m->p->mOTstate == mOT_Ready && !m->p->ep) DebugStr("\pmDNSPlatformLock: m->p->mOTstate == mOT_Ready && !m->p->ep"); - if (!m->p->ep || m->p->nesting || OTEnterNotifier(m->p->ep) == false) m->p->nesting++; -} - -mDNSlocal void ScheduleNextTimerCallback(const mDNS *const m) -{ - if (m->mDNSPlatformStatus == mStatus_NoError) - { - SInt32 interval = m->NextScheduledEvent - mDNS_TimeNow_NoLock(m); - if (interval < 1) interval = 1; - else if (interval > 0x70000000 / 1000) interval = 0x70000000 / mDNSPlatformOneSecond; - else interval = (interval * 1000 + mDNSPlatformOneSecond-1)/ mDNSPlatformOneSecond; - OTScheduleTimerTask(m->p->OTTimerTask, (OTTimeout)interval); - } -} - -mDNSexport void mDNSPlatformUnlock(const mDNS *const m) -{ - if (!m) { DebugStr("\pmDNSPlatformUnlock m NULL!"); return; } - if (!m->p) { DebugStr("\pmDNSPlatformUnlock m->p NULL!"); return; } - - if (m->p->ep && m->mDNS_busy == 0) ScheduleNextTimerCallback(m); - - if (m->p->nesting) m->p->nesting--; - else OTLeaveNotifier(m->p->ep); -} - -mDNSexport void mDNSPlatformStrCopy( void *dst, const void *src) { OTStrCopy((char*)dst, (char*)src); } -mDNSexport UInt32 mDNSPlatformStrLen ( const void *src) { return(OTStrLength((char*)src)); } -mDNSexport void mDNSPlatformMemCopy( void *dst, const void *src, UInt32 len) { OTMemcpy(dst, src, len); } -mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, UInt32 len) { return(OTMemcmp(dst, src, len)); } -mDNSexport void mDNSPlatformMemZero( void *dst, UInt32 len) { OTMemzero(dst, len); } -mDNSexport void * mDNSPlatformMemAllocate(mDNSu32 len) { return(OTAllocMem(len)); } -mDNSexport void mDNSPlatformMemFree(void *mem) { OTFreeMem(mem); } -mDNSexport mDNSu32 mDNSPlatformRandomSeed(void) { return(TickCount()); } -mDNSexport mStatus mDNSPlatformTimeInit(void) { return(mStatus_NoError); } -mDNSexport SInt32 mDNSPlatformRawTime() { return((SInt32)TickCount()); } -mDNSexport SInt32 mDNSPlatformOneSecond = 60; - -mDNSexport mDNSs32 mDNSPlatformUTC(void) -{ - // Classic Mac OS since Midnight, 1st Jan 1904 - // Standard Unix counts from 1970 - // This value adjusts for the 66 years and 17 leap-days difference - mDNSu32 SecsSince1904; - MachineLocation ThisLocation; - #define TIME_ADJUST (((1970 - 1904) * 365 + 17) * 24 * 60 * 60) - #define ThisLocationGMTdelta ((ThisLocation.u.gmtDelta << 8) >> 8) - GetDateTime(&SecsSince1904); - ReadLocation(&ThisLocation); - return((mDNSs32)(SecsSince1904 - ThisLocationGMTdelta - TIME_ADJUST)); -} diff --git a/mDNSMacOS9/mDNSMacOS9.h b/mDNSMacOS9/mDNSMacOS9.h deleted file mode 100755 index 496c07f..0000000 --- a/mDNSMacOS9/mDNSMacOS9.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- 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. - */ - -// *************************************************************************** -// Classic Mac (Open Transport) structures - -//#include // OpenTransport.h requires this -#include -#include -#include - -typedef enum -{ - mOT_Closed = 0, // We got kOTProviderIsClosed message - mOT_Reset, // We got xOTStackWasLoaded message - mOT_Start, // We've called OTAsyncOpenEndpoint - mOT_ReusePort, // Have just done kReusePortOption - mOT_RcvDestAddr, // Have just done kRcvDestAddrOption - mOT_SetUTTL, // Have just done kSetUnicastTTLOption - mOT_SetMTTL, // Have just done kSetMulticastTTLOption - mOT_LLScope, // Have just done kAddLinkMulticastOption -// mOT_AdminScope, // Have just done kAddAdminMulticastOption - mOT_Bind, // We've just called OTBind - mOT_Ready // Got T_BINDCOMPLETE; Interface is registered and active -} mOT_State; - -typedef struct { TOptionHeader h; mDNSv4Addr multicastGroupAddress; mDNSv4Addr InterfaceAddress; } TIPAddMulticastOption; -typedef struct { TOptionHeader h; UInt8 val; } TSetByteOption; -typedef struct { TOptionHeader h; UInt32 flag; } TSetBooleanOption; - -// TOptionBlock is a union of various types. -// What they all have in common is that they all start with a TOptionHeader. -typedef union { TOptionHeader h; TIPAddMulticastOption m; TSetByteOption i; TSetBooleanOption b; } TOptionBlock; - -struct mDNS_PlatformSupport_struct -{ - EndpointRef ep; - UInt32 mOTstate; // mOT_State enum - TOptionBlock optBlock; - TOptMgmt optReq; - long OTTimerTask; - UInt32 nesting; - NetworkInterfaceInfo interface; -}; diff --git a/mDNSMacOS9/mDNSPrefix.h b/mDNSMacOS9/mDNSPrefix.h deleted file mode 100644 index cf6cec8..0000000 --- a/mDNSMacOS9/mDNSPrefix.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- Mode: C; tab-width: 4 -*- - * - * Copyright (c) 2004 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. - */ - -// Global definitions that apply to all source files -// -// Symbols that are defined here are available within all source files. -// This is the equivalent of using using "-d SYMBOL=VALUE" in a Makefile -// in command-line build environments. - -// For normal DeferredTask time execution, set MDNS_ONLYSYSTEMTASK to 0 -// For easier debugging, set MDNS_ONLYSYSTEMTASK to 1, and OT Notifier executions -// will be deferred until SystemTask time. (This option is available only for building -// the standalone application samples that have their own event loop -- don't try -// to build the System Extension with MDNS_ONLYSYSTEMTASK set because it won't work.) - -#if __ide_target("Standalone TestResponder") || __ide_target("Standalone TestSearcher") || __ide_target("Standalone SubTypeTester") -#define TARGET_API_MAC_CARBON 1 -#define OTCARBONAPPLICATION 1 -#define MDNS_ONLYSYSTEMTASK 0 -#define MDNS_DEBUGMSGS 0 - -#elif __ide_target("Standalone TestResponder (Debug)") || __ide_target("Standalone TestSearcher (Debug)") -#define TARGET_API_MAC_CARBON 1 -#define OTCARBONAPPLICATION 1 -#define MDNS_ONLYSYSTEMTASK 1 -#define MDNS_DEBUGMSGS 1 - -#elif __ide_target("Standalone TestResponder (Classic)") || __ide_target("Standalone TestSearcher (Classic)") -#define MDNS_ONLYSYSTEMTASK 0 -#define MDNS_DEBUGMSGS 0 - -#elif __ide_target("CFM Library for Extensions Folder") -#define MDNS_BUILDINGSHAREDLIBRARY 2 - -#elif __ide_target("CFM Library for Extensions (Debug)") -#define MDNS_DEBUGMSGS 0 -#define MDNS_BUILDINGSHAREDLIBRARY 1 - -#elif __ide_target("CFM Stub for clients to link against") -#define MDNS_BUILDINGSTUBLIBRARY 1 - -#else -#error Options for this target not found in prefix file -#endif - -// dnssd_clientlib.c assumes malloc() and free(), so we #define them here to be the OT equivalents -#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY -#define malloc(x) OTAllocMem(x) -#define free(x) OTFreeMem(x) -#endif diff --git a/mDNSMacOSX/BLE.c b/mDNSMacOSX/BLE.c new file mode 100644 index 0000000..85fb810 --- /dev/null +++ b/mDNSMacOSX/BLE.c @@ -0,0 +1,853 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015-2016 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 "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" +#include "mDNSMacOSX.h" +#include "BLE.h" +#include + +#pragma mark - Browse and Registration Request Handling + +// Disable use of BLE discovery APIs by default. +mDNSBool EnableBLEBasedDiscovery = mDNSfalse; + +typedef struct matchingResponses +{ + struct matchingResponses * next; + void * response; +} matchingResponses_t; + +// Initially used for both the browse and registration lists. +typedef struct requestList +{ + struct requestList * next; + unsigned int refCount; + domainname name; + mDNSu16 type; + DNSServiceFlags flags; + mDNSInterfaceID InterfaceID; + +// TODO: Possibly restructure the following browse and registration specific +// members as a union to save a bit of space. + + // The following fields are only used for browse requests currently + serviceHash_t browseHash; + DNSQuestion * question; + mDNSu8 key[MAX_DOMAIN_LABEL]; + size_t keySize; + matchingResponses_t * ourResponses; + + // The following fields are only used for registration requests currently + serviceHash_t registeredHash; + ServiceRecordSet * serviceRecordSet; // service record set in the original request + AuthRecType savedARType; + bool triggeredOnAWDL; +} requestList_t; + +// Lists for all DNSServiceBrowse() and DNSServiceRegister() requests using +// BLE beacon based triggering. +static requestList_t* BLEBrowseListHead = NULL; +static requestList_t* BLERegistrationListHead = NULL; + +#define isAutoTriggerRequest(ptr) ((ptr->InterfaceID == kDNSServiceInterfaceIndexAny) && (ptr->flags & kDNSServiceFlagsAutoTrigger)) + +#pragma mark - Manage list of responses that match this request. + +mDNSlocal bool inResponseListForRequest(requestList_t *request, void * response) +{ + matchingResponses_t * rp; + + for (rp = request->ourResponses; rp; rp = rp->next) + if (rp->response == response) + break; + + return (rp != 0); +} + +mDNSlocal void addToResponseListForRequest(requestList_t *request, void * response) +{ + matchingResponses_t *matchingResponse = calloc(1, sizeof(matchingResponses_t)); + + if (matchingResponse == NULL) + { + LogMsg("addToResponseListForRequest: calloc() failed!"); + return; + } + matchingResponse->response = response; + matchingResponse->next = request->ourResponses; + request->ourResponses = matchingResponse; +} + +// If response is currently in the list of responses, remove it and return true. +// Othewise, return false. +mDNSlocal bool removeFromResponseListForRequest(requestList_t *request, void * response) +{ + matchingResponses_t ** nextp; + bool responseRemoved = false; + + for (nextp = & request->ourResponses; *nextp; nextp = & (*nextp)->next) + if ((*nextp)->response == response) + break; + + if (*nextp) + { + LogInfo("removeFromResponseListForRequest: response no longer matches for %##s %s ", request->name.c, DNSTypeName(request->type)); + + responseRemoved = true; + matchingResponses_t *tmp = *nextp; + *nextp = (*nextp)->next; + free(tmp); + } + return responseRemoved; +} + +// Free all current entries on the response list for this request. +mDNSlocal void freeResponseListEntriesForRequest(requestList_t *request) +{ + matchingResponses_t * ptr; + + ptr = request->ourResponses; + while (ptr) + { + matchingResponses_t * tmp; + + tmp = ptr; + ptr = ptr->next; + free(tmp); + } +} + +#pragma mark - Manage request lists + +mDNSlocal requestList_t ** findInRequestList(requestList_t ** listHead, const domainname *const name, mDNSu16 type) +{ + requestList_t **ptr = listHead; + + for ( ; *ptr; ptr = &(*ptr)->next) + if ((*ptr)->type == type && SameDomainName(&(*ptr)->name, name)) + break; + + return ptr; +} + +mDNSlocal requestList_t * addToRequestList(requestList_t ** listHead, const domainname *const name, mDNSu16 type, DNSServiceFlags flags) +{ + requestList_t **ptr = findInRequestList(listHead, name, type); + + if (!*ptr) + { + *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); + mDNSPlatformMemZero(*ptr, sizeof(**ptr)); + (*ptr)->type = type; + (*ptr)->flags = flags; + AssignDomainName(&(*ptr)->name, name); + } + (*ptr)->refCount += 1; + + LogInfo("addToRequestList: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); + + return *ptr; +} + +mDNSlocal void removeFromRequestList(requestList_t ** listHead, const domainname *const name, mDNSu16 type) +{ + requestList_t **ptr = findInRequestList(listHead, name, type); + + if (!*ptr) { LogMsg("removeFromRequestList: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return; } + + (*ptr)->refCount -= 1; + + LogInfo("removeFromRequestList: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); + + if (!(*ptr)->refCount) + { + requestList_t *tmp = *ptr; + *ptr = (*ptr)->next; + freeResponseListEntriesForRequest(tmp); + mDNSPlatformMemFree(tmp); + } +} + +#pragma mark - Hashing and beacon state + +// Simple string hash based on the Bernstein hash. + +#define PRIME 31 // small prime number +#define MODULO (sizeof(serviceHash_t) * 8) +#define CONVERT_TO_LOWER_CASE(x) (((x) <= 'Z' && (x) >= 'A') ? ((x) | 0x20) : (x)) + +mDNSlocal serviceHash_t BLELabelHash(const unsigned char *str, unsigned int length) +{ + serviceHash_t hash = 0; + + for (unsigned int i = 0; i < length; i++) { + hash = PRIME * hash + CONVERT_TO_LOWER_CASE(*str); + str++; + } + + hash %= MODULO; + LogInfo("BLELabelHash: %d characters hashed to %d", length, hash); + + return ((serviceHash_t)1 << hash); +} + +// Hash just the service type not including the protocol or first "_" character initially. +mDNSlocal serviceHash_t BLEServiceHash(const domainname *const domain) +{ + const unsigned char *p = domain->c; + unsigned int length = (unsigned int) *p; + + p++; + if (*p != '_') + { + LogInfo("BLEServiceHash: browse type does not begin with a _"); + return 0; + } + p++; // skip the '-" + length--; + + if (length > MAX_DOMAIN_LABEL || length == 0) + { + LogInfo("BLEServiceHash: invalid browse type length: %d characters", length); + return 0; + } + + return BLELabelHash(p, length); +} + +// Storage for the current Bonjour BLE beacon data; +typedef struct BLEBeacon +{ + serviceHash_t browseHash; + serviceHash_t registeredHash; +} BLEBeacon_t; + +BLEBeacon_t BLEBeacon; + +mDNSlocal void addServiceToBeacon(serviceHash_t browseHash, serviceHash_t registeredHash) +{ + bool beaconUpdated = false; + + if (BLEBeacon.browseHash & browseHash) + { + LogInfo("addServiceToBeacon: Bit 0x%x already set in browsing services hash", browseHash); + } + else + { + BLEBeacon.browseHash |= browseHash; + beaconUpdated = true; + } + + if (BLEBeacon.registeredHash & registeredHash) + { + LogInfo("addServiceToBeacon: Bit 0x%x already set in advertising services hash", registeredHash); + } + else + { + BLEBeacon.registeredHash |= registeredHash; + beaconUpdated = true; + } + + if (beaconUpdated) + updateBLEBeaconAndScan(BLEBeacon.browseHash, BLEBeacon.registeredHash); +} + +// Go through all the existing browses and registrations to get the +// current hash values for the corresponding BLE beacon. +// We must do this when any hash bits are removed do accurately generate +// the correct combination of all currently set hash bits. +mDNSlocal void updateBeacon() +{ + requestList_t *ptr; + + BLEBeacon.browseHash = 0; + BLEBeacon.registeredHash = 0; + + for (ptr = BLEBrowseListHead; ptr; ptr = ptr->next) + { + BLEBeacon.browseHash |= ptr->browseHash; + } + + for (ptr = BLERegistrationListHead; ptr; ptr = ptr->next) + { + BLEBeacon.registeredHash |= ptr->registeredHash; + } + + updateBLEBeaconAndScan(BLEBeacon.browseHash, BLEBeacon.registeredHash); +} + +#pragma mark - Request start/stop + +// Forward declarations for mDNSLocal functions that are called before they are defined. +mDNSlocal void checkForMatchingResponses(requestList_t *bp); +mDNSlocal void clearResponseLists(); + +void start_BLE_browse(DNSQuestion * q, const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags, mDNSu8 *key, size_t keySize) +{ + requestList_t * ptr; + + if (!EnableBLEBasedDiscovery) + { + LogMsg("start_BLE_browse: EnableBLEBasedDiscovery disabled"); + return; + } + + LogInfo("start_BLE_browse: Starting BLE browse for: %##s %s", domain->c, DNSTypeName(type)); + + ptr = addToRequestList(&BLEBrowseListHead, domain, type, flags); + + // If equivalent BLE browse is already running, just return. + if (ptr->refCount > 1) + { + LogInfo("start_BLE_browse: Dup of existing BLE browse."); + return; + } + + ptr->browseHash = BLEServiceHash(domain); + ptr->question = q; + + if (ptr->browseHash == 0) + { + LogInfo("BLEServiceHash failed!"); + removeFromRequestList(&BLEBrowseListHead, domain, type); + return; + } + + // Save these for use in D2D plugin callback logic. + memcpy(ptr->key, key, keySize); + ptr->keySize = keySize; + // Extract the interface ID for easier access in the requestList_t structure + ptr->InterfaceID = q->InterfaceID; + + addServiceToBeacon(ptr->browseHash, 0); + + checkForMatchingResponses(ptr); +} + +// Stop the browse. +// Return true if this is the last reference to the browse, false otherwise. +bool stop_BLE_browse(const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags) +{ + (void) flags; // not used initially + requestList_t * ptr; + bool lastReference = false; + + if (!EnableBLEBasedDiscovery) + { + LogMsg("stop_BLE_browse: EnableBLEBasedDiscovery disabled"); + return lastReference; + } + + LogInfo("stop_BLE_browse: Stopping BLE browse for: %##s %s", domain->c, DNSTypeName(type)); + + ptr = *(findInRequestList(&BLEBrowseListHead, domain, type)); + if (ptr == 0) + { + LogInfo("stop_BLE_browse: No matching browse found."); + return lastReference; + } + + // If this is the last reference for this browse, update advertising and browsing bits set in + // the beacon after removing this browse from the list. + if (ptr->refCount == 1) + lastReference = true; + + removeFromRequestList(&BLEBrowseListHead, domain, type); + + if (lastReference) + updateBeacon(); + + // If there are no active browse or registration requests, BLE scanning will be disabled. + // Clear the list of responses received to remove any stale response state. + if (BLEBrowseListHead == NULL && BLERegistrationListHead == 0) + clearResponseLists(); + + return lastReference; +} + +extern void internal_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags, DNSQuestion * q); +extern void internal_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags); + +extern void internal_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); +extern void internal_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); + +void start_BLE_advertise(ServiceRecordSet * serviceRecordSet, const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags) +{ + requestList_t * ptr; + const domainname * instanceRemoved; + + if (!EnableBLEBasedDiscovery) + { + LogMsg("start_BLE_advertise: EnableBLEBasedDiscovery disabled"); + return; + } + + // Just process the SRV record for each service registration. The PTR + // record already has the service type at the beginning of the domain, but + // we want to filter out reverse address PTR records at this point in time, so using + // the SRV record instead. + if (type != kDNSServiceType_SRV) + return; + + if (serviceRecordSet == NULL) + { + LogInfo("start_BLE_advertise: NULL service record set for: %##s %s, returning", domain->c, DNSTypeName(type)); + return; + } + LogInfo("start_BLE_advertise: Starting BLE advertisement for: %##s %s", domain->c, DNSTypeName(type)); + + instanceRemoved = SkipLeadingLabels(domain, 1); + + ptr = addToRequestList(&BLERegistrationListHead, instanceRemoved, type, flags); + + // If equivalent BLE registration is already running, just return. + if (ptr->refCount > 1) + { + LogInfo("start_BLE_advertise: Dup of existing BLE advertisement."); + return; + } + + ptr->registeredHash = BLEServiceHash(instanceRemoved); + if (ptr->registeredHash == 0) + { + LogInfo("BLEServiceHash failed!"); + removeFromRequestList(&BLERegistrationListHead, instanceRemoved, type); + return; + } + ptr->serviceRecordSet = serviceRecordSet; + // Extract the interface ID for easier access in the requestList_t structure + ptr->InterfaceID = serviceRecordSet->RR_SRV.resrec.InterfaceID; + + addServiceToBeacon(0, ptr->registeredHash); +} + +void stop_BLE_advertise(const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags) +{ + (void) flags; // not used initially + requestList_t * ptr; + bool lastReference = false; + const domainname * instanceRemoved; + + if (!EnableBLEBasedDiscovery) + { + LogMsg("stop_BLE_advertise: EnableBLEBasedDiscovery disabled"); + return; + } + + // Just process the SRV record for each service registration. The PTR + // record already has the service type at the beginning of the domain, but + // we want to filter out reverse address PTR records at this point in time, so using + // the SRV record instead. + if (type != kDNSServiceType_SRV) + return; + + LogInfo("stop_BLE_advertise: Stopping BLE advertisement for: %##s %s", domain->c, DNSTypeName(type)); + + instanceRemoved = SkipLeadingLabels(domain, 1); + + // Get the request pointer from the indirect pointer returned. + ptr = *(findInRequestList(&BLERegistrationListHead, instanceRemoved, type)); + + if (ptr == 0) + { + LogInfo("stop_BLE_advertise: No matching advertisement found."); + return; + } + + // If this is the last reference for this registration, update advertising and browsing bits set in + // the beacon before removing this registration from the request list. + if (ptr->refCount == 1) + { + lastReference = true; + + if (isAutoTriggerRequest(ptr) && ptr->triggeredOnAWDL) + { + // And remove the corresponding advertisements from the AWDL D2D plugin. + // Do it directly here, since we do not set the kDNSServiceFlagsIncludeAWDL bit in the original client request structure + // when we trigger the registration over AWDL, we just update the record ARType field, so our caller, external_stop_browsing_for_service() + // would not call into the D2D plugin to remove the advertisements in this case. + internal_stop_advertising_service(& ptr->serviceRecordSet->RR_PTR.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + internal_stop_advertising_service(& ptr->serviceRecordSet->RR_SRV.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + internal_stop_advertising_service(& ptr->serviceRecordSet->RR_TXT.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + } + } + removeFromRequestList(&BLERegistrationListHead, instanceRemoved, type); + + if (lastReference) + updateBeacon(); + + // If there are no active browse or registration requests, BLE scanning will be disabled. + // Clear the list of responses received to remove any stale response state. + if (BLEBrowseListHead == NULL && BLERegistrationListHead == 0) + clearResponseLists(); +} + +#pragma mark - Response Handling + +// Structure used to track the beacons received from various peers. +typedef struct responseList +{ + struct responseList * next; + serviceHash_t browseHash; + serviceHash_t registeredHash; + mDNSEthAddr senderMAC; +} responseList_t; + +#define RESPONSE_LIST_NUMBER 8 +static responseList_t* BLEResponseListHeads[RESPONSE_LIST_NUMBER]; + +mDNSlocal responseList_t ** findInResponseList(mDNSEthAddr * ptrToMAC) +{ + // Use the least significant byte of the MAC address as our hash index to find the list. + responseList_t **ptr = & BLEResponseListHeads[ptrToMAC->b[5] % RESPONSE_LIST_NUMBER]; + + for ( ; *ptr; ptr = &(*ptr)->next) + { + if (memcmp(&(*ptr)->senderMAC, ptrToMAC, sizeof(mDNSEthAddr)) == 0) + break; + } + + return ptr; +} + + +mDNSlocal responseList_t ** addToResponseList(serviceHash_t browseHash, serviceHash_t registeredHash, mDNSEthAddr * ptrToMAC) +{ + responseList_t **ptr = findInResponseList(ptrToMAC); + + if (!*ptr) + { + *ptr = mDNSPlatformMemAllocate(sizeof(**ptr)); + mDNSPlatformMemZero(*ptr, sizeof(**ptr)); + (*ptr)->browseHash = browseHash; + (*ptr)->registeredHash = registeredHash; + memcpy(& (*ptr)->senderMAC, ptrToMAC, sizeof(mDNSEthAddr)); + } + + return ptr; +} + +mDNSlocal void removeFromResponseList(mDNSEthAddr * ptrToMAC) +{ + responseList_t **ptr = findInResponseList(ptrToMAC); + + if (!*ptr) + { + LogMsg("removeFromResponseList: did not find entry for MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]); + return; + } + + LogInfo("removeFromResponseList: removing entry for MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]); + + responseList_t *tmp = *ptr; + *ptr = (*ptr)->next; + mDNSPlatformMemFree(tmp); +} + +// Free all current entries on the BLE response lists, removing all pointers +// to freed structures from the lists. +mDNSlocal void clearResponseLists() +{ + responseList_t **ptr; + + for (unsigned int i = 0; i < RESPONSE_LIST_NUMBER; i++) + { + ptr = & BLEResponseListHeads[i]; + while (*ptr) + { + responseList_t * tmp; + + tmp = *ptr; + *ptr = (*ptr)->next; + mDNSPlatformMemFree(tmp); + } + } +} + +// Called from mDNS_Execute() when NextBLEServiceTime is reached +// to stop the BLE beacon a few seconds after the last request has +// been stopped. This gives peers a chance to see that this device +// is no longer browsing for or advertising any services via the +// BLE beacon. +void serviceBLE(void) +{ + mDNSStorage.NextBLEServiceTime = 0; + if (BLEBrowseListHead || BLERegistrationListHead) + { + // We don't expect to be called if there are active requests. + LogInfo("serviceBLE: called with active BLE requests ??"); + return; + } + stopBLEBeacon(); +} + +// Called from start_BLE_browse() on the mDNSResonder kqueue thread +mDNSlocal void checkForMatchingResponses(requestList_t *bp) +{ + responseList_t *ptr; + + for (unsigned int i = 0; i < RESPONSE_LIST_NUMBER; i++) + { + for (ptr = BLEResponseListHeads[i]; ptr; ptr = ptr->next) + { + if ((bp->browseHash & ptr->registeredHash) == bp->browseHash) + { + // Clear the registered services hash for the response. + // The next beacon from this peer will update the hash and our + // newly started browse will get an add event if there is a match. + ptr->registeredHash = 0; + } + } + } +} + +// Define a fixed name to use for the instance name denoting that one or more instances +// of a service are being advetised by peers in their BLE beacons. +// Name format is: length byte + bytes of name string + two byte pointer to the PTR record name. +// See compression_lhs definition in the D2D plugin code for backgound on 0xc027 DNS name compression pointer value. +static Byte *BLEinstanceValue = (Byte *) "\x11ThresholdInstance\xc0\x27"; +#define BLEValueSize strlen((const char *)BLEinstanceValue) + +void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize); +void xD2DRemoveFromCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize); + +// Find each unique browse that matches the registered service hash in the BLE response. +// Called on the CFRunLoop thread while handling a callback from CoreBluetooth. +// Caller should hold KQueueLock(). +mDNSlocal void findMatchingBrowse(responseList_t *response) +{ + requestList_t *ptr; + + ptr = BLEBrowseListHead; + for ( ; ptr; ptr = ptr->next) + { + if ((ptr->browseHash & response->registeredHash) == ptr->browseHash) + { + + LogInfo("findMatchingBrowse: Registration in response matched browse for: %##s", ptr->name.c); + + if (inResponseListForRequest(ptr, response)) + { + LogInfo("findMatchingBrowse: Already on response list for browse: %##s", ptr->name.c); + + continue; + } + else + { + LogInfo("findMatchingBrowse: Adding to response list for browse: %##s", ptr->name.c); + + if (ptr->ourResponses == 0) + { + if (isAutoTriggerRequest(ptr)) + { + LogInfo("findMatchingBrowse: First BLE response, triggering browse for %##s on AWDL", ptr->name.c); + ptr->question->flags |= kDNSServiceFlagsIncludeAWDL; + mDNSCoreRestartQuestion(& mDNSStorage, ptr->question); + // register with the AWDL D2D plugin, + internal_start_browsing_for_service(ptr->question->InterfaceID, & ptr->name, ptr->type, ptr->question->flags, ptr->question); + } + + // Browse on mDNSInterface_BLE is used to determine if there are one or more instances of the + // service type discoveryed over BLE. If this is the first instance, add the psuedo instance defined by BLEinstanceValue. + if (ptr->question->InterfaceID == mDNSInterface_BLE) + { + xD2DAddToCache(& mDNSStorage, kD2DSuccess, 0, D2DBLETransport, ptr->key, ptr->keySize, BLEinstanceValue, BLEValueSize); + } + } + addToResponseListForRequest(ptr, response); + } + } + else + { + // If a previous response from this peer had matched the browse, remove that response from the + // list now. If this is the last matching response, remove the corresponding key from the AWDL D2D plugin + if (removeFromResponseListForRequest(ptr, response) && (ptr->ourResponses == 0)) + { + if (ptr->question->InterfaceID == mDNSInterface_BLE) + { + xD2DRemoveFromCache(& mDNSStorage, kD2DSuccess, 0, D2DBLETransport, ptr->key, ptr->keySize, BLEinstanceValue, BLEValueSize); + } + + if (isAutoTriggerRequest(ptr)) + { + LogInfo("findMatchingBrowse: Last BLE response, disabling browse for %##s on AWDL", ptr->name.c); + internal_stop_browsing_for_service(ptr->question->InterfaceID, & ptr->name, ptr->type, ptr->question->flags); + } + } + } + } +} + +// Find each local registration that matches the service browse hash in the BLE response. +// Called on the CFRunLoop thread while handling a callback from CoreBluetooth. +// Caller should hold KQueueLock(). +mDNSlocal void findMatchingRegistration(responseList_t *response) +{ + requestList_t *ptr; + + ptr = BLERegistrationListHead; + for ( ; ptr; ptr = ptr->next) + { + if ((ptr->registeredHash & response->browseHash) == ptr->registeredHash) + { + + LogInfo("findMatchingRegistration: Incoming browse matched registration for: %##s", ptr->name.c); + + if (inResponseListForRequest(ptr, response)) + { + LogInfo("findMatchingRegistration: Already on response list for registration: %##s", ptr->name.c); + + continue; + } + else + { + LogInfo("findMatchingRegistration: Adding to response list for registration: %##s", ptr->name.c); + + // Also pass the registration to the AWDL D2D plugin if this is the first matching peer browse for + // an auto triggered local registration. + if ((ptr->ourResponses == 0) && isAutoTriggerRequest(ptr)) + { + AuthRecType newARType; + + LogInfo("findMatchingRegistration: First BLE response, triggering registration for %##s on AWDL", ptr->name.c); + if (ptr->serviceRecordSet == 0) + { + LogInfo("findMatchingRegistration: serviceRecordSet pointer is NULL ??"); + continue; + } + // Modify the PTR, TXT, and SRV records so that they now apply to AWDL and restart the registration. + // RR_ADV is not passed to the D2D plugins froma internal_start_advertising_helper(), so we don't do it here either. + + if (ptr->flags & kDNSServiceFlagsIncludeAWDL) + { + LogInfo("findMatchingRegistration: registration for %##s already applies to AWDL, skipping", ptr->name.c); + continue; + } + + // Save the current ARType value to restore when the promotion to use AWDL is stopped. + ptr->savedARType = ptr->serviceRecordSet->RR_PTR.ARType; + + // Preserve P2P attribute if original registration was applied to P2P. + if (ptr->serviceRecordSet->RR_PTR.ARType == AuthRecordAnyIncludeP2P) + newARType = AuthRecordAnyIncludeAWDLandP2P; + else + newARType = AuthRecordAnyIncludeAWDL; + + ptr->serviceRecordSet->RR_PTR.ARType = newARType; + ptr->serviceRecordSet->RR_SRV.ARType = newARType; + ptr->serviceRecordSet->RR_TXT.ARType = newARType; + mDNSCoreRestartRegistration(& mDNSStorage, & ptr->serviceRecordSet->RR_PTR, -1); + mDNSCoreRestartRegistration(& mDNSStorage, & ptr->serviceRecordSet->RR_SRV, -1); + mDNSCoreRestartRegistration(& mDNSStorage, & ptr->serviceRecordSet->RR_TXT, -1); + + internal_start_advertising_service(& ptr->serviceRecordSet->RR_PTR.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + internal_start_advertising_service(& ptr->serviceRecordSet->RR_SRV.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + internal_start_advertising_service(& ptr->serviceRecordSet->RR_TXT.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + // indicate the registration has been applied to the AWDL interface + ptr->triggeredOnAWDL = true; + } + + addToResponseListForRequest(ptr, response); + } + } + else + { + // If a previous response from this peer had matched the browse, remove that response from the + // list now. If this is the last matching response for a local auto triggered registration, + // remove the advertised key/value pairs from the AWDL D2D plugin. + if (removeFromResponseListForRequest(ptr, response) && (ptr->ourResponses == 0) && isAutoTriggerRequest(ptr)) + { + LogInfo("findMatchingRegistration: Last BLE response, disabling registration for %##s on AWDL", ptr->name.c); + + // Restore the saved ARType and call into the AWDL D2D plugin to stop the corresponding record advertisements over AWDL. + ptr->serviceRecordSet->RR_PTR.ARType = ptr->savedARType; + ptr->serviceRecordSet->RR_SRV.ARType = ptr->savedARType; + ptr->serviceRecordSet->RR_TXT.ARType = ptr->savedARType; + internal_stop_advertising_service(& ptr->serviceRecordSet->RR_PTR.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + internal_stop_advertising_service(& ptr->serviceRecordSet->RR_SRV.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + internal_stop_advertising_service(& ptr->serviceRecordSet->RR_TXT.resrec, (ptr->flags | kDNSServiceFlagsIncludeAWDL)); + } + } + } +} + +// Called on CFRunLoop thread during CoreBluetooth beacon response processing. +// Thus, must call KQueueLock() prior to calling any core mDNSResponder routines to register records, etc. +void responseReceived(serviceHash_t browseHash, serviceHash_t registeredHash, mDNSEthAddr * ptrToMAC) +{ + responseList_t ** ptr; + + KQueueLock(& mDNSStorage); + ptr = findInResponseList(ptrToMAC); + if (*ptr == 0) + { + // Only add to list if peer is actively browsing or advertising. + if (browseHash || registeredHash) + { + LogInfo("responseReceived: First beacon of this type, adding to list"); + LogInfo("responseReceived: browseHash = 0x%x, registeredHash = 0x%x", + browseHash, registeredHash); + LogInfo("responseReceived: sender MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]); + + ptr = addToResponseList(browseHash, registeredHash, ptrToMAC); + // See if we are browsing for any of the peers advertised services. + findMatchingBrowse(*ptr); + // See if we have a registration that matches the peer's browse. + findMatchingRegistration(*ptr); + } + } + else // have entry from this MAC in the list + { + if (((*ptr)->browseHash == browseHash) && ((*ptr)->registeredHash == registeredHash)) + { + // A duplicate of a current entry. +#if VERBOSE_BLE_DEBUG + LogInfo("responseReceived: Duplicate of previous beacon, ignoring"); + LogInfo("responseReceived: sender MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]); +#endif // VERBOSE_BLE_DEBUG + } + else + { + LogInfo("responseReceived: Update of previous beacon"); + LogInfo("responseReceived: browseHash = 0x%x, registeredHash = 0x%x", + browseHash, registeredHash); + LogInfo("responseReceived: sender MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ptrToMAC->b[0], ptrToMAC->b[1], ptrToMAC->b[2], ptrToMAC->b[3], ptrToMAC->b[4], ptrToMAC->b[5]); + + (*ptr)->browseHash = browseHash; + (*ptr)->registeredHash = registeredHash; + + findMatchingBrowse(*ptr); + findMatchingRegistration(*ptr); + } + + // If peer is no longer browsing or advertising, remove from list. + if ((browseHash == 0) && (registeredHash == 0)) + { + LogInfo("responseReceived: Removing peer entry from the list"); + + removeFromResponseList(ptrToMAC); + } + } + + KQueueUnlock(& mDNSStorage, "BLE responseReceived"); +} diff --git a/mDNSMacOSX/BLE.h b/mDNSMacOSX/BLE.h new file mode 100644 index 0000000..970c8fa --- /dev/null +++ b/mDNSMacOSX/BLE.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015-2016 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 _BLE_H_ +#define _BLE_H_ + +#include "dns_sd.h" +#include "dns_sd_private.h" + +typedef unsigned int serviceHash_t; + +void start_BLE_browse(DNSQuestion * q, const domainname *const typeDomain, DNS_TypeValues type, DNSServiceFlags flags, + mDNSu8 *key, size_t keySize); +bool stop_BLE_browse(const domainname *const typeDomain, DNS_TypeValues type, DNSServiceFlags flags); + +void start_BLE_advertise(ServiceRecordSet * serviceRecordSet, const domainname *const domain, DNS_TypeValues type, DNSServiceFlags flags); +void stop_BLE_advertise(const domainname *const typeDomain, DNS_TypeValues type, DNSServiceFlags flags); + +void responseReceived(serviceHash_t browseHash, serviceHash_t registeredHash, mDNSEthAddr *ptrToMAC); + +void serviceBLE(void); + +// C interfaces to Objective-C beacon management code. +void updateBLEBeaconAndScan(serviceHash_t browseHash, serviceHash_t registeredHash); +void stopBLEBeacon(void); + +extern mDNS mDNSStorage; +extern mDNSBool EnableBLEBasedDiscovery; + +// TODO: Add the following to a local D2D.h file +#include + +// Just define as the current max value for now. +// TODO: Will need to define in DeviceToDeviceManager.framework if we convert this +// BLE discovery code to a D2D plugin. +#define D2DBLETransport D2DTransportMax + +#define applyToBLE(interface, flags) ((interface == mDNSInterface_BLE) || ((interface == mDNSInterface_Any) && (flags & kDNSServiceFlagsAutoTrigger))) + +#endif /* _BLE_H_ */ diff --git a/mDNSMacOSX/BonjourEvents.c b/mDNSMacOSX/BonjourEvents.c index f4f3e83..e06106e 100644 --- a/mDNSMacOSX/BonjourEvents.c +++ b/mDNSMacOSX/BonjourEvents.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2010 Apple Inc. All rights reserved. + * Copyright (c) 2010-2015 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. @@ -291,7 +291,8 @@ void * UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) (void)allocator; BonjourUserEventsPlugin * result = NULL; - if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) { + if (typeID && CFEqual(typeID, kUserEventAgentTypeID)) + { result = Alloc(kUserEventAgentFactoryID); } @@ -604,8 +605,6 @@ NetBrowserInfo* CreateBrowser(BonjourUserEventsPlugin* plugin, CFStringRef type, // Add the dictionary to the browsers dictionary. CFDictionarySetValue(plugin->_browsers, browser, browserDict); - NetBrowserInfoRelease(NULL, browser); - // Release Memory CFRelease(browserDict); } diff --git a/mDNSMacOSX/CUPolicy.c b/mDNSMacOSX/CUPolicy.c deleted file mode 100644 index 187748e..0000000 --- a/mDNSMacOSX/CUPolicy.c +++ /dev/null @@ -1,95 +0,0 @@ -/* -*- 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 - -#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 - { - xpc_object_t uuidx = xpc_uuid_create(q->uuid); - if (uuidx) - { - allowed = (mDNSBool) cellular_usage_policy_is_data_allowed_for_uuid(m->p->handle, uuidx); - if (!allowed) - { - network_config_cellular_blocked_notify(NULL, uuidx, NULL); - LogInfo("mDNSPlaformAllowPID: Notified UUID for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - } - xpc_release(uuidx); - } - else - { - allowed = false; - } - } - 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/mDNSMacOSX/CryptoSupport.c b/mDNSMacOSX/CryptoSupport.c index 408b3a2..57c7017 100644 --- a/mDNSMacOSX/CryptoSupport.c +++ b/mDNSMacOSX/CryptoSupport.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -350,18 +350,18 @@ mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, const void *data, mDNSu32 len) 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 + static const int max_modulus_bytes = 512; // Modulus is limited to 4096 bits (512 octets) in length. + static const int max_exp_bytes = 512; // Exponent is limited to 4096 bits (512 octets) in length. + static const int asn1_type_bytes = 3; // Since there is an ASN1 SEQ and two INTs. + static const int asn1_max_len_bytes = 3 * 3; // Capped at 3 due to max payload size. + unsigned char asn1[max_modulus_bytes + 1 + max_exp_bytes + asn1_type_bytes + asn1_max_len_bytes]; // +1 is for leading 0 for non negative asn1 number const mDNSu8 *modulus; unsigned int modulus_length; + const mDNSu8 *exponent; unsigned int exp_length; + unsigned int num_length_bytes; mDNSu32 index = 0; mDNSu32 asn1_length = 0; - unsigned int i; // Validate Input if (!data) @@ -372,16 +372,33 @@ mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) 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) + // If the first byte is zero, then the exponent length is in the three-byte format, otherwise the length is in the first byte. + if (data[0] == 0) + { + if (len < 3) + return NULL; + exp_length = (data[1] << 8) | data[2]; + num_length_bytes = 3; + } + else + { + exp_length = data[0]; + num_length_bytes = 1; + } + + // RFC3110 limits the exponent length to 4096 bits (512 octets). + if (exp_length > 512) + return NULL; + + // We have to have at least len bytes + size of exponent. + if (len < (num_length_bytes + exp_length)) return NULL; - // -1 is for the exp_length byte - modulus_length = len - 1 - exp_length; + // The modulus is the remaining space. + modulus_length = len - (num_length_bytes + exp_length); - // rfc3110 limits modulus to 4096 bits + // RFC3110 limits the modulus length to 4096 bits (512 octets). if (modulus_length > 512) return NULL; @@ -391,18 +408,30 @@ mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) // 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]; + exponent = &data[num_length_bytes]; + modulus = &data[num_length_bytes + 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; + // Account for modulus length causing INT length field to grow. + if (modulus_length >= 128) + { + if (modulus_length > 255) + asn1_length += 2; + else + asn1_length += 1; + } + + // Account for exponent length causing INT length field to grow. + if (exp_length >= 128) + { + if (exp_length > 255) + asn1_length += 2; + else + asn1_length += 1; + } // Construct ASN1 formatted public key // Write ASN1 SEQ byte @@ -415,8 +444,8 @@ mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) } else { - asn1[index++] = (0x80 | ((asn1_length & 0xFF00) ? 2 : 1)); - if (asn1_length & 0xFF00) + asn1[index++] = (0x80 | ((asn1_length > 255) ? 2 : 1)); + if (asn1_length > 255) asn1[index++] = (asn1_length & 0xFF00) >> 8; asn1[index++] = asn1_length & 0xFF; } @@ -426,12 +455,12 @@ mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) // Write ASN1 length for INT if (modulus_length < 128) { - asn1[index++] = asn1_length & 0xFF; + asn1[index++] = modulus_length & 0xFF; } else { - asn1[index++] = 0x80 | ((modulus_length & 0xFF00) ? 2 : 1); - if (modulus_length & 0xFF00) + asn1[index++] = 0x80 | ((modulus_length > 255) ? 2 : 1); + if (modulus_length > 255) asn1[index++] = (modulus_length & 0xFF00) >> 8; asn1[index++] = modulus_length & 0xFF; } @@ -439,16 +468,26 @@ mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) // 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; + memcpy(&asn1[index], 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; + if (exp_length < 128) + { + asn1[index++] = exp_length & 0xFF; + } + else + { + asn1[index++] = 0x80 | ((exp_length > 255) ? 2 : 1); + if (exp_length > 255) + asn1[index++] = (exp_length & 0xFF00) >> 8; + asn1[index++] = exp_length & 0xFF; + } // Write exponent bytes - for (i = 1; i <= exp_length; i++) - asn1[index++] = data[i]; + memcpy(&asn1[index], exponent, exp_length); + index += exp_length; #if TARGET_OS_IPHONE // index contains bytes written, use it for length @@ -605,6 +644,8 @@ mDNSlocal Boolean VerifyData(SecKeyRef key, CFStringRef digestStr, mDNSu8 *diges } CFBooleanRef boolRef = SecTransformExecute(verifyXForm, &error); + ret = boolRef ? CFBooleanGetValue(boolRef) : false; + if (boolRef) CFRelease(boolRef); CFRelease(verifyXForm); if (error != NULL) @@ -618,11 +659,12 @@ mDNSlocal Boolean VerifyData(SecKeyRef key, CFStringRef digestStr, mDNSu8 *diges { LogMsg("VerifyData: CFStringGetCString failed"); } + CFRelease(errStr); } LogMsg("VerifyData: SecTransformExecute failed with %s", errorbuf); return false; } - return CFEqual(boolRef, kCFBooleanTrue); + return ret; err: CFRelease(verifyXForm); return false; diff --git a/mDNSMacOSX/CryptoSupport.h b/mDNSMacOSX/CryptoSupport.h index 9c0fd07..8625ba7 100644 --- a/mDNSMacOSX/CryptoSupport.h +++ b/mDNSMacOSX/CryptoSupport.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * 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. @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #ifndef __CRYPTO_SUPPORT_H #define __CRYPTO_SUPPORT_H diff --git a/mDNSMacOSX/DNSProxySupport.c b/mDNSMacOSX/DNSProxySupport.c index 042bbc8..7666cbc 100644 --- a/mDNSMacOSX/DNSProxySupport.c +++ b/mDNSMacOSX/DNSProxySupport.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2011-2013 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. @@ -14,12 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "mDNSEmbeddedAPI.h" #include "mDNSMacOSX.h" #include #include #include +#include #define ValidSocket(s) ((s) >= 0) @@ -100,6 +102,9 @@ mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context) ProxyTCPInfo_t *ti = (ProxyTCPInfo_t *)context; TCPSocket *sock = &ti->sock; KQSocketSet *kq = &sock->ss; + struct tcp_info tcp_if; + socklen_t size = sizeof(tcp_if); + int32_t intf_id = 0; (void) filter; @@ -134,6 +139,12 @@ mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context) mDNSPlatformDisposeProxyContext(ti); return; } + if (getsockopt(s1, IPPROTO_TCP, TCP_INFO, &tcp_if, &size) != 0) + { + LogMsg("ProxyTCPReceive: getsockopt for TCP_INFO failed (fd=%d) errno %d", s1, errno); + return; + } + intf_id = tcp_if.tcpi_last_outif; if (from.ss_family == AF_INET) { @@ -147,7 +158,8 @@ mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context) 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); + LogInfo("ProxyTCPReceive received IPv4 packet(len %d) from %#-15a to %#-15a on skt %d %s ifindex %d", + ti->replyLen, &senderAddr, &destAddr, s1, NULL, intf_id); } else if (from.ss_family == AF_INET6) { @@ -160,7 +172,8 @@ mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context) 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); + LogInfo("ProxyTCPReceive received IPv6 packet(len %d) from %#-15a to %#-15a on skt %d %s ifindex %d", + ti->replyLen, &senderAddr, &destAddr, s1, NULL, intf_id); } else { @@ -173,7 +186,7 @@ mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context) // 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); + UnicastDNSPort, (mDNSInterfaceID)(uintptr_t)intf_id, ti); } mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context) @@ -203,6 +216,7 @@ mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context) return; } mDNSPlatformMemZero(ti, sizeof(ProxyTCPInfo_t)); + TCPSocket *sock = &ti->sock; kq = &sock->ss; @@ -221,6 +235,7 @@ mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context) { LogMsg("ProxyTCPAccept: IP_RECVIF %d errno %d (%s)", newfd, errno, strerror(errno)); mDNSPlatformDisposeProxyContext(ti); + close(newfd); return; } } @@ -234,6 +249,7 @@ mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context) { LogMsg("ProxyTCPAccept: IP_RECVPKTINFO %d errno %d (%s)", newfd, errno, strerror(errno)); mDNSPlatformDisposeProxyContext(ti); + close(newfd); return; } } @@ -243,7 +259,6 @@ mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context) // 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"; @@ -261,11 +276,9 @@ mDNSlocal mStatus SetupUDPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u 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 @@ -339,11 +352,9 @@ mDNSlocal mStatus SetupTCPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u { 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; @@ -384,7 +395,7 @@ mDNSlocal void BindDPSocket(int fd, int sa_family) 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)); + LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for IPv4 %d errno %d (%s)", fd, errno, strerror(errno)); memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; diff --git a/mDNSMacOSX/DNSSECSupport.c b/mDNSMacOSX/DNSSECSupport.c index e1255c4..b87f0d4 100644 --- a/mDNSMacOSX/DNSSECSupport.c +++ b/mDNSMacOSX/DNSSECSupport.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2012-2013 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. @@ -44,6 +44,8 @@ // 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 +#define DNSSECProbePercentage 1 + #if !TARGET_OS_IPHONE DNSQuestion DNSSECProbeQuestion; @@ -165,7 +167,7 @@ mDNSlocal mDNSu8 *ConvertDigest(char *digest, int digestType, int *diglen) int l, h; l = HexVal(digest[i]); h = HexVal(digest[i+1]); - if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); return NULL;} + if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); mDNSPlatformMemFree(dig); return NULL;} dig[j++] = (mDNSu8)((l << 4) | h); } return dig; @@ -449,9 +451,13 @@ mDNSlocal void FetchRootTA(mDNS *const m) return; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // 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)) + Boolean success = CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &xmlData, NULL, NULL, NULL); +#pragma clang diagnostic pop + if (!success) { LogInfo("FetchRootTA: CFURLCreateDataAndPropertiesFromResource error"); CFRelease(url); @@ -479,7 +485,8 @@ mDNSlocal void FetchRootTA(mDNS *const m) xmlDocPtr tadoc = xmlReadMemory((const char*)CFDataGetBytePtr(xmlData), (int)CFDataGetLength(xmlData), xmlFileName, NULL, 0); - CFRelease(fileRef); + if (fileRef) + CFRelease(fileRef); CFRelease(url); CFRelease(xmlData); @@ -555,8 +562,8 @@ mDNSexport void DNSSECProbe(mDNS *const m) return; rand = mDNSRandom(0x3FFFFFFF) % 100; - // Probe 5% of the time - if (rand > 5) + // Probe 1% of the time + if (rand >= DNSSECProbePercentage) return; mDNS_DropLockBeforeCallback(); diff --git a/mDNSMacOSX/DNSSECSupport.h b/mDNSMacOSX/DNSSECSupport.h index 2310fc2..eaaf36e 100644 --- a/mDNSMacOSX/DNSSECSupport.h +++ b/mDNSMacOSX/DNSSECSupport.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2012-2013 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. diff --git a/mDNSMacOSX/DNSServiceDiscovery.c b/mDNSMacOSX/DNSServiceDiscovery.c index 717efca..dd670ab 100644 --- a/mDNSMacOSX/DNSServiceDiscovery.c +++ b/mDNSMacOSX/DNSServiceDiscovery.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-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. @@ -25,7 +25,6 @@ #include "../mDNSMacOSX/DNSServiceDiscovery.h" #include "DNSServiceDiscoveryDefines.h" -#include "DNSServiceDiscoveryReplyServer.h" #include #include @@ -49,25 +48,6 @@ kern_return_t DNSServiceBrowserCreate_rpc 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 @@ -79,36 +59,6 @@ kern_return_t DNSServiceResolverResolve_rpc 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; @@ -122,553 +72,51 @@ struct a_requests { 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; + + (void) regtype; // Unused + (void) domain; // Unused + (void) callBack; // Unused + (void) context; // Unused + + printf("DNSServiceBrowserCreate deprecated since 10.3 \n"); + return NULL; + } -/* 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; - } + (void) name; // Unused + (void) regtype; // Unused + (void) domain; // Unused + (void) callBack; // Unused + (void) context; // Unused + + printf("DNSServiceResolverResolve deprecated since 10.3 \n"); + 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); + (void) replyMsg; // Unused + printf("DNSServiceDiscovery_handleReply deprecated since 10.3 \n"); } mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery) { + printf("DNSServiceDiscoveryMachPort deprecated since 10.3 \n"); 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; + (void) dnsServiceDiscovery; // Unused + printf("DNSServiceDiscoveryDeallocate deprecated since 10.3 \n"); } diff --git a/mDNSMacOSX/DNSServiceDiscovery.h b/mDNSMacOSX/DNSServiceDiscovery.h index ae73647..004d325 100644 --- a/mDNSMacOSX/DNSServiceDiscovery.h +++ b/mDNSMacOSX/DNSServiceDiscovery.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002, 2004, 2006, 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. @@ -17,31 +17,7 @@ /*! @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 + * @discussion 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. */ @@ -71,29 +47,38 @@ enum { }; +typedef enum +{ + DNSServiceDomainEnumerationReplyAddDomain, + DNSServiceDomainEnumerationReplyAddDomainDefault, + DNSServiceDomainEnumerationReplyRemoveDomain, +} 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 +); + + /* 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 void (*DNSServiceRegistrationReply)( + DNSServiceRegistrationReplyErrorType errorCode, + void *context +); + typedef uint32_t DNSRecordReference; @@ -107,79 +92,6 @@ typedef uint32_t DNSRecordReference; */ 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 ) - @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 */ @@ -271,44 +183,6 @@ mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDisc */ 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/mDNSMacOSX/DNSServiceDiscoveryDefines.h b/mDNSMacOSX/DNSServiceDiscoveryDefines.h index cfad0a4..01a28cd 100644 --- a/mDNSMacOSX/DNSServiceDiscoveryDefines.h +++ b/mDNSMacOSX/DNSServiceDiscoveryDefines.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2003, 2006, 2009, 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. @@ -20,8 +20,6 @@ #include -#define DNS_SERVICE_DISCOVERY_SERVER "com.apple.mDNSResponder" - typedef char DNSCString[1024]; typedef char sockaddr_t[128]; diff --git a/mDNSMacOSX/DNSServiceDiscoveryReply.defs b/mDNSMacOSX/DNSServiceDiscoveryReply.defs deleted file mode 100644 index b918bee..0000000 --- a/mDNSMacOSX/DNSServiceDiscoveryReply.defs +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- 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 -#include - -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/mDNSMacOSX/DNSServiceDiscoveryRequest.defs b/mDNSMacOSX/DNSServiceDiscoveryRequest.defs deleted file mode 100644 index ad06bdb..0000000 --- a/mDNSMacOSX/DNSServiceDiscoveryRequest.defs +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- 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 -#include - -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/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist b/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist deleted file mode 100644 index 895287c..0000000 --- a/mDNSMacOSX/LaunchDaemonInfo-Tiger.helper.plist +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Label - com.apple.mDNSResponderHelper - OnDemand - - ProgramArguments - - /usr/sbin/mDNSResponderHelper - -t - 0 - - ServiceIPC - - - diff --git a/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist b/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist deleted file mode 100644 index e91b775..0000000 --- a/mDNSMacOSX/LaunchDaemonInfo-Tiger.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Label - com.apple.mDNSResponder - OnDemand - - ProgramArguments - - /usr/sbin/mDNSResponder - -launchdaemon - - ServiceIPC - - - diff --git a/mDNSMacOSX/LegacyNATTraversal.c b/mDNSMacOSX/LegacyNATTraversal.c index e7cd65f..acfb3dc 100644 --- a/mDNSMacOSX/LegacyNATTraversal.c +++ b/mDNSMacOSX/LegacyNATTraversal.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2004-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2015 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. @@ -22,6 +22,7 @@ #include "assert.h" // For assert() #if defined( WIN32 ) +# include "CommonServices.h" # include # include # define strcasecmp _stricmp @@ -82,8 +83,11 @@ mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n); 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; } + if ((strlen((char*)src)) >= UINT32_MAX || (*dst = mDNSPlatformMemAllocate((mDNSu32)strlen((char*)src) + 1)) == mDNSNULL) + { + LogMsg("AllocAndCopy: can't allocate string"); + return; + } strcpy((char*)*dst, (char*)src); } @@ -279,7 +283,7 @@ mDNSlocal void handleLNTDeviceDescriptionResponse(tcpLNTInfo *tcpInfo) 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; } } + for (stop = ptr; stop < end; stop++) { if (stop && *stop == '<') { end = stop; break; } } if (ParseHttpUrl(ptr, end, &m->UPnPSOAPAddressString, &m->UPnPSOAPPort, mDNSNULL) != mStatus_NoError) { LogInfo("handleLNTDeviceDescriptionResponse: failed to parse URLBase"); @@ -412,8 +416,6 @@ mDNSlocal void tcpConnectionCallback(TCPSocket *sock, void *context, mDNSBool Co 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); @@ -793,7 +795,7 @@ mDNSexport void LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID Interface { const mDNSu8 *ptr = data; const mDNSu8 *end = data + len; - const mDNSu8 *stop = ptr; + const mDNSu8 *stop; if (!mDNSIPPortIsZero(m->UPnPRouterPort)) return; // already have the info we need diff --git a/mDNSMacOSX/Metrics.h b/mDNSMacOSX/Metrics.h new file mode 100644 index 0000000..dbe6196 --- /dev/null +++ b/mDNSMacOSX/Metrics.h @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2016 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 "mDNSEmbeddedAPI.h" + +#ifndef __Metrics_h +#define __Metrics_h + +#ifdef __cplusplus +extern "C" { +#endif + +#if TARGET_OS_EMBEDDED +mStatus MetricsInit(void); +void MetricsUpdateUDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, mDNSu32 inLatencyMs, mDNSBool inForCell); +void MetricsUpdateUDNSResolveStats(const domainname *inQueryName, const ResourceRecord *inRR, mDNSBool inForCell); +void LogMetrics(void); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __Metrics_h diff --git a/mDNSMacOSX/Metrics.m b/mDNSMacOSX/Metrics.m new file mode 100644 index 0000000..bfff58e --- /dev/null +++ b/mDNSMacOSX/Metrics.m @@ -0,0 +1,1967 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2016 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. + */ + +#import "Metrics.h" + +#if (TARGET_OS_EMBEDDED) +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "DNSCommon.h" +#import "mDNSMacOSX.h" +#import "DebugServices.h" + +//=========================================================================================================================== +// External Frameworks +//=========================================================================================================================== + +SOFT_LINK_FRAMEWORK(PrivateFrameworks, WirelessDiagnostics) + +SOFT_LINK_CLASS(WirelessDiagnostics, AWDDNSDomainStats) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSStatistics) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStats) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsDNSServer) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsDomain) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsHostname) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderResolveStatsResult) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDServerConnection) +SOFT_LINK_CLASS(WirelessDiagnostics, AWDMetricManager) + +#define AWDDNSDomainStatsSoft getAWDDNSDomainStatsClass() +#define AWDMDNSResponderDNSStatisticsSoft getAWDMDNSResponderDNSStatisticsClass() +#define AWDMDNSResponderResolveStatsSoft getAWDMDNSResponderResolveStatsClass() +#define AWDMDNSResponderResolveStatsResultSoft getAWDMDNSResponderResolveStatsResultClass() +#define AWDMDNSResponderResolveStatsDNSServerSoft getAWDMDNSResponderResolveStatsDNSServerClass() +#define AWDMDNSResponderResolveStatsDomainSoft getAWDMDNSResponderResolveStatsDomainClass() +#define AWDMDNSResponderResolveStatsHostnameSoft getAWDMDNSResponderResolveStatsHostnameClass() +#define AWDServerConnectionSoft getAWDServerConnectionClass() +#define AWDMetricManagerSoft getAWDMetricManagerClass() + +//=========================================================================================================================== +// Macros +//=========================================================================================================================== + +#define countof(X) (sizeof(X) / sizeof(X[0])) +#define countof_field(TYPE, FIELD) countof(((TYPE *)0)->FIELD) +#define increment_saturate(VAR, MAX) do {if ((VAR) < (MAX)) {++(VAR);}} while (0) +#define ForgetMem(X) do {if(*(X)) {free(*(X)); *(X) = NULL;}} while(0) + +//=========================================================================================================================== +// Constants +//=========================================================================================================================== + +#define kQueryStatsMaxQuerySendCount 10 +#define kQueryStatsSendCountBinCount (kQueryStatsMaxQuerySendCount + 1) +#define kQueryStatsLatencyBinCount 55 +#define kResolveStatsMaxObjCount 2000 + +//=========================================================================================================================== +// Data structures +//=========================================================================================================================== + +typedef struct +{ + const char * cstr; // Name of domain as a c-string. + const domainname * name; // Name of domain as length-prefixed labels. + int labelCount; // Number of labels in domain name. Used for domain name comparisons. + +} Domain; + +// Important: Do not add to this list without getting privacy approval beforehand. See . +// If you get approval and do add a domain to this list, make sure it passes ValidateDNSStatsDomains() below. + +static const Domain kQueryStatsDomains[] = +{ + { ".", (domainname *)"", 0 }, + { "apple.com.", (domainname *)"\x5" "apple" "\x3" "com", 2 }, + { "icloud.com.", (domainname *)"\x6" "icloud" "\x3" "com", 2 }, + { "mzstatic.com.", (domainname *)"\x8" "mzstatic" "\x3" "com", 2 }, + { "google.com.", (domainname *)"\x6" "google" "\x3" "com", 2 }, + { "facebook.com.", (domainname *)"\x8" "facebook" "\x3" "com", 2 }, + { "baidu.com.", (domainname *)"\x5" "baidu" "\x3" "com", 2 }, + { "yahoo.com.", (domainname *)"\x5" "yahoo" "\x3" "com", 2 }, + { "qq.com.", (domainname *)"\x2" "qq" "\x3" "com", 2 }, +}; + +check_compile_time(countof(kQueryStatsDomains) == 9); + +// DNSHist contains the per domain per network type histogram data that goes in a DNSDomainStats protobuf message. See +// MDNSResponder.proto update. +// +// answeredQuerySendCountBins +// +// An array of 11 histogram bins. The value at index i, for 0 <= i <= 9, is the number of times that an answered DNS query +// was sent i times. The value at index 10 is the number of times that an answered query was sent 10+ times. +// +// unansweredQuerySendCountBins +// +// An array of 11 histogram bins. The value at index i, for 0 <= i <= 9, is the number of times that an unanswered DNS query +// was sent i times. The value at index 10 is the number of times that an unanswered query was sent 10+ times. +// +// responseLatencyBins +// +// An array of 55 histogram bins. Each array value is the number of DNS queries that were answered in a paricular time +// interval. The 55 consecutive non-overlapping time intervals have the following non-inclusive upper bounds (all values are +// in milliseconds): 1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, +// 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, 1000, 1500, 2000, 2500, 3000, 3500, 4000, +// 4500, 5000, 6000, 7000, 8000, 9000, 10000, ∞. + +typedef struct +{ + uint16_t unansweredQuerySendCountBins[kQueryStatsSendCountBinCount]; + uint16_t unansweredQueryDurationBins[kQueryStatsLatencyBinCount]; + uint16_t answeredQuerySendCountBins[kQueryStatsSendCountBinCount]; + uint16_t responseLatencyBins[kQueryStatsLatencyBinCount]; + uint16_t negAnsweredQuerySendCountBins[kQueryStatsSendCountBinCount]; + uint16_t negResponseLatencyBins[kQueryStatsLatencyBinCount]; + +} DNSHist; + +check_compile_time(sizeof(DNSHist) <= 512); +check_compile_time(countof_field(DNSHist, unansweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1)); +check_compile_time(countof_field(DNSHist, answeredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1)); +check_compile_time(countof_field(DNSHist, negAnsweredQuerySendCountBins) == (kQueryStatsMaxQuerySendCount + 1)); + +// Important: Do not modify kResponseLatencyMsLimits because the code used to generate AWD reports expects the response +// latency histogram bins to observe these time interval upper bounds. + +static const mDNSu32 kResponseLatencyMsLimits[] = +{ + 1, 2, 3, 4, 5, + 10, 20, 30, 40, 50, 60, 70, 80, 90, + 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, + 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950, + 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, + 5000, 6000, 7000, 8000, 9000, + 10000 +}; + +check_compile_time(countof(kResponseLatencyMsLimits) == 54); +check_compile_time(countof_field(DNSHist, unansweredQueryDurationBins) == (countof(kResponseLatencyMsLimits) + 1)); +check_compile_time(countof_field(DNSHist, responseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1)); +check_compile_time(countof_field(DNSHist, negResponseLatencyBins) == (countof(kResponseLatencyMsLimits) + 1)); + +typedef struct +{ + DNSHist * histA; // Histogram data for queries for A resource records. + DNSHist * histAAAA; // Histogram data for queries for AAAA resource records. + +} DNSHistSet; + +typedef struct DNSDomainStats DNSDomainStats; +struct DNSDomainStats +{ + DNSDomainStats * next; // Pointer to next domain stats in list. + const Domain * domain; // Domain for which these stats are collected. + DNSHistSet * nonCellular; // Query stats for queries sent over non-cellular interfaces. + DNSHistSet * cellular; // Query stats for queries sent over cellular interfaces. +}; + +check_compile_time(sizeof(struct DNSDomainStats) <= 32); + +static const Domain kResolveStatsDomains[] = +{ + { "apple.com.", (domainname *)"\x5" "apple" "\x3" "com", 2 }, + { "icloud.com.", (domainname *)"\x6" "icloud" "\x3" "com", 2 }, + { "mzstatic.com.", (domainname *)"\x8" "mzstatic" "\x3" "com", 2 }, + { "me.com.", (domainname *)"\x2" "me" "\x3" "com", 2 }, +}; + +check_compile_time(countof(kResolveStatsDomains) == 4); + +typedef struct ResolveStatsDomain ResolveStatsDomain; +typedef struct ResolveStatsHostname ResolveStatsHostname; +typedef struct ResolveStatsDNSServer ResolveStatsDNSServer; +typedef struct ResolveStatsIPv4AddrSet ResolveStatsIPv4AddrSet; +typedef struct ResolveStatsIPv6Addr ResolveStatsIPv6Addr; +typedef struct ResolveStatsNegAAAASet ResolveStatsNegAAAASet; + +struct ResolveStatsDomain +{ + ResolveStatsDomain * next; // Next domain object in list. + ResolveStatsHostname * hostnameList; // List of hostname objects in this domain. + const Domain * domainInfo; // Pointer to domain info. +}; + +struct ResolveStatsHostname +{ + ResolveStatsHostname * next; // Next hostname object in list. + ResolveStatsIPv4AddrSet * addrV4List; // List of IPv4 addresses to which this hostname resolved. + ResolveStatsIPv6Addr * addrV6List; // List of IPv6 addresses to which this hostname resolved. + ResolveStatsNegAAAASet * negV6List; // List of negative AAAA response objects. + uint8_t name[1]; // Variable length storage for hostname as length-prefixed labels. +}; + +check_compile_time(sizeof(ResolveStatsHostname) <= 64); + +struct ResolveStatsDNSServer +{ + ResolveStatsDNSServer * next; // Next DNS server object in list. + uint8_t id; // 8-bit ID assigned to this DNS server used by IP address objects. + mDNSBool isForCell; // True if this DNS server belongs to a cellular interface. + mDNSBool isAddrV6; // True if this DNS server has an IPv6 address instead of IPv4. + uint8_t addrBytes[1]; // Variable length storage for DNS server's IP address. +}; + +check_compile_time(sizeof(ResolveStatsDNSServer) <= 32); + +typedef struct +{ + uint16_t count; // Number of times this IPv4 address was provided as a resolution result. + uint8_t serverID; // 8-bit ID of the DNS server from which this IPv4 address came. + uint8_t isNegative; + uint8_t addrBytes[4]; // IPv4 address bytes. + +} IPv4AddrCounter; + +check_compile_time(sizeof(IPv4AddrCounter) <= 8); + +struct ResolveStatsIPv4AddrSet +{ + ResolveStatsIPv4AddrSet * next; // Next set of IPv4 address counters in list. + IPv4AddrCounter counters[3]; // Array of IPv4 address counters. +}; + +check_compile_time(sizeof(ResolveStatsIPv4AddrSet) <= 32); + +struct ResolveStatsIPv6Addr +{ + ResolveStatsIPv6Addr * next; // Next IPv6 address object in list. + uint16_t count; // Number of times this IPv6 address was provided as a resolution result. + uint8_t serverID; // 8-bit ID of the DNS server from which this IPv6 address came. + uint8_t addrBytes[16]; // IPv6 address bytes. +}; + +check_compile_time(sizeof(ResolveStatsIPv6Addr) <= 32); + +typedef struct +{ + uint16_t count; // Number of times that a negative response was returned by a DNS server. + uint8_t serverID; // 8-bit ID of the DNS server that sent the negative responses. + +} NegAAAACounter; + +check_compile_time(sizeof(NegAAAACounter) <= 4); + +struct ResolveStatsNegAAAASet +{ + ResolveStatsNegAAAASet * next; // Next set of negative AAAA response counters in list. + NegAAAACounter counters[6]; // Array of negative AAAA response counters. +}; + +check_compile_time(sizeof(ResolveStatsNegAAAASet) <= 32); + +typedef enum +{ + kResponseType_IPv4Addr = 1, + kResponseType_IPv6Addr = 2, + kResponseType_NegA = 3, + kResponseType_NegAAAA = 4 + +} ResponseType; + +typedef struct +{ + ResponseType type; + const uint8_t * data; + +} Response; + +//=========================================================================================================================== +// Globals +//=========================================================================================================================== + +extern mDNS mDNSStorage; + +static DNSDomainStats * gDomainStatsList = NULL; +static ResolveStatsDomain * gResolveStatsList = NULL; +static ResolveStatsDNSServer * gResolveStatsServerList = NULL; +static unsigned int gResolveStatsNextServerID = 0; +static int gResolveStatsObjCount = 0; +static AWDServerConnection * gAWDServerConnection = nil; + +//=========================================================================================================================== +// Local Prototypes +//=========================================================================================================================== + +mDNSlocal mStatus DNSDomainStatsCreate(const Domain *inDomain, DNSDomainStats **outStats); +mDNSlocal void DNSDomainStatsFree(DNSDomainStats *inStats); +mDNSlocal void DNSDomainStatsFreeList(DNSDomainStats *inList); +mDNSlocal mStatus DNSDomainStatsUpdate(DNSDomainStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs, mDNSBool inForCell); + +mDNSlocal mStatus ResolveStatsDomainCreate(const Domain *inDomain, ResolveStatsDomain **outDomain); +mDNSlocal void ResolveStatsDomainFree(ResolveStatsDomain *inDomain); +mDNSlocal mStatus ResolveStatsDomainUpdate(ResolveStatsDomain *inDomain, const domainname *inHostname, const Response *inResp, const mDNSAddr *inDNSAddr, mDNSBool inForCell); +mDNSlocal mStatus ResolveStatsDomainCreateAWDVersion(const ResolveStatsDomain *inDomain, AWDMDNSResponderResolveStatsDomain **outDomain); + +mDNSlocal mStatus ResolveStatsHostnameCreate(const domainname *inName, ResolveStatsHostname **outHostname); +mDNSlocal void ResolveStatsHostnameFree(ResolveStatsHostname *inHostname); +mDNSlocal mStatus ResolveStatsHostnameUpdate(ResolveStatsHostname *inHostname, const Response *inResp, uint8_t inServerID); +mDNSlocal mStatus ResolveStatsHostnameCreateAWDVersion(const ResolveStatsHostname *inHostname, AWDMDNSResponderResolveStatsHostname **outHostname); + +mDNSlocal mStatus ResolveStatsDNSServerCreate(const mDNSAddr *inAddr, mDNSBool inForCell, ResolveStatsDNSServer **outServer); +mDNSlocal void ResolveStatsDNSServerFree(ResolveStatsDNSServer *inServer); +mDNSlocal mStatus ResolveStatsDNSServerCreateAWDVersion(const ResolveStatsDNSServer *inServer, AWDMDNSResponderResolveStatsDNSServer **outServer); + +mDNSlocal mStatus ResolveStatsIPv4AddrSetCreate(ResolveStatsIPv4AddrSet **outSet); +mDNSlocal void ResolveStatsIPv4AddrSetFree(ResolveStatsIPv4AddrSet *inSet); + +mDNSlocal mStatus ResolveStatsIPv6AddressCreate(uint8_t inServerID, const uint8_t inAddrBytes[16], ResolveStatsIPv6Addr **outAddr); +mDNSlocal void ResolveStatsIPv6AddressFree(ResolveStatsIPv6Addr *inAddr); + +mDNSlocal mStatus ResolveStatsNegAAAASetCreate(ResolveStatsNegAAAASet **outSet); +mDNSlocal void ResolveStatsNegAAAASetFree(ResolveStatsNegAAAASet *inSet); +mDNSlocal mStatus ResolveStatsGetServerID(const mDNSAddr *inServerAddr, mDNSBool inForCell, uint8_t *outServerID); + +mDNSlocal mStatus CreateDomainStatsList(DNSDomainStats **outList); +mDNSlocal mStatus CreateResolveStatsList(ResolveStatsDomain **outList); +mDNSlocal void FreeResolveStatsList(ResolveStatsDomain *inList); +mDNSlocal void FreeResolveStatsServerList(ResolveStatsDNSServer *inList); +mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID); +mDNSlocal mStatus SubmitAWDMetricQueryStats(void); +mDNSlocal mStatus SubmitAWDMetricResolveStats(void); +mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats); +mDNSlocal mStatus AddAWDDNSDomainStats(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell); +mDNSlocal void LogDNSHistSet(const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell); +mDNSlocal void LogDNSHist(const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType); +mDNSlocal void LogDNSHistSendCounts(const uint16_t inSendCountBins[kQueryStatsSendCountBinCount]); +mDNSlocal void LogDNSHistLatencies(const uint16_t inLatencyBins[kQueryStatsLatencyBinCount]); +#if (METRICS_VALIDATE_DNS_STATS_DOMAINS) +mDNSlocal void ValidateDNSStatsDomains(void); +#endif + +//=========================================================================================================================== +// MetricsInit +//=========================================================================================================================== + +mStatus MetricsInit(void) +{ +#if (METRICS_VALIDATE_DNS_STATS_DOMAINS) + ValidateDNSStatsDomains(); +#endif + + @autoreleasepool + { + gAWDServerConnection = [[AWDServerConnectionSoft alloc] + initWithComponentId: AWDComponentId_MDNSResponder + andBlockOnConfiguration: NO]; + + if (gAWDServerConnection) + { + [gAWDServerConnection + registerQueriableMetricCallback: ^(UInt32 inMetricID) + { + SubmitAWDMetric(inMetricID); + } + forIdentifier: (UInt32)AWDMetricId_MDNSResponder_DNSStatistics]; + + [gAWDServerConnection + registerQueriableMetricCallback: ^(UInt32 inMetricID) + { + SubmitAWDMetric(inMetricID); + } + forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ResolveStats]; + + [gAWDServerConnection + registerQueriableMetricCallback: ^(UInt32 inMetricID) + { + SubmitAWDMetric(inMetricID); + } + forIdentifier: (UInt32)AWDMetricId_MDNSResponder_ServicesStats]; + } + else + { + LogMsg("MetricsInit: failed to create AWD server connection."); + } + } + + if( gAWDServerConnection ) + { + CreateDomainStatsList(&gDomainStatsList); + CreateResolveStatsList(&gResolveStatsList); + } + + return (mStatus_NoError); +} + +//=========================================================================================================================== +// MetricsUpdateUDNSQueryStats +//=========================================================================================================================== + +mDNSexport void MetricsUpdateUDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, mDNSu32 inLatencyMs, mDNSBool inForCell) +{ + DNSDomainStats * stats; + int queryLabelCount; + const domainname * queryParentDomain; + mDNSBool isQueryInDomain; + int skipCount; + int skipCountLast = -1; + + require_quiet(gAWDServerConnection, exit); + require_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit); + + queryLabelCount = CountLabels(inQueryName); + + for (stats = gDomainStatsList; stats; stats = stats->next) + { + isQueryInDomain = mDNSfalse; + if (strcmp(stats->domain->cstr, ".") == 0) + { + // All queries are in the root domain. + isQueryInDomain = mDNStrue; + } + else + { + skipCount = queryLabelCount - stats->domain->labelCount; + if (skipCount >= 0) + { + if (skipCount != skipCountLast) + { + queryParentDomain = SkipLeadingLabels(inQueryName, skipCount); + skipCountLast = skipCount; + } + isQueryInDomain = SameDomainName(queryParentDomain, stats->domain->name); + } + } + + if (isQueryInDomain) + { + DNSDomainStatsUpdate(stats, inType, inRR, inSendCount, inLatencyMs, inForCell); + } + } + +exit: + return; +} + +//=========================================================================================================================== +// MetricsUpdateUDNSResolveStats +//=========================================================================================================================== + +mDNSexport void MetricsUpdateUDNSResolveStats(const domainname *inQueryName, const ResourceRecord *inRR, mDNSBool inForCell) +{ + ResolveStatsDomain * domain; + domainname hostname; + size_t hostnameLen; + mDNSBool isQueryInDomain; + int skipCount; + int skipCountLast = -1; + int queryLabelCount; + const domainname * queryParentDomain; + Response response; + + require_quiet(gAWDServerConnection, exit); + require_quiet((inRR->rrtype == kDNSType_A) || (inRR->rrtype == kDNSType_AAAA), exit); + require_quiet(inRR->rDNSServer, exit); + + queryLabelCount = CountLabels(inQueryName); + + for (domain = gResolveStatsList; domain; domain = domain->next) + { + isQueryInDomain = mDNSfalse; + skipCount = queryLabelCount - domain->domainInfo->labelCount; + if (skipCount >= 0) + { + if (skipCount != skipCountLast) + { + queryParentDomain = SkipLeadingLabels(inQueryName, skipCount); + skipCountLast = skipCount; + } + isQueryInDomain = SameDomainName(queryParentDomain, domain->domainInfo->name); + } + if (!isQueryInDomain) continue; + + hostnameLen = (size_t)(queryParentDomain->c - inQueryName->c); + if (hostnameLen >= sizeof(hostname.c)) continue; + + memcpy(hostname.c, inQueryName->c, hostnameLen); + hostname.c[hostnameLen] = 0; + + if (inRR->RecordType == kDNSRecordTypePacketNegative) + { + response.type = (inRR->rrtype == kDNSType_A) ? kResponseType_NegA : kResponseType_NegAAAA; + response.data = NULL; + } + else + { + response.type = (inRR->rrtype == kDNSType_A) ? kResponseType_IPv4Addr : kResponseType_IPv6Addr; + response.data = (inRR->rrtype == kDNSType_A) ? inRR->rdata->u.ipv4.b : inRR->rdata->u.ipv6.b; + } + ResolveStatsDomainUpdate(domain, &hostname, &response, &inRR->rDNSServer->addr, inForCell); + } + +exit: + return; +} + +//=========================================================================================================================== +// LogMetrics +//=========================================================================================================================== + +mDNSexport void LogMetrics(void) +{ + DNSDomainStats * stats; + const ResolveStatsDomain * domain; + const ResolveStatsHostname * hostname; + const ResolveStatsDNSServer * server; + const ResolveStatsIPv4AddrSet * addrV4; + const ResolveStatsIPv6Addr * addrV6; + const ResolveStatsNegAAAASet * negV6; + int hostnameCount; + int i; + unsigned int serverID; + int serverObjCount = 0; + int hostnameObjCount = 0; + int addrObjCount = 0; + + LogMsgNoIdent("gAWDServerConnection %p", gAWDServerConnection); + LogMsgNoIdent("---- DNS query stats by domain -----"); + + for (stats = gDomainStatsList; stats; stats = stats->next) + { + if (!stats->nonCellular && !stats->cellular) + { + LogMsgNoIdent("No data for %s", stats->domain->cstr); + continue; + } + if (stats->nonCellular) LogDNSHistSet(stats->nonCellular, stats->domain->cstr, mDNSfalse); + if (stats->cellular) LogDNSHistSet(stats->cellular, stats->domain->cstr, mDNStrue); + } + + LogMsgNoIdent("---- DNS resolve stats by domain -----"); + + LogMsgNoIdent("Servers:"); + for (server = gResolveStatsServerList; server; server = server->next) + { + serverObjCount++; + LogMsgNoIdent(server->isAddrV6 ? "%2u: %s %.16a" : "%2u: %s %.4a", + server->id, server->isForCell ? " C" : "NC", server->addrBytes); + } + + for (domain = gResolveStatsList; domain; domain = domain->next) + { + hostnameCount = 0; + for (hostname = domain->hostnameList; hostname; hostname = hostname->next) { hostnameCount++; } + hostnameObjCount += hostnameCount; + + LogMsgNoIdent("%##s (%d hostname%s)", domain->domainInfo->name, hostnameCount, (hostnameCount == 1) ? "" : "s"); + + for (hostname = domain->hostnameList; hostname; hostname = hostname->next) + { + LogMsgNoIdent(" %##s", hostname->name); + for (serverID = 0; serverID < gResolveStatsNextServerID; ++serverID) + { + for (addrV4 = hostname->addrV4List; addrV4; addrV4 = addrV4->next) + { + if (serverID == 0) addrObjCount++; + for (i = 0; i < (int)countof(addrV4->counters); ++i) + { + const IPv4AddrCounter * counter; + + counter = &addrV4->counters[i]; + if (counter->count == 0) break; + if (counter->serverID == serverID) + { + if (counter->isNegative) + { + LogMsgNoIdent("%10u: %3u negative A", counter->serverID, counter->count); + } + else + { + LogMsgNoIdent("%10u: %3u %.4a", counter->serverID, counter->count, counter->addrBytes); + } + } + } + } + for (addrV6 = hostname->addrV6List; addrV6; addrV6 = addrV6->next) + { + if (serverID == 0) addrObjCount++; + if (addrV6->serverID == serverID) + { + LogMsgNoIdent("%10u: %3u %.16a", addrV6->serverID, addrV6->count, addrV6->addrBytes); + } + } + for (negV6 = hostname->negV6List; negV6; negV6 = negV6->next) + { + if (serverID == 0) addrObjCount++; + for (i = 0; i < (int)countof(negV6->counters); ++i) + { + const NegAAAACounter * counter; + + counter = &negV6->counters[i]; + if (counter->count == 0) break; + if (counter->serverID == serverID) + { + LogMsgNoIdent("%10u: %3u negative AAAA", counter->serverID, counter->count); + } + } + } + } + } + } + LogMsgNoIdent("Total object count: %3d (server %d hostname %d address %d)", + serverObjCount + hostnameObjCount + addrObjCount, serverObjCount, hostnameObjCount, addrObjCount); + + LogMsgNoIdent("---- Num of Services Registered -----"); + LogMsgNoIdent("Current_number_of_services_registered :[%d], Max_number_of_services_registered :[%d]", + curr_num_regservices, max_num_regservices); +} + +//=========================================================================================================================== +// DNSDomainStatsCreate +//=========================================================================================================================== + +mDNSlocal mStatus DNSDomainStatsCreate(const Domain *inDomain, DNSDomainStats **outStats) +{ + mStatus err; + DNSDomainStats * obj; + + obj = (DNSDomainStats *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + obj->domain = inDomain; + + *outStats = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// DNSDomainStatsFree +//=========================================================================================================================== + +mDNSlocal void DNSDomainStatsFree(DNSDomainStats *inStats) +{ + if (inStats->nonCellular) + { + ForgetMem(&inStats->nonCellular->histA); + ForgetMem(&inStats->nonCellular->histAAAA); + free(inStats->nonCellular); + inStats->nonCellular = NULL; + } + if (inStats->cellular) + { + ForgetMem(&inStats->cellular->histA); + ForgetMem(&inStats->cellular->histAAAA); + free(inStats->cellular); + inStats->cellular = NULL; + } + free(inStats); +} + +//=========================================================================================================================== +// DNSDomainStatsFreeList +//=========================================================================================================================== + +mDNSlocal void DNSDomainStatsFreeList(DNSDomainStats *inList) +{ + DNSDomainStats * stats; + + while ((stats = inList) != NULL) + { + inList = stats->next; + DNSDomainStatsFree(stats); + } +} + +//=========================================================================================================================== +// DNSDomainStatsUpdate +//=========================================================================================================================== + +mDNSlocal mStatus DNSDomainStatsUpdate(DNSDomainStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs, mDNSBool inForCell) +{ + mStatus err; + DNSHistSet * set; + DNSHistSet ** pSet; + DNSHist * hist; + DNSHist ** pHist; + int i; + + require_action_quiet(inRR || (inQuerySendCount > 0), exit, err = mStatus_NoError); + require_action_quiet((inType == kDNSType_A) || (inType == kDNSType_AAAA), exit, err = mStatus_NoError); + + pSet = inForCell ? &inStats->cellular : &inStats->nonCellular; + if ((set = *pSet) == NULL) + { + set = (DNSHistSet *)calloc(1, sizeof(*set)); + require_action_quiet(set, exit, err = mStatus_NoMemoryErr); + *pSet = set; + } + pHist = (inType == kDNSType_A) ? &set->histA : &set->histAAAA; + if ((hist = *pHist) == NULL) + { + hist = (DNSHist *)calloc(1, sizeof(*hist)); + require_action_quiet(hist, exit, err = mStatus_NoMemoryErr); + *pHist = hist; + } + + if (inRR) + { + uint16_t * sendCountBins; + uint16_t * latencyBins; + const mDNSBool isNegative = (inRR->RecordType == kDNSRecordTypePacketNegative); + + i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount); + + sendCountBins = isNegative ? hist->negAnsweredQuerySendCountBins : hist->answeredQuerySendCountBins; + increment_saturate(sendCountBins[i], UINT16_MAX); + + if (inQuerySendCount > 0) + { + for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {} + latencyBins = isNegative ? hist->negResponseLatencyBins : hist->responseLatencyBins; + increment_saturate(latencyBins[i], UINT16_MAX); + } + } + else + { + i = Min(inQuerySendCount, kQueryStatsMaxQuerySendCount); + increment_saturate(hist->unansweredQuerySendCountBins[i], UINT16_MAX); + + for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {} + increment_saturate(hist->unansweredQueryDurationBins[i], UINT16_MAX); + } + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDomainCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDomainCreate(const Domain *inDomain, ResolveStatsDomain **outDomain) +{ + mStatus err; + ResolveStatsDomain * obj; + + obj = (ResolveStatsDomain *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + obj->domainInfo = inDomain; + + *outDomain = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDomainFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsDomainFree(ResolveStatsDomain *inDomain) +{ + ResolveStatsHostname * hostname; + + while ((hostname = inDomain->hostnameList) != NULL) + { + inDomain->hostnameList = hostname->next; + ResolveStatsHostnameFree(hostname); + } + free(inDomain); +} + +//=========================================================================================================================== +// ResolveStatsDomainUpdate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDomainUpdate(ResolveStatsDomain *inDomain, const domainname *inHostname, const Response *inResp, const mDNSAddr *inDNSAddr, mDNSBool inForCell) +{ + mStatus err; + ResolveStatsHostname ** p; + ResolveStatsHostname * hostname; + uint8_t serverID; + + for (p = &inDomain->hostnameList; (hostname = *p) != NULL; p = &hostname->next) + { + if (SameDomainName((domainname *)hostname->name, inHostname)) break; + } + + if (!hostname) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsHostnameCreate(inHostname, &hostname); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + *p = hostname; + } + + err = ResolveStatsGetServerID(inDNSAddr, inForCell, &serverID); + require_noerr_quiet(err, exit); + + err = ResolveStatsHostnameUpdate(hostname, inResp, serverID); + require_noerr_quiet(err, exit); + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsHostnameCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsHostnameCreate(const domainname *inName, ResolveStatsHostname **outHostname) +{ + mStatus err; + ResolveStatsHostname * obj; + size_t nameLen; + + nameLen = DomainNameLength(inName); + require_action_quiet(nameLen > 0, exit, err = mStatus_Invalid); + + obj = (ResolveStatsHostname *)calloc(1, sizeof(*obj) - 1 + nameLen); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + memcpy(obj->name, inName, nameLen); + + *outHostname = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDomainCreateAWDVersion +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDomainCreateAWDVersion(const ResolveStatsDomain *inDomain, AWDMDNSResponderResolveStatsDomain **outDomain) +{ + mStatus err; + AWDMDNSResponderResolveStatsDomain * domain; + ResolveStatsHostname * hostname; + AWDMDNSResponderResolveStatsHostname * awdHostname; + NSString * name; + + domain = [[AWDMDNSResponderResolveStatsDomainSoft alloc] init]; + require_action_quiet(domain, exit, err = mStatus_UnknownErr); + + name = [[NSString alloc] initWithUTF8String:inDomain->domainInfo->cstr]; + require_action_quiet(name, exit, err = mStatus_UnknownErr); + + domain.name = name; + [name release]; + name = nil; + + for (hostname = inDomain->hostnameList; hostname; hostname = hostname->next) + { + err = ResolveStatsHostnameCreateAWDVersion(hostname, &awdHostname); + require_noerr_quiet(err, exit); + + [domain addHostname:awdHostname]; + [awdHostname release]; + awdHostname = nil; + } + + *outDomain = domain; + domain = nil; + err = mStatus_NoError; + +exit: + [domain release]; + return (err); +} + +//=========================================================================================================================== +// ResolveStatsHostnameFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsHostnameFree(ResolveStatsHostname *inHostname) +{ + ResolveStatsIPv4AddrSet * addrV4; + ResolveStatsIPv6Addr * addrV6; + ResolveStatsNegAAAASet * negV6; + + while ((addrV4 = inHostname->addrV4List) != NULL) + { + inHostname->addrV4List = addrV4->next; + ResolveStatsIPv4AddrSetFree(addrV4); + } + while ((addrV6 = inHostname->addrV6List) != NULL) + { + inHostname->addrV6List = addrV6->next; + ResolveStatsIPv6AddressFree(addrV6); + } + while ((negV6 = inHostname->negV6List) != NULL) + { + inHostname->negV6List = negV6->next; + ResolveStatsNegAAAASetFree(negV6); + } + free(inHostname); +} + +//=========================================================================================================================== +// ResolveStatsHostnameUpdate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsHostnameUpdate(ResolveStatsHostname *inHostname, const Response *inResp, uint8_t inServerID) +{ + mStatus err; + + if ((inResp->type == kResponseType_IPv4Addr) || (inResp->type == kResponseType_NegA)) + { + ResolveStatsIPv4AddrSet ** p; + ResolveStatsIPv4AddrSet * addrV4; + int i; + IPv4AddrCounter * counter; + + for (p = &inHostname->addrV4List; (addrV4 = *p) != NULL; p = &addrV4->next) + { + for (i = 0; i < (int)countof(addrV4->counters); ++i) + { + counter = &addrV4->counters[i]; + if (counter->count == 0) break; + if (counter->serverID != inServerID) continue; + if (inResp->type == kResponseType_NegA) + { + if (counter->isNegative) break; + } + else + { + if (memcmp(counter->addrBytes, inResp->data, 4) == 0) break; + } + } + if (i < (int)countof(addrV4->counters)) break; + } + if (!addrV4) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsIPv4AddrSetCreate(&addrV4); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + *p = addrV4; + counter = &addrV4->counters[0]; + } + if (counter->count == 0) + { + counter->serverID = inServerID; + if (inResp->type == kResponseType_NegA) + { + counter->isNegative = 1; + } + else + { + counter->isNegative = 0; + memcpy(counter->addrBytes, inResp->data, 4); + } + } + increment_saturate(counter->count, UINT16_MAX); + err = mStatus_NoError; + } + else if (inResp->type == kResponseType_IPv6Addr) + { + ResolveStatsIPv6Addr ** p; + ResolveStatsIPv6Addr * addrV6; + + for (p = &inHostname->addrV6List; (addrV6 = *p) != NULL; p = &addrV6->next) + { + if ((addrV6->serverID == inServerID) && (memcmp(addrV6->addrBytes, inResp->data, 16) == 0)) break; + } + if (!addrV6) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsIPv6AddressCreate(inServerID, inResp->data, &addrV6); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + *p = addrV6; + } + increment_saturate(addrV6->count, UINT16_MAX); + err = mStatus_NoError; + } + else if (inResp->type == kResponseType_NegAAAA) + { + ResolveStatsNegAAAASet ** p; + ResolveStatsNegAAAASet * negV6; + int i; + NegAAAACounter * counter; + + for (p = &inHostname->negV6List; (negV6 = *p) != NULL; p = &negV6->next) + { + for (i = 0; i < (int)countof(negV6->counters); ++i) + { + counter = &negV6->counters[i]; + if ((counter->count == 0) || (counter->serverID == inServerID)) break; + } + if (i < (int)countof(negV6->counters)) break; + } + if (!negV6) + { + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsNegAAAASetCreate(&negV6); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + *p = negV6; + counter = &negV6->counters[0]; + } + if (counter->count == 0) counter->serverID = inServerID; + increment_saturate(counter->count, UINT16_MAX); + err = mStatus_NoError; + } + else + { + err = mStatus_Invalid; + } + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsHostnameCreateAWDVersion +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsHostnameCreateAWDVersion(const ResolveStatsHostname *inHostname, AWDMDNSResponderResolveStatsHostname **outHostname) +{ + mStatus err; + AWDMDNSResponderResolveStatsHostname * hostname; + NSString * name; + char nameBuf[MAX_ESCAPED_DOMAIN_NAME]; + const char * ptr; + ResolveStatsIPv4AddrSet * addrV4; + ResolveStatsIPv6Addr * addrV6; + ResolveStatsNegAAAASet * negV6; + AWDMDNSResponderResolveStatsResult * result = nil; + int i; + + hostname = [[AWDMDNSResponderResolveStatsHostnameSoft alloc] init]; + require_action_quiet(hostname, exit, err = mStatus_UnknownErr); + + ptr = ConvertDomainNameToCString((domainname *)inHostname->name, nameBuf); + require_action_quiet(ptr, exit, err = mStatus_UnknownErr); + + name = [[NSString alloc] initWithUTF8String:nameBuf]; + require_action_quiet(name, exit, err = mStatus_UnknownErr); + + hostname.name = name; + [name release]; + name = nil; + + for (addrV4 = inHostname->addrV4List; addrV4; addrV4 = addrV4->next) + { + for (i = 0; i < (int)countof(addrV4->counters); ++i) + { + const IPv4AddrCounter * counter; + NSData * addrBytes; + + counter = &addrV4->counters[i]; + if (counter->count == 0) break; + + result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init]; + require_action_quiet(result, exit, err = mStatus_UnknownErr); + + if (counter->isNegative) + { + result.type = AWDMDNSResponderResolveStatsResult_ResultType_NegA; + } + else + { + addrBytes = [[NSData alloc] initWithBytes:counter->addrBytes length:4]; + require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr); + + result.type = AWDMDNSResponderResolveStatsResult_ResultType_IPv4Addr; + result.data = addrBytes; + [addrBytes release]; + } + result.count = counter->count; + result.serverID = counter->serverID; + + [hostname addResult:result]; + [result release]; + result = nil; + } + } + + for (addrV6 = inHostname->addrV6List; addrV6; addrV6 = addrV6->next) + { + NSData * addrBytes; + + result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init]; + require_action_quiet(result, exit, err = mStatus_UnknownErr); + + addrBytes = [[NSData alloc] initWithBytes:addrV6->addrBytes length:16]; + require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr); + + result.type = AWDMDNSResponderResolveStatsResult_ResultType_IPv6Addr; + result.count = addrV6->count; + result.serverID = addrV6->serverID; + result.data = addrBytes; + + [addrBytes release]; + + [hostname addResult:result]; + [result release]; + result = nil; + } + + for (negV6 = inHostname->negV6List; negV6; negV6 = negV6->next) + { + for (i = 0; i < (int)countof(negV6->counters); ++i) + { + const NegAAAACounter * counter; + + counter = &negV6->counters[i]; + if (counter->count == 0) break; + + result = [[AWDMDNSResponderResolveStatsResultSoft alloc] init]; + require_action_quiet(result, exit, err = mStatus_UnknownErr); + + result.type = AWDMDNSResponderResolveStatsResult_ResultType_NegAAAA; + result.count = counter->count; + result.serverID = counter->serverID; + + [hostname addResult:result]; + [result release]; + result = nil; + } + } + + *outHostname = hostname; + hostname = nil; + err = mStatus_NoError; + +exit: + [result release]; + [hostname release]; + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDNSServerCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDNSServerCreate(const mDNSAddr *inAddr, mDNSBool inForCell, ResolveStatsDNSServer **outServer) +{ + mStatus err; + ResolveStatsDNSServer * obj; + size_t addrLen; + + require_action_quiet((inAddr->type == mDNSAddrType_IPv4) || (inAddr->type == mDNSAddrType_IPv6), exit, err = mStatus_Invalid); + + addrLen = (inAddr->type == mDNSAddrType_IPv4) ? 4 : 16; + obj = (ResolveStatsDNSServer *)calloc(1, sizeof(*obj) - 1 + addrLen); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + obj->isForCell = inForCell; + if (inAddr->type == mDNSAddrType_IPv4) + { + obj->isAddrV6 = mDNSfalse; + memcpy(obj->addrBytes, inAddr->ip.v4.b, addrLen); + } + else + { + obj->isAddrV6 = mDNStrue; + memcpy(obj->addrBytes, inAddr->ip.v6.b, addrLen); + } + + *outServer = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsDNSServerFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsDNSServerFree(ResolveStatsDNSServer *inServer) +{ + free(inServer); +} + +//=========================================================================================================================== +// ResolveStatsDNSServerCreateAWDVersion +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsDNSServerCreateAWDVersion(const ResolveStatsDNSServer *inServer, AWDMDNSResponderResolveStatsDNSServer **outServer) +{ + mStatus err; + AWDMDNSResponderResolveStatsDNSServer * server; + NSData * addrBytes = nil; + + server = [[AWDMDNSResponderResolveStatsDNSServerSoft alloc] init]; + require_action_quiet(server, exit, err = mStatus_UnknownErr); + + addrBytes = [[NSData alloc] initWithBytes:inServer->addrBytes length:(inServer->isAddrV6 ? 16 : 4)]; + require_action_quiet(addrBytes, exit, err = mStatus_UnknownErr); + + server.serverID = inServer->id; + server.address = addrBytes; + if (inServer->isForCell) + { + server.networkType = AWDMDNSResponderResolveStatsDNSServer_NetworkType_Cellular; + } + else + { + server.networkType = AWDMDNSResponderResolveStatsDNSServer_NetworkType_NonCellular; + } + + *outServer = server; + server = nil; + err = mStatus_NoError; + +exit: + [addrBytes release]; + [server release]; + return (err); +} + +//=========================================================================================================================== +// ResolveStatsIPv4AddrSetCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsIPv4AddrSetCreate(ResolveStatsIPv4AddrSet **outSet) +{ + mStatus err; + ResolveStatsIPv4AddrSet * obj; + + obj = (ResolveStatsIPv4AddrSet *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + *outSet = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsIPv4AddrSetFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsIPv4AddrSetFree(ResolveStatsIPv4AddrSet *inSet) +{ + free(inSet); +} + +//=========================================================================================================================== +// ResolveStatsIPv6AddressCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsIPv6AddressCreate(uint8_t inServerID, const uint8_t inAddrBytes[16], ResolveStatsIPv6Addr **outAddr) +{ + mStatus err; + ResolveStatsIPv6Addr * obj; + + obj = (ResolveStatsIPv6Addr *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + obj->serverID = inServerID; + memcpy(obj->addrBytes, inAddrBytes, 16); + + *outAddr = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsIPv6AddressFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsIPv6AddressFree(ResolveStatsIPv6Addr *inAddr) +{ + free(inAddr); +} + +//=========================================================================================================================== +// ResolveStatsNegAAAASetCreate +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsNegAAAASetCreate(ResolveStatsNegAAAASet **outSet) +{ + mStatus err; + ResolveStatsNegAAAASet * obj; + + obj = (ResolveStatsNegAAAASet *)calloc(1, sizeof(*obj)); + require_action_quiet(obj, exit, err = mStatus_NoMemoryErr); + + *outSet = obj; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// ResolveStatsNegAAAASetFree +//=========================================================================================================================== + +mDNSlocal void ResolveStatsNegAAAASetFree(ResolveStatsNegAAAASet *inSet) +{ + free(inSet); +} + +//=========================================================================================================================== +// ResolveStatsGetServerID +//=========================================================================================================================== + +mDNSlocal mStatus ResolveStatsGetServerID(const mDNSAddr *inServerAddr, mDNSBool inForCell, uint8_t *outServerID) +{ + mStatus err; + ResolveStatsDNSServer ** p; + ResolveStatsDNSServer * server; + + require_action_quiet((inServerAddr->type == mDNSAddrType_IPv4) || (inServerAddr->type == mDNSAddrType_IPv6), exit, err = mStatus_Invalid); + + for (p = &gResolveStatsServerList; (server = *p) != NULL; p = &server->next) + { + if ((inForCell && server->isForCell) || (!inForCell && !server->isForCell)) + { + if (inServerAddr->type == mDNSAddrType_IPv4) + { + if (!server->isAddrV6 && (memcmp(server->addrBytes, inServerAddr->ip.v4.b, 4) == 0)) break; + } + else + { + if (server->isAddrV6 && (memcmp(server->addrBytes, inServerAddr->ip.v6.b, 16) == 0)) break; + } + } + } + + if (!server) + { + require_action_quiet(gResolveStatsNextServerID <= UINT8_MAX, exit, err = mStatus_Refused); + require_action_quiet(gResolveStatsObjCount < kResolveStatsMaxObjCount, exit, err = mStatus_Refused); + err = ResolveStatsDNSServerCreate(inServerAddr, inForCell, &server); + require_noerr_quiet(err, exit); + gResolveStatsObjCount++; + + server->id = gResolveStatsNextServerID++; + server->next = gResolveStatsServerList; + gResolveStatsServerList = server; + } + else if (gResolveStatsServerList != server) + { + *p = server->next; + server->next = gResolveStatsServerList; + gResolveStatsServerList = server; + } + + *outServerID = server->id; + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// CreateDomainStatsList +//=========================================================================================================================== + +mDNSlocal mStatus CreateDomainStatsList(DNSDomainStats **outList) +{ + mStatus err; + int i; + DNSDomainStats * stats; + DNSDomainStats ** p; + DNSDomainStats * list = NULL; + + p = &list; + for (i = 0; i < (int)countof(kQueryStatsDomains); ++i) + { + err = DNSDomainStatsCreate(&kQueryStatsDomains[i], &stats); + require_noerr_quiet(err, exit); + + *p = stats; + p = &stats->next; + } + + *outList = list; + list = NULL; + +exit: + DNSDomainStatsFreeList(list); + return (err); +} + +//=========================================================================================================================== +// CreateResolveStatsList +//=========================================================================================================================== + +mDNSlocal mStatus CreateResolveStatsList(ResolveStatsDomain **outList) +{ + mStatus err; + int i; + ResolveStatsDomain * domain; + ResolveStatsDomain ** p; + ResolveStatsDomain * list = NULL; + + p = &list; + for (i = 0; i < (int)countof(kResolveStatsDomains); ++i) + { + err = ResolveStatsDomainCreate(&kResolveStatsDomains[i], &domain); + require_noerr_quiet(err, exit); + + *p = domain; + p = &domain->next; + } + + *outList = list; + list = NULL; + +exit: + FreeResolveStatsList(list); + return (err); +} + +//=========================================================================================================================== +// FreeResolveStatsList +//=========================================================================================================================== + +mDNSlocal void FreeResolveStatsList(ResolveStatsDomain *inList) +{ + ResolveStatsDomain * domain; + + while ((domain = inList) != NULL) + { + inList = domain->next; + ResolveStatsDomainFree(domain); + } +} + +//=========================================================================================================================== +// FreeResolveStatsServerList +//=========================================================================================================================== + +mDNSlocal void FreeResolveStatsServerList(ResolveStatsDNSServer *inList) +{ + ResolveStatsDNSServer * server; + + while ((server = inList) != NULL) + { + inList = server->next; + ResolveStatsDNSServerFree(server); + } +} + +//=========================================================================================================================== +// SubmitAWDMetric +//=========================================================================================================================== + +mDNSlocal mStatus SubmitAWDMetric(UInt32 inMetricID) +{ + mStatus err; + + switch (inMetricID) + { + case AWDMetricId_MDNSResponder_DNSStatistics: + err = SubmitAWDMetricQueryStats(); + break; + + case AWDMetricId_MDNSResponder_ResolveStats: + err = SubmitAWDMetricResolveStats(); + break; + + case AWDMetricId_MDNSResponder_ServicesStats: + [AWDMetricManagerSoft postMetricWithId:AWDMetricId_MDNSResponder_ServicesStats unsignedIntegerValue:max_num_regservices]; + KQueueLock(&mDNSStorage); + // reset the no of max services since we want to collect the max no of services registered per AWD submission period + max_num_regservices = curr_num_regservices; + KQueueUnlock(&mDNSStorage, "SubmitAWDSimpleMetricServiceStats"); + err = mStatus_NoError; + break; + + default: + err = mStatus_UnsupportedErr; + break; + } + + if (err) LogMsg("SubmitAWDMetric for metric ID 0x%08X failed with error %d", inMetricID, err); + return (err); +} + +//=========================================================================================================================== +// SubmitAWDMetricQueryStats +//=========================================================================================================================== + +mDNSlocal mStatus SubmitAWDMetricQueryStats(void) +{ + mStatus err; + BOOL success; + DNSDomainStats * stats; + DNSDomainStats * newDomainStatsList; + DNSDomainStats * domainStatsList = NULL; + AWDMetricContainer * container = nil; + AWDMDNSResponderDNSStatistics * metric = nil; + + err = CreateDomainStatsList(&newDomainStatsList); + require_noerr_quiet(err, exit); + + KQueueLock(&mDNSStorage); + domainStatsList = gDomainStatsList; + gDomainStatsList = newDomainStatsList; + KQueueUnlock(&mDNSStorage, "SubmitAWDMetricQueryStats"); + + container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_DNSStatistics]; + require_action_quiet(container, exit, err = mStatus_UnknownErr); + + metric = [[AWDMDNSResponderDNSStatisticsSoft alloc] init]; + require_action_quiet(metric, exit, err = mStatus_UnknownErr); + + while ((stats = domainStatsList) != NULL) + { + if (stats->nonCellular) + { + err = AddAWDDNSDomainStats(metric, stats->nonCellular, stats->domain->cstr, mDNSfalse); + require_noerr_quiet(err, exit); + } + if (stats->cellular) + { + err = AddAWDDNSDomainStats(metric, stats->cellular, stats->domain->cstr, mDNStrue); + require_noerr_quiet(err, exit); + } + domainStatsList = stats->next; + DNSDomainStatsFree(stats); + } + + container.metric = metric; + success = [gAWDServerConnection submitMetric:container]; + LogMsg("SubmitAWDMetricQueryStats: metric submission %s.", success ? "succeeded" : "failed" ); + err = success ? mStatus_NoError : mStatus_UnknownErr; + +exit: + [metric release]; + [container release]; + DNSDomainStatsFreeList(domainStatsList); + return (err); +} + +//=========================================================================================================================== +// SubmitAWDMetricResolveStats +//=========================================================================================================================== + +mDNSlocal mStatus SubmitAWDMetricResolveStats(void) +{ + mStatus err; + ResolveStatsDomain * newResolveStatsList; + ResolveStatsDomain * domainList = NULL; + ResolveStatsDNSServer * serverList = NULL; + AWDMetricContainer * container = nil; + AWDMDNSResponderResolveStats * metric = nil; + ResolveStatsDNSServer * server; + ResolveStatsDomain * domain; + BOOL success; + + err = CreateResolveStatsList(&newResolveStatsList); + require_noerr_quiet(err, exit); + + KQueueLock(&mDNSStorage); + domainList = gResolveStatsList; + serverList = gResolveStatsServerList; + gResolveStatsList = newResolveStatsList; + gResolveStatsServerList = NULL; + gResolveStatsNextServerID = 0; + gResolveStatsObjCount = 0; + KQueueUnlock(&mDNSStorage, "SubmitAWDMetricResolveStats"); + + container = [gAWDServerConnection newMetricContainerWithIdentifier:AWDMetricId_MDNSResponder_ResolveStats]; + require_action_quiet(container, exit, err = mStatus_UnknownErr); + + metric = [[AWDMDNSResponderResolveStatsSoft alloc] init]; + require_action_quiet(metric, exit, err = mStatus_UnknownErr); + + while ((server = serverList) != NULL) + { + AWDMDNSResponderResolveStatsDNSServer * awdServer; + + serverList = server->next; + err = ResolveStatsDNSServerCreateAWDVersion(server, &awdServer); + ResolveStatsDNSServerFree(server); + require_noerr_quiet(err, exit); + + [metric addServer:awdServer]; + [awdServer release]; + } + + while ((domain = domainList) != NULL) + { + AWDMDNSResponderResolveStatsDomain * awdDomain; + + domainList = domain->next; + err = ResolveStatsDomainCreateAWDVersion(domain, &awdDomain); + ResolveStatsDomainFree(domain); + require_noerr_quiet(err, exit); + + [metric addDomain:awdDomain]; + [awdDomain release]; + } + + container.metric = metric; + success = [gAWDServerConnection submitMetric:container]; + LogMsg("SubmitAWDMetricResolveStats: metric submission %s.", success ? "succeeded" : "failed" ); + err = success ? mStatus_NoError : mStatus_UnknownErr; + +exit: + [metric release]; + [container release]; + FreeResolveStatsList(domainList); + FreeResolveStatsServerList(serverList); + return (err); +} + +//=========================================================================================================================== +// CreateAWDDNSDomainStats +//=========================================================================================================================== + +mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, mDNSBool inForCell, AWDDNSDomainStats_RecordType inType, AWDDNSDomainStats **outStats) +{ + mStatus err; + AWDDNSDomainStats * awdStats = nil; + NSString * domain = nil; + uint32_t sendCountBins[kQueryStatsSendCountBinCount]; + uint32_t latencyBins[kQueryStatsLatencyBinCount]; + int i; + unsigned int totalAnswered; + unsigned int totalNegAnswered; + unsigned int totalUnanswered; + + awdStats = [[AWDDNSDomainStatsSoft alloc] init]; + require_action_quiet(awdStats, exit, err = mStatus_UnknownErr); + + domain = [[NSString alloc] initWithUTF8String:inDomain]; + require_action_quiet(domain, exit, err = mStatus_UnknownErr); + + awdStats.domain = domain; + awdStats.networkType = inForCell ? AWDDNSDomainStats_NetworkType_Cellular : AWDDNSDomainStats_NetworkType_NonCellular; + awdStats.recordType = inType; + + totalAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + sendCountBins[i] = inHist->answeredQuerySendCountBins[i]; + totalAnswered += inHist->answeredQuerySendCountBins[i]; + } + [awdStats setAnsweredQuerySendCounts:sendCountBins count:kQueryStatsSendCountBinCount]; + + totalNegAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + sendCountBins[i] = inHist->negAnsweredQuerySendCountBins[i]; + totalNegAnswered += inHist->negAnsweredQuerySendCountBins[i]; + } + [awdStats setNegAnsweredQuerySendCounts:sendCountBins count:kQueryStatsSendCountBinCount]; + + totalUnanswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + sendCountBins[i] = inHist->unansweredQuerySendCountBins[i]; + totalUnanswered += inHist->unansweredQuerySendCountBins[i]; + } + [awdStats setUnansweredQuerySendCounts:sendCountBins count:kQueryStatsSendCountBinCount]; + + if (totalAnswered > inHist->answeredQuerySendCountBins[0]) + { + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + latencyBins[i] = inHist->responseLatencyBins[i]; + } + [awdStats setResponseLatencyMs:latencyBins count:kQueryStatsLatencyBinCount]; + } + + if (totalNegAnswered > inHist->negAnsweredQuerySendCountBins[0]) + { + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + latencyBins[i] = inHist->negResponseLatencyBins[i]; + } + [awdStats setNegResponseLatencyMs:latencyBins count:kQueryStatsLatencyBinCount]; + } + + if (totalUnanswered > 0) + { + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + latencyBins[i] = inHist->unansweredQueryDurationBins[i]; + } + [awdStats setUnansweredQueryDurationMs:latencyBins count:kQueryStatsLatencyBinCount]; + } + + *outStats = awdStats; + awdStats = nil; + err = mStatus_NoError; + +exit: + [domain release]; + [awdStats release]; + return (err); +} + +//=========================================================================================================================== +// AddAWDDNSDomainStats +//=========================================================================================================================== + +mDNSlocal mStatus AddAWDDNSDomainStats(AWDMDNSResponderDNSStatistics *inMetric, DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell) +{ + mStatus err; + AWDDNSDomainStats * awdStats; + + if (inSet->histA) + { + err = CreateAWDDNSDomainStats(inSet->histA, inDomain, inForCell, AWDDNSDomainStats_RecordType_A, &awdStats); + require_noerr_quiet(err, exit); + + [inMetric addStats:awdStats]; + [awdStats release]; + } + if (inSet->histAAAA) + { + err = CreateAWDDNSDomainStats(inSet->histAAAA, inDomain, inForCell, AWDDNSDomainStats_RecordType_AAAA, &awdStats); + require_noerr_quiet(err, exit); + + [inMetric addStats:awdStats]; + [awdStats release]; + } + err = mStatus_NoError; + +exit: + return (err); +} + +//=========================================================================================================================== +// LogDNSHistSet +//=========================================================================================================================== + +mDNSlocal void LogDNSHistSet(const DNSHistSet *inSet, const char *inDomain, mDNSBool inForCell) +{ + if (inSet->histA) LogDNSHist(inSet->histA, inDomain, inForCell, "A"); + if (inSet->histAAAA) LogDNSHist(inSet->histAAAA, inDomain, inForCell, "AAAA"); +} + +//=========================================================================================================================== +// LogDNSHist +//=========================================================================================================================== + +#define Percent(N, D) ((N) * 100) / (D), (((N) * 10000) / (D)) % 100 +#define PercentFmt "%3u.%02u" +#define LogStat(LABEL, COUNT, ACCUMULATOR, TOTAL) \ + LogMsgNoIdent("%s %5u " PercentFmt " " PercentFmt, (LABEL), (COUNT), Percent(COUNT, TOTAL), Percent(ACCUMULATOR, TOTAL)) + +mDNSlocal void LogDNSHist(const DNSHist *inHist, const char *inDomain, mDNSBool inForCell, const char *inType) +{ + unsigned int totalAnswered; + unsigned int totalNegAnswered; + unsigned int totalUnanswered; + int i; + + totalAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + totalAnswered += inHist->answeredQuerySendCountBins[i]; + } + + totalNegAnswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + totalNegAnswered += inHist->negAnsweredQuerySendCountBins[i]; + } + + totalUnanswered = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + totalUnanswered += inHist->unansweredQuerySendCountBins[i]; + } + + LogMsgNoIdent("Domain: %s (%s, %s)", inDomain, inForCell ? "C" : "NC", inType); + LogMsgNoIdent("Answered questions %4u", totalAnswered); + LogMsgNoIdent("Negatively answered questions %4u", totalNegAnswered); + LogMsgNoIdent("Unanswered questions %4u", totalUnanswered); + LogMsgNoIdent("-- Query send counts ---------"); + LogDNSHistSendCounts(inHist->answeredQuerySendCountBins); + LogMsgNoIdent("-- Query send counts (NAQs) --"); + LogDNSHistSendCounts(inHist->negAnsweredQuerySendCountBins); + + if (totalAnswered > inHist->answeredQuerySendCountBins[0]) + { + LogMsgNoIdent("--- Response times -----------"); + LogDNSHistLatencies(inHist->responseLatencyBins); + } + + if (totalNegAnswered > inHist->negAnsweredQuerySendCountBins[0]) + { + LogMsgNoIdent("--- Response times (NAQs) ----"); + LogDNSHistLatencies(inHist->negResponseLatencyBins); + } + + if (totalUnanswered > 0) + { + LogMsgNoIdent("--- Unanswered query times ---"); + LogDNSHistLatencies(inHist->unansweredQueryDurationBins); + } +} + +//=========================================================================================================================== +// LogDNSHistSendCounts +//=========================================================================================================================== + +mDNSlocal void LogDNSHistSendCounts(const uint16_t inSendCountBins[kQueryStatsSendCountBinCount]) +{ + uint32_t total; + char label[16]; + int i; + + total = 0; + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + total += inSendCountBins[i]; + } + + if (total > 0) + { + uint32_t accumulator = 0; + + for (i = 0; i < kQueryStatsSendCountBinCount; ++i) + { + accumulator += inSendCountBins[i]; + if (i < (kQueryStatsSendCountBinCount - 1)) + { + snprintf(label, sizeof(label), "%2d ", i); + } + else + { + snprintf(label, sizeof(label), "%2d+", i); + } + LogStat(label, inSendCountBins[i], accumulator, total); + if (accumulator == total) break; + } + } + else + { + LogMsgNoIdent("No data."); + } +} + +//=========================================================================================================================== +// LogDNSHistLatencies +//=========================================================================================================================== + +mDNSlocal void LogDNSHistLatencies(const uint16_t inLatencyBins[kQueryStatsLatencyBinCount]) +{ + uint32_t total; + int i; + char label[16]; + + total = 0; + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + total += inLatencyBins[i]; + } + + if (total > 0) + { + uint32_t accumulator = 0; + + for (i = 0; i < kQueryStatsLatencyBinCount; ++i) + { + accumulator += inLatencyBins[i]; + if (i < (int)countof(kResponseLatencyMsLimits)) + { + snprintf(label, sizeof(label), "< %5u ms", kResponseLatencyMsLimits[i]); + } + else + { + snprintf(label, sizeof(label), "< ∞ ms"); + } + LogStat(label, inLatencyBins[i], accumulator, total); + if (accumulator == total) break; + } + } + else + { + LogMsgNoIdent("No data."); + } +} + +//=========================================================================================================================== +// ValidateDNSStatsDomains +//=========================================================================================================================== + +#if (METRICS_VALIDATE_DNS_STATS_DOMAINS) +#warning "Do not include ValidateDNSStatsDomains() in customer release!" +mDNSlocal void ValidateDNSStatsDomains(void) +{ + int i; + const Domain * domain; + mDNSu8 * ptr; + domainname domainNameExpected; + int labelCountExpected; + mDNSBool domainNamesEqual; + mDNSBool failed = mDNSfalse; + + for (i = 0; i < countof(kQueryStatsDomains); ++i) + { + domain = &kQueryStatsDomains[i]; + + if (strcmp(domain->cstr, ".") == 0) + { + domainNameExpected.c[0] = 0; + } + else + { + ptr = MakeDomainNameFromDNSNameString(&domainNameExpected, domain->cstr); + if (!ptr) + { + LogMsg("ValidateDNSStatsDomains: Failed to make domain name for \"%s\".", domain->cstr); + failed = mDNStrue; + goto exit; + } + } + + domainNamesEqual = SameDomainName(domain->name, &domainNameExpected); + labelCountExpected = CountLabels(&domainNameExpected); + if (domainNamesEqual && (domain->labelCount == labelCountExpected)) + { + LogMsg("ValidateDNSStatsDomains: \"%s\" passed.", domain->cstr); + } + else + { + if (!domainNamesEqual) + { + LogMsg("ValidateDNSStatsDomains: \"%s\" failed: incorrect domain name.", domain->cstr); + } + if (domain->labelCount != labelCountExpected) + { + LogMsg("ValidateDNSStatsDomains: \"%s\" failed: incorrect label count. Actual %d, expected %d.", + domain->cstr, domain->labelCount, labelCountExpected); + } + failed = mDNStrue; + } + } + +exit: + if (failed) abort(); +} +#endif +#endif // TARGET_OS_EMBEDDED diff --git a/mDNSMacOSX/P2PPacketFilter.c b/mDNSMacOSX/P2PPacketFilter.c index 1ab8a02..59a5cf4 100644 --- a/mDNSMacOSX/P2PPacketFilter.c +++ b/mDNSMacOSX/P2PPacketFilter.c @@ -1,4 +1,4 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2011 Apple Inc. All rights reserved. * @@ -228,7 +228,9 @@ static void initPortRule( struct pfioc_rule * pr, int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray ) { int result; - u_int32_t i, ticket, poolTicket; + u_int32_t i; + u_int32_t ticket = 0; + u_int32_t poolTicket = 0; int devFD = -1; char * anchorPath = MDNS_ANCHOR_PATH; @@ -250,7 +252,8 @@ int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count require( result == 0, exit ); // open inbound port for each service - for (i = 0; i < count; i++) { + for (i = 0; i < count; i++) + { initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, portArray[i], protocolArray[i] ); result = addRule( devFD, &pr ); require( result == 0, exit ); @@ -277,7 +280,7 @@ int P2PPacketFilterClearBonjourRules() { int result; int pfDev = -1; - u_int32_t ticket; + u_int32_t ticket = 0; char * anchorPath = MDNS_ANCHOR_PATH; result = openPFDevice( &pfDev ); diff --git a/mDNSMacOSX/P2PPacketFilter.h b/mDNSMacOSX/P2PPacketFilter.h index 9c19657..23e5410 100644 --- a/mDNSMacOSX/P2PPacketFilter.h +++ b/mDNSMacOSX/P2PPacketFilter.h @@ -1,4 +1,4 @@ -/* +/* -*- Mode: C; tab-width: 4 -*- * * Copyright (c) 2011 Apple Inc. All rights reserved. * @@ -20,7 +20,8 @@ #include "helpermsg-types.h" -enum { +enum +{ PF_SET_RULES, PF_CLEAR_RULES }; diff --git a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c index a2ab546..43da502 100644 --- a/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c +++ b/mDNSMacOSX/PreferencePane/ConfigurationAuthority.c @@ -112,7 +112,7 @@ OSStatus InitConfigAuthority(void) CFSTR("Describes operation that requires user authorization")); require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); dict = CreateRightsDict(rightInfo); - require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr; CFRelease(rightInfo)); err = AuthorizationRightSet(gAuthRef, UPDATE_SC_RIGHT, dict, (CFStringRef) NULL, (CFBundleRef) NULL, (CFStringRef) NULL); @@ -128,7 +128,7 @@ OSStatus InitConfigAuthority(void) CFSTR("Describes operation that requires user authorization")); require_action( rightInfo != NULL, GetStrFailed, err=coreFoundationUnknownErr;); dict = CreateRightsDict( rightInfo); - require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr;); + require_action( dict != NULL, GetStrFailed, err=coreFoundationUnknownErr; CFRelease(rightInfo)); err = AuthorizationRightSet(gAuthRef, EDIT_SYS_KEYCHAIN_RIGHT, dict, (CFStringRef) NULL, (CFBundleRef) NULL, (CFStringRef) NULL); diff --git a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m b/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m index 063bc72..ba5f64b 100644 --- a/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m +++ b/mDNSMacOSX/PreferencePane/DNSServiceDiscoveryPref.m @@ -3,9 +3,9 @@ Abstract: System Preference Pane for Dynamic DNS and Wide-Area DNS Service Discovery - Copyright: (c) Copyright 2005-2011 Apple Computer, Inc. All rights reserved. + Copyright: (c) Copyright 2005-2011 Apple Inc. All rights reserved. - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple 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, @@ -19,7 +19,7 @@ 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 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 @@ -96,8 +96,7 @@ static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, NSMutableArray * defaultBrowseDomainsArray = nil; NSComboBox * domainComboBox; NSString * domainString; - NSString * currentDomain = nil; - char decodedDomainString[kDNSServiceMaxDomainName] = "\0"; + char decodedDomainString[kDNSServiceMaxDomainName] = "\0"; char nextLabel[256] = "\0"; char * buffer = (char *)replyDomain; @@ -115,7 +114,6 @@ static void ServiceDomainEnumReply( DNSServiceRef sdRef, DNSServiceFlags flags, if (enumType & kDNSServiceFlagsRegistrationDomains) { domainArray = [me registrationDataSource]; domainComboBox = [me regDomainsComboBox]; - currentDomain = [me currentRegDomain]; } else { domainArray = [me browseDataSource]; domainComboBox = [me browseDomainsComboBox]; @@ -261,6 +259,7 @@ MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) assert(rls != NULL); CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopCommonModes); + CFRelease(rls); CFRelease(keys); CFRelease(store); @@ -334,8 +333,8 @@ MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) currentHostName = [[NSString alloc] initWithString:@""]; } - [origDict release]; - CFRelease(store); + if (origDict) CFRelease((CFDictionaryRef)origDict); + if (store) CFRelease(store); } @@ -394,6 +393,7 @@ MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) [browseDomainsArray replaceObjectAtIndex:[tableView clickedRow] withObject:browseDomainDict]; [tableView reloadData]; [self updateApplyButtonState]; + [browseDomainDict release]; } @@ -681,12 +681,8 @@ MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) { 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])) || + if ((currentHostName && ([hostNameString compare:currentHostName] != NSOrderedSame)) || + (currentRegDomain && ([regDomainString compare:currentRegDomain] != NSOrderedSame) && ([wideAreaCheckBox state])) || (currentHostName == nil && ([hostNameString length]) > 0) || (currentRegDomain == nil && ([regDomainString length]) > 0) || (currentWideAreaState != [wideAreaCheckBox state]) || @@ -946,7 +942,7 @@ MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) } CFRelease(itemRef); } - return keyName; + return [keyName autorelease]; } @@ -1000,7 +996,6 @@ MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) -(void)savePreferences { NSString *hostNameString = [hostName stringValue]; - NSString *browseDomainString = [browseDomainsComboBox stringValue]; NSString *regDomainString = [regDomainsComboBox stringValue]; NSString *tempHostNameSharedSecretName = hostNameSharedSecretName; NSString *tempRegSharedSecretName = regSharedSecretName; @@ -1010,7 +1005,6 @@ MyDNSServiceAddServiceToRunLoop(MyDNSServiceState * query) OSStatus err = noErr; hostNameString = [self trimCharactersFromDomain:hostNameString]; - browseDomainString = [self trimCharactersFromDomain:browseDomainString]; regDomainString = [self trimCharactersFromDomain:regDomainString]; tempHostNameSharedSecretName = [self trimCharactersFromDomain:tempHostNameSharedSecretName]; tempRegSharedSecretName = [self trimCharactersFromDomain:tempRegSharedSecretName]; diff --git a/mDNSMacOSX/PreferencePane/English.lproj/InfoPlist.strings b/mDNSMacOSX/PreferencePane/English.lproj/InfoPlist.strings index e2dfa991dedc0e00bf59c0152f4d3104dd7b94d4..71d52de889bbed11a4feae1d751ce9784779707c 100644 GIT binary patch literal 219 zcmaKly$*sf6oq%5;)Y3yLLp%?3_{{hVi@EJDqOHydYiVG;M0qP;^cU8&i9=tfD_eR ziYGSkKw||d3)I3Kd>7cD13gi>JVb8h{IV!;#Y{)z17vk4+a`=m`ME9WY4po7(z!_?!fl|;u zxIlCGW2{k+TO? #include #include -#include +#include #include #include #include +extern char **environ; Boolean gToolApproved = false; -static pid_t execTool(const char *args[]) -// fork/exec and return new pid +static pid_t execTool(const char *args[]) { pid_t child; - child = vfork(); - if (child == 0) + int err = posix_spawn(&child, args[0], NULL, NULL, (char *const *)args, environ); + if (err) { - execv(args[0], (char *const *)args); - printf("exec of %s failed; errno = %d\n", args[0], errno); - _exit(-1); // exec failed + printf("exec of %s failed; err = %d\n", args[0], err); + return -1; } else return child; @@ -76,7 +75,7 @@ OSStatus EnsureToolInstalled(void) { CFURLRef bundleURL; pid_t toolPID; - int status; + int status = 0; OSStatus err = noErr; const char *args[] = { kToolPath, "0", "V", NULL }; char toolSourcePath[PATH_MAX] = {}; @@ -99,8 +98,10 @@ OSStatus EnsureToolInstalled(void) 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); + CFRelease(bundleURL); + + if (strlcat(toolSourcePath, "/Contents/Resources/" kToolName, sizeof toolSourcePath ) >= sizeof toolSourcePath ) return(-1); if (strlcat(toolInstallerPath, "/Contents/Resources/" kToolInstaller, sizeof toolInstallerPath) >= sizeof toolInstallerPath) return(-1); } else @@ -119,11 +120,14 @@ OSStatus EnsureToolInstalled(void) { char *installerargs[] = { toolSourcePath, NULL }; err = AuthorizationExecuteWithPrivileges(authRef, toolInstallerPath, 0, installerargs, (FILE**) NULL); - if (err == noErr) { + if (err == noErr) + { int pid = wait(&status); - if (pid > 0 && WIFEXITED(status)) { + if (pid > 0 && WIFEXITED(status)) + { err = WEXITSTATUS(status); - if (err == noErr) { + if (err == noErr) + { gToolApproved = true; } } else { @@ -162,7 +166,7 @@ static OSStatus ExecWithCmdAndParam(const char *subCmd, CFDataRef paramData) } commFD = fileno(tmpfile()); - sprintf(fileNum, "%d", commFD); + snprintf(fileNum, sizeof(fileNum), "%d", commFD); args[1] = fileNum; args[3] = subCmd; @@ -181,8 +185,9 @@ static OSStatus ExecWithCmdAndParam(const char *subCmd, CFDataRef paramData) write(commFD, buff, len); child = execTool(args); - if (child > 0) { - int status; + if (child > 0) + { + int status = 0; waitpid(child, &status, 0); if (WIFEXITED(status)) err = WEXITSTATUS(status); diff --git a/mDNSMacOSX/PreferencePane/ddnswriteconfig.m b/mDNSMacOSX/PreferencePane/ddnswriteconfig.m index 437879b..8e64fc4 100644 --- a/mDNSMacOSX/PreferencePane/ddnswriteconfig.m +++ b/mDNSMacOSX/PreferencePane/ddnswriteconfig.m @@ -87,18 +87,23 @@ WriteArrayToDynDNS(CFStringRef arrayKey, CFArrayRef domainArray) require_action(true == SCPreferencesLock( store, true), LockFailed, err=coreFoundationUnknownErr;); origDict = SCPreferencesPathGetValue(store, scKey); - if (origDict) { + if (origDict) + { dict = CFDictionaryCreateMutableCopy(NULL, 0, origDict); } - if (!dict) { + if (!dict) + { dict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); } require_action( dict != NULL, NoDict, err=memFullErr;); - if (CFArrayGetCount(domainArray) > 0) { + if (CFArrayGetCount(domainArray) > 0) + { CFDictionarySetValue(dict, arrayKey, domainArray); - } else { + } + else + { CFDictionaryRemoveValue(dict, arrayKey); } @@ -138,10 +143,13 @@ readTaggedBlock(int fd, u_int32_t *pTag, u_int32_t *pLen, char **ppBuff) require_action(*ppBuff != NULL, AllocFailed, result = -1;); num = read(fd, *ppBuff, len); - if (num == (ssize_t)len) { + if (num == (ssize_t)len) + { *pTag = tag; *pLen = len; - } else { + } + else + { free(*ppBuff); result = -1; } @@ -166,7 +174,8 @@ SetAuthInfo( int fd) require( len == sizeof(AuthorizationExternalForm), ReadParamsFailed); require( len == kAuthorizationExternalFormLength, ReadParamsFailed); - if (gAuthRef != 0) { + if (gAuthRef != 0) + { (void) AuthorizationFree(gAuthRef, kAuthorizationFlagDefaults); gAuthRef = 0; } @@ -199,10 +208,15 @@ HandleWriteDomain(int fd, int domainType) domainData = CFDataCreate(NULL, (UInt8 *)p, len); domainArray = (CFArrayRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)domainData]; + CFRelease(domainData); + free(p); - if (domainType) { + if (domainType) + { result = WriteArrayToDynDNS(SC_DYNDNS_REGDOMAINS_KEY, domainArray); - } else { + } + else + { result = WriteArrayToDynDNS(SC_DYNDNS_BROWSEDOMAINS_KEY, domainArray); } @@ -232,6 +246,8 @@ HandleWriteHostname(int fd) domainData = CFDataCreate(NULL, (const UInt8 *)p, len); domainArray = (CFArrayRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)domainData]; result = WriteArrayToDynDNS(SC_DYNDNS_HOSTNAMES_KEY, domainArray); + CFRelease(domainData); + free(p); ReadParamsFailed: return result; @@ -341,6 +357,8 @@ SetKeychainEntry(int fd) secretData = CFDataCreate(NULL, (UInt8 *)p, len); secretDictionary = (CFDictionaryRef)[NSUnarchiver unarchiveObjectWithData:(NSData *)secretData]; + CFRelease(secretData); + free(p); keyNameString = (CFStringRef)CFDictionaryGetValue(secretDictionary, SC_DYNDNS_KEYNAME_KEY); assert(keyNameString != NULL); @@ -356,9 +374,11 @@ SetKeychainEntry(int fd) CFStringGetCString(secretString, secret, kDNSServiceMaxDomainName, kCFStringEncodingUTF8); result = SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); - if (result == noErr) { + if (result == noErr) + { result = SecKeychainFindGenericPassword(NULL, strlen(domain), domain, 0, NULL, 0, NULL, &item); - if (result == noErr) { + if (result == noErr) + { result = SecKeychainItemDelete(item); if (result != noErr) fprintf(stderr, "SecKeychainItemDelete returned %d\n", result); } diff --git a/mDNSMacOSX/Private/com.apple.mDNSResponder.plist b/mDNSMacOSX/Private/com.apple.mDNSResponder.plist new file mode 100644 index 0000000..6d403b5 --- /dev/null +++ b/mDNSMacOSX/Private/com.apple.mDNSResponder.plist @@ -0,0 +1,19 @@ + + + + + DEFAULT-OPTIONS + + Default-Privacy-Setting + Public + Level + + Persist + Inherit + Enable + Inherit + + + + + diff --git a/mDNSMacOSX/Private/dns_sd_private.h b/mDNSMacOSX/Private/dns_sd_private.h new file mode 100644 index 0000000..f3b480e --- /dev/null +++ b/mDNSMacOSX/Private/dns_sd_private.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + */ + +#ifndef _DNS_SD_PRIVATE_H +#define _DNS_SD_PRIVATE_H + +/* DNSServiceCreateDelegateConnection() + * + * Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. + * + * pid : Process ID of the delegate + * + * uuid: UUID of the delegate + * + * Note that only one of the two arguments (pid or uuid) can be specified. If pid + * is zero, uuid will be assumed to be a valid value; otherwise pid will be used. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred (in which + * case the DNSServiceRef is not initialized). kDNSServiceErr_NotAuth is + * returned to indicate that the calling process does not have entitlements + * to use this API. + */ +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid); + +// Map the source port of the local UDP socket that was opened for sending the DNS query +// to the process ID of the application that triggered the DNS resolution. +// +/* DNSServiceGetPID() Parameters: + * + * srcport: Source port (in network byte order) of the UDP socket that was created by + * the daemon to send the DNS query on the wire. + * + * pid: Process ID of the application that started the name resolution which triggered + * the daemon to send the query on the wire. The value can be -1 if the srcport + * cannot be mapped. + * + * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning + * if the daemon is not running. The value of the pid is undefined if the return + * value has error. + */ +DNSServiceErrorType DNSSD_API DNSServiceGetPID +( + uint16_t srcport, + int32_t *pid +); + +#define kDNSServiceCompPrivateDNS "PrivateDNS" +#define kDNSServiceCompMulticastDNS "MulticastDNS" + +#endif diff --git a/mDNSMacOSX/Private/dns_services.c b/mDNSMacOSX/Private/dns_services.c index 1df3b21..46e2a23 100644 --- a/mDNSMacOSX/Private/dns_services.c +++ b/mDNSMacOSX/Private/dns_services.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Inc. All rights reserved. + * Copyright (c) 2012-2015 Apple Inc. All rights reserved. * * PRIVATE DNSX CLIENT LIBRARY --FOR Apple Platforms ONLY OSX/iOS-- * Resides in /usr/lib/libdns_services.dylib @@ -10,9 +10,7 @@ #include "dns_xpc.h" #include #include -#include -#include -#include +#include //************************************************************************************************************* // Globals @@ -22,7 +20,7 @@ struct _DNSXConnRef_t { connection_t conn_ref; // xpc_connection between client and daemon - dispatch_queue_t lib_q; // internal queue created in library itself + 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 }; @@ -37,11 +35,11 @@ static bool LogDebugEnabled() static void LogDebug(const char *prefix, xpc_object_t o) { - if (!LogDebugEnabled()) + if (!LogDebugEnabled()) return; char *desc = xpc_copy_description(o); - syslog(LOG_INFO, "%s: %s", prefix, desc); + os_log_info(OS_LOG_DEFAULT, "%s: %s", prefix, desc); free(desc); } @@ -49,164 +47,206 @@ static void LogDebug(const char *prefix, xpc_object_t o) void DNSXRefDeAlloc(DNSXConnRef connRef) { - if (!connRef) + if (connRef == NULL) { - syslog(LOG_WARNING, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef"); + os_log(OS_LOG_DEFAULT, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef"); return; } - + // Schedule this work on the internal library queue dispatch_sync(connRef->lib_q, ^{ - + xpc_connection_set_event_handler((connRef)->conn_ref, ^(__unused xpc_object_t event){}); // ignore any more events xpc_release(connRef->conn_ref); + connRef->conn_ref = NULL; + dispatch_release(connRef->lib_q); + connRef->lib_q = NULL; connRef->AppCallBack = NULL; - dispatch_release(connRef->client_q); - + os_log_info(OS_LOG_DEFAULT, "dns_services: DNSXRefDeAlloc successfully DeAllocated conn_ref & lib_q"); + + dispatch_async((connRef)->client_q, ^{ + dispatch_release(connRef->client_q); + connRef->client_q = NULL; + free(connRef); + os_log_info(OS_LOG_DEFAULT, "dns_services: DNSXRefDeAlloc successfully DeAllocated client_q & freed connRef"); + }); }); - - dispatch_release(connRef->lib_q); - free(connRef); - - syslog(LOG_INFO, "dns_services: DNSXRefDeAlloc successfully DeAllocated connRef"); - + + // DO NOT reference connRef after this comment, as it may have been freed + os_log_info(OS_LOG_DEFAULT, "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) +// Sends the Msg(Dictionary) to the Server Daemon +static DNSXErrorType SendMsgToServer(DNSXConnRef connRef, xpc_object_t msg) { DNSXErrorType errx = kDNSX_NoError; - - LogDebug("dns_services: SendMsgToServer", msg); - xpc_connection_set_event_handler((*connRef)->conn_ref, ^(xpc_object_t recv_msg) + LogDebug("dns_services: SendMsgToServer Sending msg to Daemon", msg); + + xpc_connection_send_message_with_reply((connRef)->conn_ref, msg, (connRef)->lib_q, ^(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"); + LogDebug("dns_services: SendMsgToServer Received reply msg from Daemon", recv_msg); 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; - } - + + if (connRef == NULL || connRef->client_q == NULL || connRef->AppCallBack == NULL) + { + // If connRef is bad, do not schedule any callbacks to the client + os_log(OS_LOG_DEFAULT, "dns_services: SendMsgToServer: connRef is BAD Daemon status code [%llu]", daemon_status); + } + else + { + switch (daemon_status) + { + case kDNSMsg_NoError: + dispatch_async((connRef)->client_q, ^{ + if (connRef->AppCallBack != NULL) + ((DNSXEnableProxyReply)connRef->AppCallBack)(connRef, kDNSX_NoError); + }); + break; + + case kDNSMsg_Busy: + os_log(OS_LOG_DEFAULT, "dns_services: SendMsgToServer: DNS Proxy already in use"); + dispatch_async((connRef)->client_q, ^{ + if (connRef->AppCallBack != NULL) + ((DNSXEnableProxyReply)connRef->AppCallBack)(connRef, kDNSX_Busy); + }); + break; + + default: + os_log(OS_LOG_DEFAULT, "dns_services: SendMsgToServer: Unknown error"); + dispatch_async((connRef)->client_q, ^{ + if (connRef->AppCallBack != NULL) + ((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); - }); + os_log(OS_LOG_DEFAULT, "dns_services: SendMsgToServer Received unexpected reply from daemon [%s]", + xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION)); + LogDebug("dns_services: SendMsgToServer Unexpected Reply contents", recv_msg); } }); - // 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) +// Creates a new DNSX Connection Reference(DNSXConnRef) +static DNSXErrorType InitConnection(DNSXConnRef *connRefOut, const char *servname, dispatch_queue_t clientq, void *AppCallBack) { - if (!connRef) + if (connRefOut == NULL) { - syslog(LOG_WARNING, "dns_services: InitConnection() called with NULL DNSXConnRef"); - return kDNSX_BadParam; + os_log(OS_LOG_DEFAULT, "dns_services: InitConnection() connRef cannot be NULL"); + return kDNSX_BadParam; } - - *connRef = malloc(sizeof(struct _DNSXConnRef_t)); - if (!(*connRef)) + + // Use a DNSXConnRef on the stack to be captured in the blocks below, rather than capturing the DNSXConnRef* owned by the client + DNSXConnRef connRef = malloc(sizeof(struct _DNSXConnRef_t)); + if (connRef == NULL) { - syslog(LOG_WARNING, "dns_services: InitConnection() No memory to allocate"); + os_log(OS_LOG_DEFAULT, "dns_services: InitConnection() No memory to allocate!"); return kDNSX_NoMem; } - - // Initialize the DNSXConnRef + + // 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"); + connRef->client_q = clientq; + connRef->AppCallBack = AppCallBack; + connRef->lib_q = dispatch_queue_create("com.apple.mDNSResponder.libdns_services.q", DISPATCH_QUEUE_SERIAL); + connRef->conn_ref = xpc_connection_create_mach_service(servname, connRef->lib_q, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + + if (connRef->conn_ref == NULL || connRef->lib_q == NULL) + { + os_log(OS_LOG_DEFAULT, "dns_services: InitConnection() conn_ref/lib_q is NULL"); + if (connRef != NULL) + free(connRef); + return kDNSX_NoMem; + } + + xpc_connection_set_event_handler(connRef->conn_ref, ^(xpc_object_t event) + { + if (connRef == NULL || connRef->client_q == NULL || connRef->AppCallBack == NULL) + { + // If connRef is bad, do not schedule any callbacks to the client + os_log(OS_LOG_DEFAULT, "dns_services: InitConnection: connRef is BAD Unexpected Connection Error [%s]", + xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + } + else + { + os_log(OS_LOG_DEFAULT, "dns_services: InitConnection: Unexpected Connection Error [%s] Ping the client", + xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + dispatch_async(connRef->client_q, ^{ + if (connRef->AppCallBack != NULL) + ((DNSXEnableProxyReply)connRef->AppCallBack)(connRef, kDNSX_DaemonNotRunning); + }); + } + + }); + xpc_connection_resume(connRef->conn_ref); + + *connRefOut = connRef; + return kDNSX_NoError; } -DNSXErrorType DNSXEnableProxy(DNSXConnRef *connRef, DNSProxyParameters proxyparam, IfIndex inIfindexArr[MaxInputIf], - IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack) +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) + if (connRef == NULL || callBack == NULL || clientq == NULL) { - syslog(LOG_WARNING, "dns_services: DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter"); + os_log(OS_LOG_DEFAULT, "dns_services: DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter"); return kDNSX_BadParam; - } - - // If no connRef, get it from InitConnection() - if (!*connRef) + } + + // Get connRef from InitConnection() + if (*connRef == NULL) { 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); + os_log(OS_LOG_DEFAULT, "dns_services: Since InitConnection() returned %d error returning w/o sending msg", errx); return errx; } } - else // Client already has a valid connRef + else // Client already has a connRef and this is not valid use for this SPI { - old_conn = true; + os_log(OS_LOG_DEFAULT, "dns_services: Client already has a valid connRef! This is incorrect usage from the client"); + return kDNSX_BadParam; } - + // Create Dictionary To Send - xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); - if (!dict) + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + if (dict == NULL) { - syslog(LOG_WARNING, "dns_services: DNSXEnableProxy could not create the Msg Dict To Send!"); + os_log(OS_LOG_DEFAULT, "dns_services: DNSXEnableProxy could not create the Msg Dict To Send!"); DNSXRefDeAlloc(*connRef); - return kDNSX_DictError; + return kDNSX_NoMem; } - + 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, 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); + + errx = SendMsgToServer(*connRef, dict); xpc_release(dict); - - return errx; + dict = NULL; + + return errx; } diff --git a/mDNSMacOSX/Private/dns_services.h b/mDNSMacOSX/Private/dns_services.h index 7b74e10..34c6875 100644 --- a/mDNSMacOSX/Private/dns_services.h +++ b/mDNSMacOSX/Private/dns_services.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Inc. All rights reserved. + * Copyright (c) 2012-2015 Apple Inc. All rights reserved. * * * @header Interface to DNSX SPI @@ -21,15 +21,13 @@ 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 + kDNSX_NoMem = -65539, /* No Memory */ + kDNSX_BadParam = -65540, /* Client passed invalid arg */ + kDNSX_Busy = -65551, /* DNS Proxy already in use: incorrect use of SPI by client */ + kDNSX_DaemonNotRunning = -65563 /* Daemon not running */ } DNSXErrorType; -// A max of 5 input interfaces can be processed at one time +// A max of 5 input interfaces can be processed #define MaxInputIf 5 #define IfIndex uint64_t #define kDNSIfindexAny 0 @@ -42,10 +40,10 @@ typedef enum } DNSProxyParameters; /********************************************************************************************* -* -* Enable DNS Proxy Functionality -* -*********************************************************************************************/ + * + * Enable DNS Proxy Functionality + * + *********************************************************************************************/ /* DNSXEnableProxy : Turns ON the DNS Proxy (Details below) * @@ -53,27 +51,26 @@ typedef enum * * 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. + * errCode: Will be kDNSX_NoError on success, otherwise will indicate the + * failure that occurred. * */ typedef void (*DNSXEnableProxyReply) ( DNSXConnRef connRef, - DNSXErrorType errCode + 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() - * + * + * Enables the DNS Proxy functionality which will remain ON until the client explicitly turns it OFF + * by passing the returned DNSXConnRef to DNSXRefDeAlloc(), or the client exits or crashes. + * * 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. + * connRef: A pointer to DNSXConnRef that is initialized to NULL. + * 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 @@ -88,16 +85,19 @@ typedef void (*DNSXEnableProxyReply) * outIfindex: Output interface on which the query will be forwarded. * Passing kDNSIfindexAny causes DNS Queries to be sent on the primary interface. * + * Note: It is the responsibility of the client to ensure the input/output interface + * indexes are valid. + * * 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. + * Note: callback may be invoked more than once, For e.g. 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 + * 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) + * return an error (such as kDNSX_DaemonNotRunning) * */ @@ -109,7 +109,7 @@ DNSXErrorType DNSXEnableProxy IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack -); + ); /* DNSXRefDeAlloc() * @@ -121,4 +121,4 @@ DNSXErrorType DNSXEnableProxy */ void DNSXRefDeAlloc(DNSXConnRef connRef); -#endif /* _DNS_SERVICES_H */ +#endif diff --git a/mDNSMacOSX/Private/dns_xpc.h b/mDNSMacOSX/Private/dns_xpc.h index 10ae01f..bda2bf9 100644 --- a/mDNSMacOSX/Private/dns_xpc.h +++ b/mDNSMacOSX/Private/dns_xpc.h @@ -11,6 +11,7 @@ #define DNS_XPC_H #define kDNSProxyService "com.apple.mDNSResponder.dnsproxy" +#define kDNSCTLService "com.apple.mDNSResponder.dnsctl" #define kDNSProxyParameters "DNSProxyParameters" @@ -26,8 +27,35 @@ typedef enum { - kDNSMsgReceived = 0, - kDNSDaemonEngaged + kDNSMsg_NoError = 0, + kDNSMsg_Busy } DaemonReplyStatusCodes; +#define kDNSLogLevel "DNSLoggingVerbosity" + +typedef enum +{ + log_level1 = 1, // logging off + log_level2, // logging USR1 + log_level3, // logging USR2 + log_level4, // logging USR1/2 +} DNSLogLevels; + +#define kDNSStateInfo "DNSStateInfoLevels" + +typedef enum +{ + full_state = 1, // full state info of mDNSResponder (INFO) +} DNSStateInfo; + +#define kmDNSResponderTests "mDNSResponderTests" + +typedef enum +{ + test_helper_ipc = 1, // invokes mDNSResponder to send a test msg to mDNSResponderHelper + test_mDNS_log, // invokes mDNSResponder to log using different internal macros +} mDNSTestModes; + + + #endif // DNS_XPC_H diff --git a/mDNSMacOSX/Private/dnsctl_server.c b/mDNSMacOSX/Private/dnsctl_server.c new file mode 100644 index 0000000..9b3885e --- /dev/null +++ b/mDNSMacOSX/Private/dnsctl_server.c @@ -0,0 +1,224 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * dnsctl_server.c + * mDNSResponder + * + * XPC as an IPC mechanism to communicate with dnsctl. Open only to Apple OSX/iOS clients + */ + +#include "xpc_services.h" +#include "dns_xpc.h" + +#include "mDNSMacOSX.h" // KQueueLock/KQueueUnlock +#include "helper.h" // mDNSResponderHelper tests +#include + +// *************************************************************************** +// Globals +extern mDNS mDNSStorage; +static dispatch_queue_t dnsctlserver_queue = NULL; +// *************************************************************************** + +mDNSlocal void handle_logging(mDNSu32 log_level) +{ + KQueueLock(&mDNSStorage); + + switch (log_level) + { + case log_level1: + mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 0; + LogMsg("USR1 Logging:[%s] USR2 Logging:[%s]", mDNS_LoggingEnabled ? "Enabled" : "Disabled", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + break; + + case log_level2: + mDNS_LoggingEnabled = 1; + LogMsg("USR1 Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); + break; + + case log_level3: + mDNS_PacketLoggingEnabled = 1; + LogMsg("USR2 Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + break; + + case log_level4: + mDNS_LoggingEnabled = 1 ; + mDNS_PacketLoggingEnabled = 1; + LogMsg("USR1 Logging:%s USR2 Logging:%s", mDNS_LoggingEnabled ? "Enabled" : "Disabled", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + break; + + default: + mDNS_LoggingEnabled = 0 ; + mDNS_PacketLoggingEnabled = 0; + break; + } + UpdateDebugState(); + + KQueueUnlock(&mDNSStorage, "LogLevel changed"); +} + +mDNSlocal void handle_stateinfo(mDNSu32 state_level) +{ + KQueueLock(&mDNSStorage); + + switch (state_level) + { + case full_state: + INFOCallback(); + break; + + default: + INFOCallback(); + break; + } + + KQueueUnlock(&mDNSStorage, "StateInfo dumped"); +} + + +mDNSlocal void handle_test_mode(mDNSu32 test_mode) +{ + KQueueLock(&mDNSStorage); + + switch (test_mode) + { + case test_helper_ipc: + mDNSNotify("mDNSResponderHelperTest", "This is just a test message to mDNSResponderHelper. This is NOT an actual alert"); + break; + + case test_mDNS_log: + LogInfo("LogInfo: Should be logged at INFO level"); + LogOperation("LogOperation: Should be logged at INFO level"); + LogMsg("LogMsg: Should be logged at DEFAULT level"); + LogSPS("LogSPS: Should be logged at INFO level"); + break; + + default: + LogMsg("Unidentified Test mode: Please add this test"); + break; + } + + KQueueUnlock(&mDNSStorage, "Test Msg to mDNSResponderHelper"); +} + +mDNSlocal void handle_terminate() +{ + + LogInfo("handle_terminate: Client terminated connection."); + +} + +mDNSlocal void handle_requests(xpc_object_t req) +{ + mDNSu32 log_level, state_level, test_mode; + xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req); + + LogInfo("handle_requests: Handler for dnsctl requests"); + + xpc_object_t response = xpc_dictionary_create_reply(req); + + // Return Success Status to the client (essentially ACKing the request) + if (response) + { + xpc_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsg_NoError); + xpc_connection_send_message(remote_conn, response); + xpc_release(response); + } + else + { + LogMsg("handle_requests: Response Dictionary could not be created"); + return; + } + + // switch here based on dictionary + // to handle various different requests like logging, INFO snapshot, etc.. + if ((xpc_dictionary_get_uint64(req, kDNSLogLevel))) + { + log_level = (mDNSu32)(xpc_dictionary_get_uint64(req, kDNSLogLevel)); + handle_logging(log_level); + } + else if ((xpc_dictionary_get_uint64(req, kDNSStateInfo))) + { + state_level = (mDNSu32)(xpc_dictionary_get_uint64(req, kDNSStateInfo)); + handle_stateinfo(state_level); + } + else if ((xpc_dictionary_get_uint64(req, kmDNSResponderTests))) + { + test_mode = (mDNSu32)(xpc_dictionary_get_uint64(req, kmDNSResponderTests)); + handle_test_mode(test_mode); + } +} + +mDNSlocal void accept_client(xpc_connection_t conn) +{ + uid_t c_euid; + int c_pid; + c_euid = xpc_connection_get_euid(conn); + c_pid = xpc_connection_get_pid(conn); + + if (c_euid != 0 || !IsEntitled(conn, kDNSCTLService)) + { + LogMsg("accept_client: Client PID[%d] is missing Entitlement or is NOT running as root!", c_pid); + xpc_connection_cancel(conn); + return; + } + + xpc_retain(conn); + xpc_connection_set_target_queue(conn, dnsctlserver_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_requests(req_msg); + } + else // We hit this case ONLY if Client Terminated Connection OR Crashed + { + LogInfo("accept_client: Client %p teared down the connection", (void *) conn); + handle_terminate(); + xpc_release(conn); + } + }); + + xpc_connection_resume(conn); + +} + +mDNSexport void init_dnsctl_service(void) +{ + + xpc_connection_t dnsctl_listener = xpc_connection_create_mach_service(kDNSCTLService, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); + if (!dnsctl_listener || xpc_get_type(dnsctl_listener) != XPC_TYPE_CONNECTION) + { + LogMsg("init_dnsctl_service: Error Creating XPC Listener for DNSCTL Server!"); + return; + } + + dnsctlserver_queue = dispatch_queue_create("com.apple.mDNSResponder.dnsctlserver_queue", NULL); + + xpc_connection_set_event_handler(dnsctl_listener, ^(xpc_object_t eventmsg) + { + xpc_type_t type = xpc_get_type(eventmsg); + + if (type == XPC_TYPE_CONNECTION) + { + LogInfo("init_dnsctl_service: New DNSCTL Client %p", eventmsg); + accept_client(eventmsg); + } + else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases + { + LogMsg("init_dnsctl_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION)); + } + else + { + LogMsg("init_dnsctl_service: Unknown EventMsg type"); + } + }); + + xpc_connection_resume(dnsctl_listener); +} + + + diff --git a/mDNSMacOSX/Private/xpc_services.c b/mDNSMacOSX/Private/xpc_services.c index 7a0e29f..4cad6ba 100644 --- a/mDNSMacOSX/Private/xpc_services.c +++ b/mDNSMacOSX/Private/xpc_services.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2012 Apple Inc. All rights reserved. + * Copyright (c) 2012-2015 Apple Inc. All rights reserved. * * xpc_services.c * mDNSResponder @@ -15,8 +15,7 @@ #include "dnsproxy.h" // DNSProxyInit/ProxyUDPCallback/ProxyTCPCallback #include "mDNSMacOSX.h" // KQueueLock/KQueueUnlock -#include -#include // xpc_connection_copy_entitlement_value +#include // xpc_connection_copy_entitlement_value // *************************************************************************** // Globals @@ -87,10 +86,10 @@ mDNSlocal void handle_dps_request(xpc_object_t req) { 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); + xpc_object_t reply = xpc_dictionary_create_reply(req); if (reply) { - xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSDaemonEngaged); + xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSMsg_Busy); xpc_connection_send_message(remote_conn, reply); xpc_release(reply); } @@ -104,12 +103,13 @@ mDNSlocal void handle_dps_request(xpc_object_t req) return; } } - + + + xpc_object_t response = xpc_dictionary_create_reply(req); // 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_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsg_NoError); xpc_connection_send_message(remote_conn, response); xpc_release(response); } @@ -137,7 +137,7 @@ mDNSlocal void handle_dps_request(xpc_object_t req) } // Verify Client's Entitlement -mDNSlocal mDNSBool IsEntitled(xpc_connection_t conn, const char *password) +mDNSexport mDNSBool IsEntitled(xpc_connection_t conn, const char *password) { mDNSBool entitled = mDNSfalse; xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password); @@ -155,21 +155,26 @@ mDNSlocal mDNSBool IsEntitled(xpc_connection_t conn, const char *password) LogMsg("IsEntitled: Client Entitlement is NULL"); } + if (!entitled) + LogMsg("IsEntitled: Client is missing Entitlement!"); + return entitled; } mDNSlocal void accept_dps_client(xpc_connection_t conn) { - uid_t euid; - euid = xpc_connection_get_euid(conn); + uid_t c_euid; + int c_pid; + c_euid = xpc_connection_get_euid(conn); + c_pid = xpc_connection_get_pid(conn); - if (euid != 0 || !IsEntitled(conn, kDNSProxyService)) + if (c_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)); + LogMsg("accept_dps_client: DNSProxyService Client PID[%d] is missing Entitlement or is not running as root!", c_pid); xpc_connection_cancel(conn); return; } - + xpc_retain(conn); xpc_connection_set_target_queue(conn, dps_queue); xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg) @@ -180,16 +185,16 @@ mDNSlocal void accept_dps_client(xpc_connection_t conn) { handle_dps_request(req_msg); } - // We hit the case below only if Client Terminated DPS Connection OR Crashed - else + else // We hit this case ONLY if Client Terminated DPS Connection OR Crashed { LogInfo("accept_dps_client: DPS Client %p teared down the connection or Crashed", (void *) conn); // Only the Client that has activated DPS should be able to terminate it - if (((int)xpc_connection_get_pid(conn)) == dps_client_pid) + if (c_pid == dps_client_pid) handle_dps_terminate(); xpc_release(conn); } }); + xpc_connection_resume(conn); } @@ -215,8 +220,7 @@ mDNSlocal void init_dnsproxy_service(void) 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) + else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases { LogMsg("init_dnsproxy_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION)); return; @@ -227,6 +231,7 @@ mDNSlocal void init_dnsproxy_service(void) return; } }); + xpc_connection_resume(dps_listener); } @@ -235,8 +240,10 @@ mDNSexport void xpc_server_init() { // Add XPC Services here init_dnsproxy_service(); + init_dnsctl_service(); } + #else // !UNICAST_DISABLED mDNSexport void xpc_server_init() diff --git a/mDNSMacOSX/Private/xpc_services.h b/mDNSMacOSX/Private/xpc_services.h index 50081be..8d7c043 100644 --- a/mDNSMacOSX/Private/xpc_services.h +++ b/mDNSMacOSX/Private/xpc_services.h @@ -14,8 +14,14 @@ #define XPC_SERVICES_H #include "mDNSEmbeddedAPI.h" +#include extern void xpc_server_init(void); extern void xpcserver_info(mDNS *const m); +extern mDNSBool IsEntitled(xpc_connection_t conn, const char *password); +extern void init_dnsctl_service(void); + +extern void INFOCallback(void); + #endif // XPC_SERVICES_H diff --git a/mDNSMacOSX/SymptomReporter.c b/mDNSMacOSX/SymptomReporter.c new file mode 100644 index 0000000..7dde2f5 --- /dev/null +++ b/mDNSMacOSX/SymptomReporter.c @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015 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 "mDNSEmbeddedAPI.h" + +#include +#include +#include +#include +#include +#include +#include + +#define TARGET_OS_MACOSX (TARGET_OS_MAC && !TARGET_OS_IPHONE) + +#if (!TARGET_OS_MACOSX || (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)) +#include +#else +#include +#endif + +#define SYMPTOM_REPORTER_mDNSResponder_NUMERIC_ID 101 +#define SYMPTOM_REPORTER_mDNSResponder_TEXT_ID "com.apple.mDNSResponder" + +#define SYMPTOM_DNS_NO_REPLIES 0x00065001 +#define SYMPTOM_DNS_RESUMED_RESPONDING 0x00065002 + +static symptom_framework_t symptomReporter = mDNSNULL; +static symptom_framework_t (*symptom_framework_init_f)(symptom_ident_t id, const char *originator_string) = mDNSNULL; +static symptom_t (*symptom_new_f)(symptom_framework_t framework, symptom_ident_t id) = mDNSNULL; +static int (*symptom_set_additional_qualifier_f)(symptom_t symptom, uint32_t qualifier_type, size_t qualifier_len, void *qualifier_data) = mDNSNULL; +static int (*symptom_send_f)(symptom_t symptom) = mDNSNULL; + +mDNSlocal mStatus SymptomReporterInitCheck(void) +{ + mStatus err; + static mDNSBool triedInit = mDNSfalse; + static void *symptomReporterLib = mDNSNULL; +#if (!TARGET_OS_MACOSX || (MAC_OS_X_VERSION_MAX_ALLOWED >= 101200)) + static const char path[] = "/System/Library/PrivateFrameworks/SymptomReporter.framework/SymptomReporter"; +#else + static const char path[] = "/System/Library/PrivateFrameworks/Symptoms.framework/Frameworks/SymptomReporter.framework/SymptomReporter"; +#endif + + if (!triedInit) + { + triedInit = mDNStrue; + + symptomReporterLib = dlopen(path, RTLD_LAZY | RTLD_LOCAL); + if (!symptomReporterLib) + goto exit; + + symptom_framework_init_f = dlsym(symptomReporterLib, "symptom_framework_init"); + if (!symptom_framework_init_f) + goto exit; + + symptom_new_f = dlsym(symptomReporterLib, "symptom_new"); + if (!symptom_new_f) + goto exit; + + symptom_set_additional_qualifier_f = dlsym(symptomReporterLib, "symptom_set_additional_qualifier"); + if (!symptom_set_additional_qualifier_f) + goto exit; + + symptom_send_f = dlsym(symptomReporterLib, "symptom_send"); + if (!symptom_send_f) + goto exit; + + symptomReporter = symptom_framework_init_f(SYMPTOM_REPORTER_mDNSResponder_NUMERIC_ID, SYMPTOM_REPORTER_mDNSResponder_TEXT_ID); + } + +exit: + err = symptomReporter ? mStatus_NoError : mStatus_NotInitializedErr; + return err; +} + +mDNSlocal mStatus SymptomReporterReportDNSReachability(const mDNSAddr *addr, mDNSBool isReachable) +{ + mStatus err; + symptom_t symptom; + struct sockaddr_storage sockAddr; + size_t sockAddrSize; + + LogInfo("SymptomReporterReportDNSReachability: DNS server %#a is %sreachable", addr, isReachable ? "" : "un"); + + if (addr->type == mDNSAddrType_IPv4) + { + struct sockaddr_in *sin = (struct sockaddr_in *)&sockAddr; + sockAddrSize = sizeof(*sin); + mDNSPlatformMemZero(sin, sockAddrSize); + sin->sin_len = sockAddrSize; + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = addr->ip.v4.NotAnInteger; + } + else if (addr->type == mDNSAddrType_IPv6) + { + struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&sockAddr; + sockAddrSize = sizeof(*sin6); + mDNSPlatformMemZero(sin6, sockAddrSize); + sin6->sin6_len = sockAddrSize; + sin6->sin6_family = AF_INET6; + sin6->sin6_addr = *(struct in6_addr *)&addr->ip.v6; + } + else + { + LogMsg("SymptomReporterReportDNSReachability: addr is not an IPv4 or IPv6 address!"); + err = mStatus_BadParamErr; + goto exit; + } + + symptom = symptom_new_f(symptomReporter, isReachable ? SYMPTOM_DNS_RESUMED_RESPONDING : SYMPTOM_DNS_NO_REPLIES); + symptom_set_additional_qualifier_f(symptom, 1, sockAddrSize, (void *)&sockAddr); + symptom_send_f(symptom); + err = mStatus_NoError; + +exit: + return err; +} + +mDNSexport mStatus SymptomReporterDNSServerReachable(mDNS *const m, const mDNSAddr *addr) +{ + mStatus err; + DNSServer *s; + mDNSBool found = mDNSfalse; + + err = SymptomReporterInitCheck(); + if (err != mStatus_NoError) + goto exit; + + for (s = m->DNSServers; s; s = s->next) + { + if (s->flags & DNSServer_FlagDelete) + continue; + if ((s->flags & DNSServer_FlagUnreachable) && mDNSSameAddress(addr, &s->addr)) + { + s->flags &= ~DNSServer_FlagUnreachable; + NumUnreachableDNSServers--; + found = mDNStrue; + } + } + + if (!found) + { + err = mStatus_NoSuchNameErr; + goto exit; + } + + err = SymptomReporterReportDNSReachability(addr, mDNStrue); + +exit: + return err; +} + +mDNSexport mStatus SymptomReporterDNSServerUnreachable(DNSServer *s) +{ + mStatus err; + + err = SymptomReporterInitCheck(); + if (err != mStatus_NoError) + goto exit; + + if ((s->flags & DNSServer_FlagDelete) || (s->flags & DNSServer_FlagUnreachable)) + goto exit; + + s->flags |= DNSServer_FlagUnreachable; + NumUnreachableDNSServers++; + + err = SymptomReporterReportDNSReachability(&s->addr, mDNSfalse); + +exit: + return err; +} diff --git a/mDNSMacOSX/base.xcconfig b/mDNSMacOSX/base.xcconfig deleted file mode 100644 index 28d9c73..0000000 --- a/mDNSMacOSX/base.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -PUBLIC_HEADERS_FOLDER_PATH = /usr/include -PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include diff --git a/mDNSMacOSX/LaunchDaemonInfo.dnsextd.plist b/mDNSMacOSX/com.apple.dnsextd.plist similarity index 92% rename from mDNSMacOSX/LaunchDaemonInfo.dnsextd.plist rename to mDNSMacOSX/com.apple.dnsextd.plist index e31b391..86d7b77 100644 --- a/mDNSMacOSX/LaunchDaemonInfo.dnsextd.plist +++ b/mDNSMacOSX/com.apple.dnsextd.plist @@ -6,8 +6,6 @@ Label com.apple.dnsextd - OnDemand - ProgramArguments /usr/sbin/dnsextd diff --git a/mDNSMacOSX/LaunchDaemonInfo.plist b/mDNSMacOSX/com.apple.mDNSResponder.plist similarity index 69% rename from mDNSMacOSX/LaunchDaemonInfo.plist rename to mDNSMacOSX/com.apple.mDNSResponder.plist index 4226c79..881f8c9 100644 --- a/mDNSMacOSX/LaunchDaemonInfo.plist +++ b/mDNSMacOSX/com.apple.mDNSResponder.plist @@ -1,11 +1,9 @@ - + Label com.apple.mDNSResponder.reloaded - OnDemand - InitGroups UserName @@ -18,10 +16,10 @@ MachServices - com.apple.mDNSResponder + com.apple.mDNSResponder.dnsproxy + + com.apple.mDNSResponder.dnsctl - com.apple.mDNSResponder.dnsproxy - Sockets @@ -35,11 +33,9 @@ 438 - EnableTransactions - - BeginTransactionAtShutdown - POSIXSpawnType Interactive + EnablePressuredExit + diff --git a/mDNSMacOSX/LaunchDaemonInfo.helper.plist b/mDNSMacOSX/com.apple.mDNSResponderHelper.plist similarity index 79% rename from mDNSMacOSX/LaunchDaemonInfo.helper.plist rename to mDNSMacOSX/com.apple.mDNSResponderHelper.plist index 09b61e0..91ed1da 100644 --- a/mDNSMacOSX/LaunchDaemonInfo.helper.plist +++ b/mDNSMacOSX/com.apple.mDNSResponderHelper.plist @@ -12,13 +12,9 @@ MachServices - com.apple.mDNSResponderHelper + com.apple.mDNSResponder_Helper - EnableTransactions - - BeginTransactionAtShutdown - POSIXSpawnType Interactive diff --git a/mDNSMacOSX/com.apple.networking.mDNSResponder b/mDNSMacOSX/com.apple.networking.mDNSResponder deleted file mode 100644 index d07c99d..0000000 --- a/mDNSMacOSX/com.apple.networking.mDNSResponder +++ /dev/null @@ -1 +0,0 @@ -? [= LoggerID com.apple.networking.mDNSResponder] file /Library/Logs/CrashReporter/com.apple.networking.mDNSResponder.log rotate file_max=1M compress diff --git a/mDNSMacOS9/CarbonResource.r b/mDNSMacOSX/coreBLE.h similarity index 57% rename from mDNSMacOS9/CarbonResource.r rename to mDNSMacOSX/coreBLE.h index 3bd7fa6..cc00923 100644 --- a/mDNSMacOS9/CarbonResource.r +++ b/mDNSMacOSX/coreBLE.h @@ -1,13 +1,13 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2015-2016 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. @@ -15,4 +15,18 @@ * limitations under the License. */ -data 'carb' (0) { }; +#ifndef _coreBLE_H_ +#define _coreBLE_H_ + +#include "BLE.h" + +@interface coreBLE : NSObject + +- (id)init; +- (void) advertiseBrowses:(serviceHash_t) browseHash andRegistrations:(serviceHash_t) registeredHash; +- (void) stopBeacon; +- (void) updateScan:(bool) start; + +@end + +#endif /* _coreBLE_H_ */ diff --git a/mDNSMacOSX/coreBLE.m b/mDNSMacOSX/coreBLE.m new file mode 100644 index 0000000..e764169 --- /dev/null +++ b/mDNSMacOSX/coreBLE.m @@ -0,0 +1,371 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015-2016 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 "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" + +#import +#import +#import +#import "mDNSMacOSX.h" +#import "BLE.h" +#import "coreBLE.h" + +static coreBLE * coreBLEptr; + +// Call Bluetooth subsystem to start/stop the the Bonjour BLE beacon and +// beacon scanning based on the current browse and registration hashes. +void updateBLEBeaconAndScan(serviceHash_t browseHash, serviceHash_t registeredHash) +{ + if (coreBLEptr == 0) + coreBLEptr = [[coreBLE alloc] init]; + + LogInfo("updateBLEBeaconAndScan: browseHash = 0x%x, registeredHash = 0x%x", browseHash, registeredHash); + + [coreBLEptr advertiseBrowses:browseHash andRegistrations: registeredHash]; + [coreBLEptr updateScan:(browseHash || registeredHash)]; +} + +// Stop the current BLE beacon. +void stopBLEBeacon(void) +{ + if (coreBLEptr == 0) + { + LogInfo("stopBLEBeacon called before BLE scan initialized ??"); + return; + } + + LogInfo("stopBLEBeacon Stopping beacon"); + [coreBLEptr stopBeacon]; +} + +@implementation coreBLE +{ + CBCentralManager *_centralManager; + CBPeripheralManager *_peripheralManager; + + NSData *_currentlyAdvertisedData; + + // [_centralManager isScanning] is only avilable on iOS and not OSX, + // so track scanning state locally. + BOOL _isScanning; +} + +- (id)init +{ + self = [super init]; + + if (self) + { + _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; + _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; + _currentlyAdvertisedData = nil; + _isScanning = NO; + + if (_centralManager == nil || _peripheralManager == nil ) + { + LogMsg("coreBLE initialization failed!"); + } + else + { + LogInfo("coreBLE initialised"); + } + } + + return self; +} + +#define ADVERTISEMENTDATALENGTH 28 // 31 - 3 (3 bytes for flags) + +// TODO: +// DBDeviceTypeBonjour should eventually be added to the DBDeviceType definitions in WirelessProximity +// The Bluetooth team recommended using a value < 32 for prototyping, since 32 is the number of +// beacon types they can track in their duplicate beacon filtering logic. +#define DBDeviceTypeBonjour 26 + +// Beacon flags and version byte +#define BonjourBLEVersion 1 + +extern mDNS mDNSStorage; +extern mDNSInterfaceID AWDLInterfaceID; + +// Transmit the last beacon indicating we are no longer advertising or browsing any services for two seconds. +#define LastBeaconTime 2 + +- (void) advertiseBrowses:(serviceHash_t) browseHash andRegistrations:(serviceHash_t) registeredHash +{ + uint8_t advertisingData[ADVERTISEMENTDATALENGTH] = {0, 0xff, 0x4c, 0x00 }; + uint8_t advertisingLength = 4; + + // TODO: If we have been transmitting a beacon, we probably want to continue transmitting + // for a few seconds after both hashes are zero so that that any devices scanning + // can see the beacon indicating we have stopped all browses and advertisements. + + // Stop the beacon if there is no data to advertise. + if (browseHash == 0 && registeredHash == 0) + { + LogInfo("advertiseBrowses:andRegistrations Stopping beacon in %d seconds", LastBeaconTime); + if (mDNSStorage.NextBLEServiceTime) + LogInfo("advertiseBrowses:andRegistrations: NextBLEServiceTime already set ??"); + + mDNSStorage.NextBLEServiceTime = NonZeroTime(mDNS_TimeNow_NoLock(& mDNSStorage) + LastBeaconTime * mDNSPlatformOneSecond); + } + else + { + mDNSStorage.NextBLEServiceTime = 0; + } + + // The beacon type. + advertisingData[advertisingLength++] = DBDeviceTypeBonjour; + + // Flags and Version field + advertisingData[advertisingLength++] = BonjourBLEVersion; + + memcpy(& advertisingData[advertisingLength], & browseHash, sizeof(serviceHash_t)); + advertisingLength += sizeof(serviceHash_t); + memcpy(& advertisingData[advertisingLength], & registeredHash, sizeof(serviceHash_t)); + advertisingLength += sizeof(serviceHash_t); + + + // Add the MAC address of the awdl0 interface. Don't cache it since + // it can get updated periodically. + if (AWDLInterfaceID) + { + NetworkInterfaceInfoOSX *intf = IfindexToInterfaceInfoOSX(& mDNSStorage, AWDLInterfaceID); + if (intf) + memcpy(& advertisingData[advertisingLength], & intf->ifinfo.MAC, sizeof(mDNSEthAddr)); + else + memset( & advertisingData[advertisingLength], 0, sizeof(mDNSEthAddr)); + } + else + { + // just use zero if not avaiblable + memset( & advertisingData[advertisingLength], 0, sizeof(mDNSEthAddr)); + } + advertisingLength += sizeof(mDNSEthAddr); + + // Total length of data advertised, minus this lenght byte. + advertisingData[0] = (advertisingLength - 1); + + LogInfo("advertiseBrowses:andRegistrations advertisingLength = %d", advertisingLength); + + NSData* data = [NSData dataWithBytes:advertisingData length:advertisingLength]; + + if([_peripheralManager isAdvertising] && [data isEqualToData: _currentlyAdvertisedData]) + { + // No need to restart the advertisement if it is already active with the same data. + LogInfo("advertiseBrowses:andRegistrations: No change in advertised data"); + } + else + { + _currentlyAdvertisedData = data; + + if ([_peripheralManager isAdvertising]) + { + LogInfo("advertiseBrowses:andRegistrations: Stop current advertisement before restarting"); + [_peripheralManager stopAdvertising]; + } + LogInfo("advertiseBrowses:andRegistrations: Starting beacon"); + + [_peripheralManager startAdvertising:@{ CBAdvertisementDataAppleMfgData : _currentlyAdvertisedData, CBCentralManagerScanOptionIsPrivilegedDaemonKey : @YES, @"kCBAdvOptionUseFGInterval" : @YES }]; + } +} + +- (void) stopBeacon +{ + [_peripheralManager stopAdvertising]; +} + +- (void) updateScan:(bool) start +{ + if (_isScanning) + { + if (!start) + { + LogInfo("updateScan: stopping scan"); + [_centralManager stopScan]; + _isScanning = NO; + } + } + else + { + if (start) + { + LogInfo("updateScan: starting scan"); + + _isScanning = YES; + [_centralManager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @NO }]; + } + } +} + +#pragma mark - CBCentralManagerDelegate protocol + +- (void)centralManagerDidUpdateState:(CBCentralManager *)central +{ + switch (central.state) { + case CBCentralManagerStateUnknown: + LogInfo("centralManagerDidUpdateState: CBCentralManagerStateUnknown"); + break; + + case CBCentralManagerStateResetting: + LogInfo("centralManagerDidUpdateState: CBCentralManagerStateResetting"); + break; + + case CBCentralManagerStateUnsupported: + LogInfo("centralManagerDidUpdateState: CBCentralManagerStateUnsupported"); + break; + + case CBCentralManagerStateUnauthorized: + LogInfo("centralManagerDidUpdateState: CBCentralManagerStateUnauthorized"); + break; + + case CBCentralManagerStatePoweredOff: + LogInfo("centralManagerDidUpdateState: CBCentralManagerStatePoweredOff"); + break; + + case CBCentralManagerStatePoweredOn: + LogInfo("centralManagerDidUpdateState: CBCentralManagerStatePoweredOn"); + break; + + default: + LogInfo("centralManagerDidUpdateState: Unknown state ??"); + break; + } +} + +// offset of beacon type in recieved CBAdvertisementDataManufacturerDataKey bytes +#define beaconTypeByteIndex 2 + +- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI +{ + (void) central; + (void) peripheral; + (void) RSSI; + + NSData *data = [advertisementData objectForKey:CBAdvertisementDataManufacturerDataKey]; + + if (!data) { + return; + } + + unsigned char *bytes = (unsigned char *)data.bytes; + + // Just parse the DBDeviceTypeBonjour beacons. + if (bytes[beaconTypeByteIndex] == DBDeviceTypeBonjour) + { + serviceHash_t browseHash, registeredHash; + mDNSEthAddr senderMAC; + unsigned char flagsAndVersion; + unsigned char *ptr; + +#if VERBOSE_BLE_DEBUG + LogInfo("didDiscoverPeripheral: received DBDeviceTypeBonjour beacon, length = %d", [data length]); + LogInfo("didDiscoverPeripheral: central = 0x%x, peripheral = 0x%x", central, peripheral); +#endif // VERBOSE_BLE_DEBUG + + // The DBDeviceTypeBonjour beacon bytes will be: + // x4C, 0x0, 0x2A, flags_and_version_byte,, browseHash, advertisingServices_hash_bytes, + // 6_bytes_of_sender_AWDL_MAC_address + + ptr = & bytes[beaconTypeByteIndex + 1]; + flagsAndVersion = *ptr++; + memcpy(& browseHash, ptr, sizeof(serviceHash_t)); + ptr += sizeof(serviceHash_t); + memcpy(& registeredHash, ptr, sizeof(serviceHash_t)); + ptr += sizeof(serviceHash_t); + memcpy(& senderMAC, ptr, sizeof(senderMAC)); + +#if VERBOSE_BLE_DEBUG + LogInfo("didDiscoverPeripheral: version = 0x%x, browseHash = 0x%x, registeredHash = 0x%x", + flagsAndVersion, browseHash, registeredHash); + LogInfo("didDiscoverPeripheral: sender MAC = 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + senderMAC.b[0], senderMAC.b[1], senderMAC.b[2], senderMAC.b[3], senderMAC.b[4], senderMAC.b[5]); +#else + (void)flagsAndVersion; // Unused +#endif // VERBOSE_BLE_DEBUG + + responseReceived(browseHash, registeredHash, & senderMAC); + +#if VERBOSE_BLE_DEBUG + // Log every 4th package during debug + static int pkgsIn = 0; + + if (((pkgsIn++) & 3) == 0) + { + LogInfo("0x%x 0x%x 0x%x 0x%x 0x%x", bytes[0], bytes[1], bytes[2], bytes[3], bytes[4]); +// LogInfo("0x%x 0x%x 0x%x 0x%x 0x%x", bytes[5], bytes[6], bytes[7], bytes[9], bytes[9]); +// LogInfo("0x%x 0x%x 0x%x 0x%x 0x%x", bytes[10], bytes[11], bytes[12], bytes[13], bytes[14]); +// LogInfo("0x%x 0x%x 0x%x 0x%x 0x%x", bytes[15], bytes[16], bytes[17], bytes[18], bytes[19]); + } +#endif // VERBOSE_BLE_DEBUG + + } +} + +#pragma mark - CBPeripheralManagerDelegate protocol + +- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral +{ + + switch (peripheral.state) { + case CBPeripheralManagerStateUnknown: + LogInfo("peripheralManagerDidUpdateState: CBPeripheralManagerStateUnknown"); + break; + + case CBPeripheralManagerStateResetting: + LogInfo("peripheralManagerDidUpdateState: CBPeripheralManagerStateResetting"); + break; + + case CBPeripheralManagerStateUnsupported: + LogInfo("peripheralManagerDidUpdateState: CBPeripheralManagerStateUnsupported"); + break; + + case CBPeripheralManagerStateUnauthorized: + LogInfo("peripheralManagerDidUpdateState: CBPeripheralManagerStateUnauthorized"); + break; + + case CBPeripheralManagerStatePoweredOff: + LogInfo("peripheralManagerDidUpdateState: CBPeripheralManagerStatePoweredOff"); + break; + + case CBPeripheralManagerStatePoweredOn: + LogInfo("peripheralManagerDidUpdateState: CBPeripheralManagerStatePoweredOn"); + break; + + default: + LogInfo("peripheralManagerDidUpdateState: Unknown state ??"); + break; + } +} + +- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error +{ + (void) peripheral; + + if (error) + { + const char * errorString = [[error localizedDescription] cStringUsingEncoding:NSASCIIStringEncoding]; + LogInfo("peripheralManagerDidStartAdvertising: error = %s", errorString ? errorString: "unknown"); + } + else + { + LogInfo("peripheralManagerDidStartAdvertising:"); + } +} + +@end diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 1b257ea..f3f00ca 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -13,7 +13,6 @@ * 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 @@ -26,20 +25,14 @@ #include #include #include // for launch_socket_service_check_in() -#include #include #include #include #include #include -#include -#include #include #include -#include // for bootstrap_check_in() - -#include "DNSServiceDiscoveryRequestServer.h" -#include "DNSServiceDiscoveryReply.h" +#include #include "uDNS.h" #include "DNSCommon.h" @@ -47,18 +40,30 @@ #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) +#include "Metrics.h" +#endif + +#if APPLE_OSX_mDNSResponder +static os_log_t log_general = NULL; +#endif + + +// Used on OSX(10.11.x onwards) for manipulating mDNSResponder program arguments +#if APPLE_OSX_mDNSResponder +// plist file to read the user's preferences +#define kProgramArguments CFSTR("com.apple.mDNSResponder") +// possible arguments for external customers +#define kPreferencesKey_DebugLogging CFSTR("DebugLogging") +#define kPreferencesKey_UnicastPacketLogging CFSTR("UnicastPacketLogging") +#define kPreferencesKey_AlwaysAppendSearchDomains CFSTR("AlwaysAppendSearchDomains") +#define kPreferencesKey_NoMulticastAdvertisements CFSTR("NoMulticastAdvertisements") +#define kPreferencesKey_StrictUnicastOrdering CFSTR("StrictUnicastOrdering") +#define kPreferencesKey_OfferSleepProxyService CFSTR("OfferSleepProxyService") +#define kPreferencesKey_UseInternalSleepProxy CFSTR("UseInternalSleepProxy") +#define kPreferencesKey_EnableBLEBasedDiscovery CFSTR("EnableBLEBasedDiscovery") #endif //************************************************************************************************************* @@ -68,131 +73,29 @@ static aslmsg log_msg = NULL; 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)) +// Start off with a default cache of 32K (141 records of 232 bytes each) +// Each time we grow the cache we add another 141 records +// 141 * 164 = 32712 bytes. +// This fits in eight 4kB pages, with 56 bytes spare for memory block headers and similar overhead +#define RR_CACHE_SIZE ((32*1024) / sizeof(CacheRecord)) static CacheEntity rrcachestorage[RR_CACHE_SIZE]; +struct CompileTimeAssertionChecks_RR_CACHE_SIZE { char a[(RR_CACHE_SIZE >= 141) ? 1 : -1]; }; -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 size_t launchd_fds_count = 0; -static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast +static mDNSBool NoMulticastAdvertisements = mDNSfalse; // 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; +extern mDNSBool EnableBLEBasedDiscovery; // We keep a list of client-supplied event sources in KQSocketEventSource records typedef struct KQSocketEventSource @@ -214,49 +117,19 @@ static KQSocketEventSource *gEventSources; 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) { +#if BONJOUR_ON_DEMAND + mDNSu32 NumAllInterfaceRecords = 0; + mDNSu32 NumAllInterfaceQuestions = 0; +#endif // BONJOUR_ON_DEMAND + // 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(); @@ -269,11 +142,19 @@ mDNSlocal void validatelists(mDNS *const m) 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); +#if BONJOUR_ON_DEMAND + if (!AuthRecord_uDNS(rr) && !RRLocalOnly(rr)) NumAllInterfaceRecords++; +#endif // BONJOUR_ON_DEMAND } 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); +#if BONJOUR_ON_DEMAND + if (!AuthRecord_uDNS(rr) && !RRLocalOnly(rr)) NumAllInterfaceRecords++; +#endif // BONJOUR_ON_DEMAND + } rr = m->NewLocalRecords; if (rr) @@ -287,8 +168,16 @@ mDNSlocal void validatelists(mDNS *const m) 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); + if (q->DuplicateOf && q->LocalSocket) + LogMemCorruption("Questions list: Duplicate Question %p should not have LocalSocket set %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); +#if BONJOUR_ON_DEMAND + if (!LocalOnlyOrP2PInterface(q->InterfaceID) && mDNSOpaque16IsZero(q->TargetQID)) + NumAllInterfaceQuestions++; +#endif // BONJOUR_ON_DEMAND + } CacheGroup *cg; CacheRecord *cr; @@ -317,6 +206,14 @@ mDNSlocal void validatelists(mDNS *const m) 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]); + +#if BONJOUR_ON_DEMAND + if (m->NumAllInterfaceRecords != NumAllInterfaceRecords) + LogMemCorruption("NumAllInterfaceRecords is %d should be %d", m->NumAllInterfaceRecords, NumAllInterfaceRecords); + + if (m->NumAllInterfaceQuestions != NumAllInterfaceQuestions) + LogMemCorruption("NumAllInterfaceQuestions is %d should be %d", m->NumAllInterfaceQuestions, NumAllInterfaceQuestions); +#endif // BONJOUR_ON_DEMAND } mDNSexport void *mallocL(char *msg, unsigned int size) @@ -327,8 +224,8 @@ mDNSexport void *mallocL(char *msg, unsigned int size) { 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]); + if (size > 32768) 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); @@ -345,11 +242,12 @@ mDNSexport void freeL(char *msg, void *x) 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]); + if (mem[0] == 0xDEADDEAD) { LogMemCorruption("free( %s : %lu @ %p ) !!!! ALREADY DISPOSED !!!!", msg, mem[1], &mem[2]); return; } + if (mem[0] != 0xDEAD1234) { LogMemCorruption("free( %s : %lu @ %p ) !!!! NEVER ALLOCATED !!!!", msg, mem[1], &mem[2]); return; } + if (mem[1] > 32768) LogMsg("free( %s : %lu @ %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]); + mem[0] = 0xDEADDEAD; + memset(mem+2, 0xFF, mem[1]); validatelists(&mDNSStorage); free(mem); } @@ -358,610 +256,6 @@ mDNSexport void freeL(char *msg, void *x) #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; iinfo->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) @@ -969,274 +263,6 @@ 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; @@ -1272,6 +298,8 @@ mDNSlocal void mDNSPreferencesSetNames(mDNS *const m, int key, domainlabel *old, !SameDomainLabelCS(old->c, prevold->c) || !SameDomainLabelCS(new->c, prevnew->c)) { +// Work around bug radar:21397654 +#ifndef __clang_analyzer__ if (old) *prevold = *old; else @@ -1280,6 +308,7 @@ mDNSlocal void mDNSPreferencesSetNames(mDNS *const m, int key, domainlabel *old, *prevnew = *new; else prevnew->c[0] = 0; +#endif mDNSPreferencesSetName(key, old, new); } else @@ -1305,258 +334,42 @@ mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) 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 s", not "Zero or more 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) + } + else if (result == mStatus_NameConflict) { - ExtraResourceRecord *e; - for (e = si->srs.Extras; e; e = e->next) + 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) { - if ((natural_t)e->ClientID == reference) - { - err = RemoveRecord(&si->srs, e, client); - break; - } + // Tell the helper we've given up + mDNSPreferencesSetNames(m, kmDNSLocalHostName, &m->p->userhostlabel, NULL); } - if (!e) { err = mStatus_BadReferenceErr; errormsg = "No such reference"; goto fail; } } + else if (result == mStatus_GrowCache) + { + // Allocate another chunk of cache storage + static unsigned int allocated = 0; +#if TARGET_OS_IPHONE + if (allocated >= 1000000) return; // For now we limit the cache to at most 1MB on iOS devices +#endif + allocated += sizeof(CacheEntity) * RR_CACHE_SIZE; + // LogMsg("GrowCache %d * %d = %d; total so far %6u", sizeof(CacheEntity), RR_CACHE_SIZE, sizeof(CacheEntity) * RR_CACHE_SIZE, allocated); + 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); - return mStatus_NoError; - -fail: - LogMsg("%5d: DNSServiceRegistrationRemoveRecord(%X) failed: %s (%d)", client, reference, errormsg, err); - return(err); + // Then we call into the UDS daemon code, to let it do the same + udsserver_handle_configchange(m); + } } + //************************************************************************************************************* #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -1568,16 +381,6 @@ 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"); @@ -1587,99 +390,6 @@ mDNSlocal void ExitCallback(int sig) #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) { @@ -1705,21 +415,25 @@ mDNSlocal void HandleSIG(int sig) #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM -mDNSlocal void INFOCallback(void) +mDNSexport void INFOCallback(void) { mDNSs32 utc = mDNSPlatformUTC(); + const mDNSs32 now = mDNS_TimeNow(&mDNSStorage); 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); + + LogMsgNoIdent("----- Platform Timers -----"); + LogTimer("m->NextCacheCheck ", mDNSStorage.NextCacheCheck); + LogTimer("m->NetworkChanged ", mDNSStorage.NetworkChanged); + LogTimer("m->p->NotifyUser ", mDNSStorage.p->NotifyUser); + LogTimer("m->p->HostNameConflict ", mDNSStorage.p->HostNameConflict); + LogTimer("m->p->KeyChainTimer ", mDNSStorage.p->KeyChainTimer); + xpcserver_info(&mDNSStorage); LogMsgNoIdent("----- KQSocketEventSources -----"); @@ -1773,21 +487,17 @@ mDNSlocal void INFOCallback(void) 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", + LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %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)); @@ -1800,72 +510,40 @@ mDNSlocal void INFOCallback(void) 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) + LogMsgNoIdent("------------ Hostnames -------------"); + if (!mDNSStorage.Hostnames) LogMsgNoIdent(""); + else { - 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. + HostnameInfo *hi; + for (hi = mDNSStorage.Hostnames; hi; hi = hi->next) + { + LogMsgNoIdent("%##s v4 %d %s", hi->fqdn.c, hi->arv4.state, ARDisplayString(&mDNSStorage, &hi->arv4)); + LogMsgNoIdent("%##s v6 %d %s", hi->fqdn.c, hi->arv6.state, ARDisplayString(&mDNSStorage, &hi->arv6)); + } } + + LogMsgNoIdent("--------------- FQDN ---------------"); + if (!mDNSStorage.FQDN.c[0]) LogMsgNoIdent(""); 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. + LogMsgNoIdent("%##s", mDNSStorage.FQDN.c); } + +#if TARGET_OS_EMBEDDED + LogMetrics(); +#endif + LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); + LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); } + 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); + if (!log_general) + os_log_error(OS_LOG_DEFAULT, "Could NOT create log handle in init_logging()"); + else + os_log_with_type(log_general, log_level, "%s", buffer); + } // Writes the state out to the dynamic store and also affects the ASL filter level @@ -1919,35 +597,9 @@ mDNSexport void UpdateDebugState() 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 @@ -1963,10 +615,6 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void 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; @@ -1984,15 +632,26 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void case SIGINT: case SIGTERM: ExitCallback(msg_header->msgh_id); break; case SIGINFO: INFOCallback(); break; - case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; + case SIGUSR1: +#if APPLE_OSX_mDNSResponder + mDNS_LoggingEnabled = 1; + LogMsg("SIGUSR1: Logging %s on Apple Platforms", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); +#else + mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); +#endif 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"); + LogInfo("USR1 Logging Enabled"); break; - case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; + case SIGUSR2: +#if APPLE_OSX_mDNSResponder + mDNS_PacketLoggingEnabled = 1; + LogMsg("SIGUSR2: Packet Logging %s on Apple Platforms", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); +#else + mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); +#endif mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse; LogInfo("SIGUSR2: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); UpdateDebugState(); @@ -2018,37 +677,24 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void 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, + !NoMulticastAdvertisements, 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); + if (err) + { + LogMsg("Daemon start: mDNS_Init failed %d", err); + return(err); + } 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); + CFRunLoopAddSource(CFRunLoopGetMain(), i_rls, kCFRunLoopDefaultMode); CFRelease(i_rls); - - if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); + return(err); } @@ -2115,28 +761,21 @@ mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int 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, + !NoMulticastAdvertisements, 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); + if (err) + { + LogMsg("Daemon start: mDNS_Init failed %d", err); + return(err); + } mDNSSetupSignal(queue, SIGHUP); mDNSSetupSignal(queue, SIGINT); @@ -2144,7 +783,6 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) 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 @@ -2169,7 +807,6 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) LogMsg("DaemonIntialize done successfully"); - if (mDNS_DebugMode) printf("Service registered with Mach Port %d\n", m_port); return(err); } @@ -2197,16 +834,20 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) // 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->NetworkChanged && now - m->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m); - if (m->p->RequestReSleep && now - m->p->RequestReSleep >= 0) { m->p->RequestReSleep = 0; mDNSPowerRequest(0, 0); } + 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->NetworkChanged) + if (nextevent - m->NetworkChanged > 0) + nextevent = m->NetworkChanged; if (m->p->KeyChainTimer) if (nextevent - m->p->KeyChainTimer > 0) @@ -2216,61 +857,7 @@ mDNSlocal mDNSs32 mDNSDaemonIdle(mDNS *const m) 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) @@ -2411,6 +998,7 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) //interval = 48; // For testing +#if !TARGET_OS_EMBEDDED #ifdef kIOPMAcknowledgmentOptionSystemCapabilityRequirements if (m->p->IOPMConnection) // If lightweight-wake capability is available, use that { @@ -2423,7 +1011,7 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) if (!Requirements) LogMsg("ScheduleNextWake: CFNumberCreate failed"); else { - const void *OptionKeys[2] = { CFSTR("WakeDate"), CFSTR("Requirements") }; + const void *OptionKeys[2] = { kIOPMAckDHCPRenewWakeDate, kIOPMAckSystemCapabilityRequirements }; const void *OptionVals[2] = { WakeDate, Requirements }; opts = CFDictionaryCreate(NULL, (void*)OptionKeys, (void*)OptionVals, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!opts) LogMsg("ScheduleNextWake: CFDictionaryCreate failed"); @@ -2434,13 +1022,15 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) LogSPS("AllowSleepNow: Will request lightweight wakeup in %d seconds", interval); } else // else schedule the wakeup using the old API instead to -#endif +#endif // kIOPMAcknowledgmentOptionSystemCapabilityRequirements +#endif // TARGET_OS_EMBEDDED { // 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; + if (interval < 60) + interval = 60; result = mDNSPowerRequest(1, interval); @@ -2594,9 +1184,9 @@ mDNSlocal void KQWokenFlushBytes(int fd, __unused short filter, __unused void *c mDNSlocal void SetLowWater(const KQSocketSet *const k, const int r) { - if (setsockopt(k->sktv4, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0) + if (k->sktv4 >=0 && setsockopt(k->sktv4, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0) LogMsg("SO_RCVLOWAT IPv4 %d error %d errno %d (%s)", k->sktv4, r, errno, strerror(errno)); - if (setsockopt(k->sktv6, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0) + if (k->sktv6 >=0 && setsockopt(k->sktv6, SOL_SOCKET, SO_RCVLOWAT, &r, sizeof(r)) < 0) LogMsg("SO_RCVLOWAT IPv6 %d error %d errno %d (%s)", k->sktv6, r, errno, strerror(errno)); } @@ -2634,6 +1224,10 @@ mDNSlocal void * KQueueLoop(void *m_param) if (end - start >= WatchDogReportingThreshold) LogInfo("WARNING: Idle task took %dms to complete", end - start); +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 + validatelists(m); +#endif + mDNSs32 now = mDNS_TimeNow(m); if (m->ShutdownTime) @@ -2758,99 +1352,134 @@ mDNSlocal void * KQueueLoop(void *m_param) #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM -mDNSlocal void LaunchdCheckin(void) +mDNSlocal size_t 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; + int result = launch_activate_socket("Listeners", &launchd_fds, &launchd_fds_count); + if (result != 0) { LogMsg("launch_activate_socket() failed errno %d (%s)", errno, strerror(errno)); } + return launchd_fds_count; +} + + +extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import)); + +#if APPLE_OSX_mDNSResponder +mDNSlocal mDNSBool PreferencesGetValueBool(CFStringRef key, mDNSBool defaultValue) +{ + CFBooleanRef boolean; + mDNSBool result = defaultValue; + + boolean = CFPreferencesCopyAppValue(key, kProgramArguments); + if (boolean) + { + if (CFGetTypeID(boolean) == CFBooleanGetTypeID()) + result = CFBooleanGetValue(boolean) ? mDNStrue : mDNSfalse; + CFRelease(boolean); + } + + return result; +} + +mDNSlocal int PreferencesGetValueInt(CFStringRef key, int defaultValue) +{ + CFNumberRef number; + int numberValue; + int result = defaultValue; + + number = CFPreferencesCopyAppValue(key, kProgramArguments); + if (number) + { + if ((CFGetTypeID(number) == CFNumberGetTypeID()) && CFNumberGetValue(number, kCFNumberIntType, &numberValue)) + result = numberValue; + CFRelease(number); } + + return result; +} +#endif + +mDNSlocal void SandboxProcess(void) +{ + // 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 { - 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 + char *sandbox_msg; + uint64_t sandbox_flags = SANDBOX_NAMED; + + (void)confstr(_CS_DARWIN_USER_CACHE_DIR, NULL, 0); + + int sandbox_err = sandbox_init("mDNSResponder", sandbox_flags, &sandbox_msg); + if (sandbox_err) { - 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); - } - } + 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"); } - launch_data_free(resp_sd); +#endif // MDNS_NO_SANDBOX } -static mach_port_t RegisterMachService(const char *service_name) +#if APPLE_OSX_mDNSResponder +mDNSlocal void init_logging(void) { - 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; - } + log_general = os_log_create("com.apple.mDNSResponder", "AllINFO"); - 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; + if (!log_general) + { + // OS_LOG_DEFAULT is the default logging object, if you are not creating a custom subsystem/category + os_log_error(OS_LOG_DEFAULT, "Could NOT create log handle in mDNSResponder"); + } } +#endif -extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import)); +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR +mDNSlocal mDNSBool initialize_networkserviceproxy(void) +{ + void *NSPImage = dlopen("/System/Library/PrivateFrameworks/NetworkServiceProxy.framework/NetworkServiceProxy", RTLD_LAZY | RTLD_LOCAL); + if (NSPImage == NULL) { + os_log_error(OS_LOG_DEFAULT, "dlopen NetworkServiceProxy.framework failed"); + return mDNSfalse; + } + return mDNStrue; +} +#endif mDNSexport int main(int argc, char **argv) { int i; kern_return_t status; +#if DEBUG + bool useDebugSocket = mDNSfalse; + bool useSandbox = mDNStrue; +#endif + +#if APPLE_OSX_mDNSResponder + init_logging(); +#endif + 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); + LogMsg("CacheRecord %5d", sizeof(CacheRecord)); + LogMsg("CacheGroup %5d", sizeof(CacheGroup)); + LogMsg("ResourceRecord %5d", sizeof(ResourceRecord)); + LogMsg("RData_small %5d", sizeof(RData_small)); + + LogMsg("sizeof(CacheEntity) %5d", sizeof(CacheEntity)); + LogMsg("RR_CACHE_SIZE %5d", RR_CACHE_SIZE); + LogMsg("block bytes used %5d", sizeof(CacheEntity) * RR_CACHE_SIZE); + LogMsg("block bytes wasted %5d", 32*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE); #endif if (0 == geteuid()) @@ -2862,7 +1491,7 @@ mDNSexport int main(int argc, char **argv) for (i=1; i com.apple.mDNSResponder.dnsproxy + com.apple.mDNSResponder.dnsctl + diff --git a/mDNSMacOSX/helper-error.h b/mDNSMacOSX/helper-error.h deleted file mode 100644 index 2e463b0..0000000 --- a/mDNSMacOSX/helper-error.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -*- 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/mDNSMacOSX/helper-main.c b/mDNSMacOSX/helper-main.c index 52779e8..9c61e48 100644 --- a/mDNSMacOSX/helper-main.c +++ b/mDNSMacOSX/helper-main.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007 Apple Inc. All rights reserved. + * 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. @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -37,9 +36,7 @@ #include #include "helper.h" #include "helper-server.h" -#include "helpermsg.h" -#include "helpermsgServer.h" -#include +#include #if TARGET_OS_EMBEDDED #define NO_SECURITYFRAMEWORK 1 @@ -51,14 +48,11 @@ #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; +int mDNSHelperLogEnabled = 0; +os_log_t log_handle = NULL; + +static dispatch_queue_t xpc_queue = NULL; static int opt_debug; static pthread_t idletimer_thread; @@ -68,45 +62,24 @@ 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 + // os_log_debug(log_handle,"entry sig=%d", sig); Can't use syslog from within a signal handler assert(sig == SIGTERM); - (void)proxy_mDNSExit(gPort); + helper_exit(); } 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)); + log_handle = os_log_create("com.apple.mDNSResponderHelper", "INFO"); + + if (!log_handle) + { + // OS_LOG_DEFAULT is the default logging object, if you are not creating a custom subsystem/category + os_log_error(OS_LOG_DEFAULT, "Could NOT create log handle in mDNSResponderHelper"); + } + } static void initialize_id(void) @@ -117,22 +90,29 @@ static void initialize_id(void) 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; } + if (!pwd) + { + os_log(log_handle, "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); + os_log_info(log_handle, "entry %p %p %lu", timer, context, actualidle); + assert(gTimer == timer); - if (maxidle) - (void)proxy_mDNSExit(gPort); + os_log_info(log_handle, "mDNSResponderHelper exiting after [%lu] seconds", actualidle); + + if (actualidle) + helper_exit(); } void pause_idle_timer(void) { - debug("entry"); + os_log_debug(log_handle,"entry"); assert(gTimer); assert(gRunLoop); CFRunLoopRemoveTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); @@ -140,7 +120,7 @@ void pause_idle_timer(void) void unpause_idle_timer(void) { - debug("entry"); + os_log_debug(log_handle,"entry"); assert(gRunLoop); assert(gTimer); CFRunLoopAddTimer(gRunLoop, gTimer, kCFRunLoopDefaultMode); @@ -148,21 +128,21 @@ void unpause_idle_timer(void) void update_idle_timer(void) { - debug("entry"); + os_log_debug(log_handle,"entry"); assert(gTimer); CFRunLoopTimerSetNextFireDate(gTimer, CFAbsoluteTimeGetCurrent() + actualidle); } static void *idletimer(void *context) { - debug("entry context=%p", context); - gRunLoop = CFRunLoopGetCurrent(); + os_log_debug(log_handle,"entry context=%p", context); + gRunLoop = CFRunLoopGetMain(); unpause_idle_timer(); for (;;) { - debug("Running CFRunLoop"); + // os_log_debug(log_handle,"Running CFRunLoop"); CFRunLoopRun(); sleep(1); } @@ -174,111 +154,504 @@ static int initialize_timer() { gTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + actualidle, actualidle, 0, 0, diediedie, NULL); int err = 0; + os_log_info(log_handle, "mDNSResponderHelper initialize_timer() started"); - debug("entry"); if (0 != (err = pthread_create(&idletimer_thread, NULL, idletimer, NULL))) - helplog(ASL_LEVEL_ERR, "Could not start idletimer thread: %s", strerror(err)); + os_log(log_handle, "Could not start idletimer thread: %s", strerror(err)); return err; } -static mach_port_t register_service(const char *service_name) +/* + Reads the user's program arguments for mDNSResponderHelper + For now we have only one option: mDNSHelperDebugLogging which is used to turn on mDNSResponderHelperLogging + + To turn ON mDNSResponderHelper Verbose Logging, + 1] sudo defaults write /Library/Preferences/com.apple.mDNSResponderHelper.plist mDNSHelperDebugLogging -bool YES + 2] sudo reboot + + To turn OFF mDNSResponderHelper Logging, + 1] sudo defaults delete /Library/Preferences/com.apple.mDNSResponderHelper.plist + + To view the current options set, + 1] plutil -p /Library/Preferences/com.apple.mDNSResponderHelper.plist + OR + 1] sudo defaults read /Library/Preferences/com.apple.mDNSResponderHelper.plist +*/ + +static mDNSBool HelperPrefsGetValueBool(CFStringRef key, mDNSBool defaultVal) { - mach_port_t port = MACH_PORT_NULL; - kern_return_t kr; + CFBooleanRef boolean; + mDNSBool result = defaultVal; + + boolean = CFPreferencesCopyAppValue(key, kmDNSHelperProgramArgs); + if (boolean) + { + if (CFGetTypeID(boolean) == CFBooleanGetTypeID()) + result = CFBooleanGetValue(boolean) ? mDNStrue : mDNSfalse; + CFRelease(boolean); + } + + return result; +} + - if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) +// Verify Client's Entitlement +static mDNSBool check_entitlement(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 { - helplog(ASL_LEVEL_ERR, "bootstrap_check_in: %d %X %s", kr, kr, mach_error_string(kr)); - return MACH_PORT_NULL; + os_log(log_handle, "client entitlement is NULL"); } - if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + if (!entitled) + os_log(log_handle, "entitlement check failed -> client is missing entitlement!"); + + return entitled; +} + + +static void handle_request(xpc_object_t req) +{ + mDNSu32 helper_mode = 0; + int error_code = 0; + + xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req); + xpc_object_t response = xpc_dictionary_create_reply(req); + + // switch here based on dictionary to handle different requests from mDNSResponder + if ((xpc_dictionary_get_uint64(req, kHelperMode))) { - 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; + os_log_info(log_handle, "Getting mDNSResponder request mode"); + helper_mode = (mDNSu32)(xpc_dictionary_get_uint64(req, kHelperMode)); } + + switch (helper_mode) + { + case bpf_request: + { + os_log_info(log_handle, "Calling new RequestBPF()"); + RequestBPF(); + break; + } + + case set_name: + { + const char *old_name; + const char *new_name; + int pref_key = 0; + + pref_key = (int)(xpc_dictionary_get_uint64(req, kPrefsNameKey)); + old_name = xpc_dictionary_get_string(req, kPrefsOldName); + new_name = xpc_dictionary_get_string(req, kPrefsNewName); + + os_log_info(log_handle, "Calling new SetName() oldname: %s newname: %s key:%d", old_name, new_name, pref_key); + PreferencesSetName(pref_key, old_name, new_name); + break; + } + + case p2p_packetfilter: + { + pfArray_t pfports; + pfArray_t pfprotocols; + const char *if_name; + uint32_t cmd; + uint32_t count; + + cmd = xpc_dictionary_get_uint64(req, "pf_opcode"); + if_name = xpc_dictionary_get_string(req, "pf_ifname"); + count = xpc_dictionary_get_uint64(req, "pf_count"); + + pfports[0] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port0"); + pfports[1] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port1"); + pfports[2] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port2"); + pfports[3] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_port3"); + + pfprotocols[0] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol0"); + pfprotocols[1] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol1"); + pfprotocols[2] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol2"); + pfprotocols[3] = (uint16_t)xpc_dictionary_get_uint64(req, "pf_protocol3"); + + os_log_info(log_handle,"Calling new PacketFilterControl()"); + PacketFilterControl(cmd, if_name, count, pfports, pfprotocols); + break; + } + + case user_notify: + { + const char *title; + const char *msg; + title = xpc_dictionary_get_string(req, "notify_title"); + msg = xpc_dictionary_get_string(req, "notify_msg"); + + os_log_info(log_handle,"Calling new UserNotify() title:%s msg:%s", title, msg); + UserNotify(title, msg); + break; + } + + case power_req: + { + int key, interval; + key = xpc_dictionary_get_uint64(req, "powerreq_key"); + interval = xpc_dictionary_get_uint64(req, "powerreq_interval"); + + os_log_info(log_handle,"Calling new PowerRequest() key[%d] interval[%d]", key, interval); + PowerRequest(key, interval, &error_code); + break; + } + + case send_wakepkt: + { + const char *ether_addr; + const char *ip_addr; + int iteration; + unsigned int if_id; + + if_id = (unsigned int)xpc_dictionary_get_uint64(req, "interface_index"); + ether_addr = xpc_dictionary_get_string(req, "ethernet_address"); + ip_addr = xpc_dictionary_get_string(req, "ip_address"); + iteration = (int)xpc_dictionary_get_uint64(req, "swp_iteration"); + + os_log_info(log_handle, "Calling new SendWakeupPacket() ether_addr[%s] ip_addr[%s] if_id[%d] iteration[%d]", + ether_addr, ip_addr, if_id, iteration); + SendWakeupPacket(if_id, ether_addr, ip_addr, iteration); + break; + } + + case set_localaddr_cacheentry: + { + int if_index, family; + + if_index = xpc_dictionary_get_uint64(req, "slace_ifindex"); + family = xpc_dictionary_get_uint64(req, "slace_family"); + + const uint8_t* ip = xpc_dictionary_get_data(req, "slace_ip", NULL); + const uint8_t* eth = xpc_dictionary_get_data(req, "slace_eth", NULL); + + os_log_info(log_handle, "Calling new SetLocalAddressCacheEntry() if_index[%d] family[%d] ", if_index, family); + + SetLocalAddressCacheEntry(if_index, family, ip, eth, &error_code); + + /* + static int v6addr_to_string(const v6addr_t addr, char *buf, size_t buflen) + { + if (NULL == inet_ntop(AF_INET6, addr, buf, buflen)) + { + os_log(log_handle, "inet_ntop failed: %s", strerror(errno)); + return -1; + } + else + { + return 0; + } + } + + ethaddr_t eth = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } ; + const uint8_t* slace_ip = NULL; + v6addr_t addr_ipv6; + size_t ip_len; + slace_ip = xpc_dictionary_get_data(req, "slace_ip", &ip_len); + if (slace_ip && (ip_len == sizeof(v6addr_t))) + { + os_log(log_handle, "mDNSResponderHelper: doing memcpy()"); + memcpy(&addr_ipv6, slace_ip, ip_len); + } + char test_ipv6_str[46]; + v6addr_to_string(addr_ipv6, test_ipv6_str, sizeof(test_ipv6_str)); + os_log(log_handle, "mDNSResponderHelper: handle_request: set_localaddr_cacheentry: test_ipv6_str is %s", test_ipv6_str); + */ + + break; + } + + case send_keepalive: + { + uint16_t lport, rport, win; + uint32_t seq, ack; + + lport = xpc_dictionary_get_uint64(req, "send_keepalive_lport"); + rport = xpc_dictionary_get_uint64(req, "send_keepalive_rport"); + seq = xpc_dictionary_get_uint64(req, "send_keepalive_seq"); + ack = xpc_dictionary_get_uint64(req, "send_keepalive_ack"); + win = xpc_dictionary_get_uint64(req, "send_keepalive_win"); + + const uint8_t* sadd6 = xpc_dictionary_get_data(req, "send_keepalive_sadd", NULL); + const uint8_t* dadd6 = xpc_dictionary_get_data(req, "send_keepalive_dadd", NULL); + + os_log_info(log_handle, "helper-main: handle_request: send_keepalive: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]", + lport, rport, seq, ack, win); + + SendKeepalive(sadd6, dadd6, lport, rport, seq, ack, win); + break; + } + + case retreive_tcpinfo: + { + uint16_t lport, rport; + int family; + uint32_t seq, ack; + uint16_t win; + int32_t intfid; + + lport = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_lport"); + rport = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_rport"); + family = xpc_dictionary_get_uint64(req, "retreive_tcpinfo_family"); + + const uint8_t* laddr = xpc_dictionary_get_data(req, "retreive_tcpinfo_laddr", NULL); + const uint8_t* raddr = xpc_dictionary_get_data(req, "retreive_tcpinfo_raddr", NULL); + + os_log_info(log_handle, "helper-main: handle_request: retreive_tcpinfo: lport is[%d] rport is[%d] family is [%d]", + lport, rport, family); + + RetrieveTCPInfo(family, laddr, lport, raddr, rport, &seq, &ack, &win, &intfid, &error_code); + + if (response) + { + xpc_dictionary_set_uint64(response, "retreive_tcpinfo_seq", seq); + xpc_dictionary_set_uint64(response, "retreive_tcpinfo_ack", ack); + xpc_dictionary_set_uint64(response, "retreive_tcpinfo_win", win); + xpc_dictionary_set_uint64(response, "retreive_tcpinfo_ifid", intfid); + } + + os_log_info(log_handle, "helper-main: handle_request: retreive_tcpinfo: seq is[%d] ack is[%d] win is [%d] intfid is [%d]", + seq, ack, win, intfid); - return port; + break; + } + + case autotunnel_setkeys: + { + uint16_t lport, rport; + int replace_del; + const char *fqdnstr; + + lport = xpc_dictionary_get_uint64(req, "autotunnelsetkeys_lport"); + rport = xpc_dictionary_get_uint64(req, "autotunnelsetkeys_rport"); + replace_del = xpc_dictionary_get_uint64(req, "autotunnelsetkeys_repdel"); + + const uint8_t* local_inner = xpc_dictionary_get_data(req, "autotunnelsetkeys_localinner", NULL); + const uint8_t* local_outer = xpc_dictionary_get_data(req, "autotunnelsetkeys_localouter", NULL); + const uint8_t* remote_inner = xpc_dictionary_get_data(req, "autotunnelsetkeys_remoteinner", NULL); + const uint8_t* remote_outer = xpc_dictionary_get_data(req, "autotunnelsetkeys_remoteouter", NULL); + + fqdnstr = xpc_dictionary_get_string(req, "autotunnelsetkeys_fqdnStr"); + + os_log_info(log_handle, "helper-main: handle_request: autotunnel_setkeys: lport is[%d] rport is[%d] replace_del is [%d]", + lport, rport, replace_del); + + + HelperAutoTunnelSetKeys(replace_del, local_inner, local_outer, lport, remote_inner, remote_outer, rport, fqdnstr, &error_code); + + break; + } + + + case keychain_getsecrets: + { + unsigned int num_sec = 0; + unsigned long secrets = 0; + unsigned int sec_cnt = 0; + + os_log_info(log_handle,"Calling new KeyChainGetSecrets()"); + + KeychainGetSecrets(&num_sec, &secrets, &sec_cnt, &error_code); + + if (response) + { + xpc_dictionary_set_uint64(response, "keychain_num_secrets", num_sec); + xpc_dictionary_set_data(response, "keychain_secrets", (void *)secrets, sec_cnt); + xpc_dictionary_set_uint64(response, "keychain_secrets_count", sec_cnt); + } + + os_log_info(log_handle,"helper-main: handle_request: keychain_getsecrets: num_secrets is %d, secrets is %lu, secrets_Cnt is %d", + num_sec, secrets, sec_cnt); + + if (secrets) + vm_deallocate(mach_task_self(), secrets, sec_cnt); + + break; + } + + default: + { + os_log(log_handle, "handle_request: Unrecognized mode!"); + error_code = kHelperErr_UndefinedMode; + break; + } + } + + // Return Response Status back to the client (essentially ACKing the request) + if (response) + { + xpc_dictionary_set_uint64(response, kHelperReplyStatus, kHelperReply_ACK); + xpc_dictionary_set_int64(response, kHelperErrCode, error_code); + xpc_connection_send_message(remote_conn, response); + xpc_release(response); + } + else + { + os_log(log_handle, "handle_requests: Response Dictionary could not be created!"); + return; + } + } +static void accept_client(xpc_connection_t conn) +{ + int c_pid = xpc_connection_get_pid(conn); + + if (!(check_entitlement(conn, kHelperService))) + { + os_log(log_handle, "accept_client: Helper Client PID[%d] is missing Entitlement. Cancelling connection", c_pid); + xpc_connection_cancel(conn); + return; + } + + xpc_retain(conn); + xpc_connection_set_target_queue(conn, xpc_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) + { + os_log_info(log_handle,"accept_client:conn:[%p] client[%d](mDNSResponder) requesting service", (void *) conn, c_pid); + handle_request(req_msg); + } + else // We hit this case ONLY if Client Terminated Connection OR Crashed + { + os_log(log_handle, "accept_client:conn:[%p] client[%d](mDNSResponder) teared down the connection (OR Crashed)", (void *) conn, c_pid); + // handle_termination(); + xpc_release(conn); + } + }); + + xpc_connection_resume(conn); +} + + +static void init_helper_service(const char *service_name) +{ + + xpc_connection_t xpc_listener = xpc_connection_create_mach_service(service_name, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); + if (!xpc_listener || xpc_get_type(xpc_listener) != XPC_TYPE_CONNECTION) + { + os_log(log_handle, "init_helper_service: Error Creating XPC Listener for mDNSResponderHelperService !!"); + return; + } + + os_log_info(log_handle,"init_helper_service: XPC Listener for mDNSResponderHelperService Listening"); + + xpc_queue = dispatch_queue_create("com.apple.mDNSHelper.service_queue", NULL); + + xpc_connection_set_event_handler(xpc_listener, ^(xpc_object_t eventmsg) + { + xpc_type_t type = xpc_get_type(eventmsg); + + if (type == XPC_TYPE_CONNECTION) + { + os_log_info(log_handle,"init_helper_service: new mDNSResponderHelper Client %p", eventmsg); + accept_client(eventmsg); + } + else if (type == XPC_TYPE_ERROR) // Ideally, we would never hit these cases below + { + os_log(log_handle, "init_helper_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION)); + return; + } + else + { + os_log(log_handle, "init_helper_service: Unknown EventMsg type"); + return; + } + }); + + xpc_connection_resume(xpc_listener); +} + + 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); + 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; + (void)ac; // Unused + (void)av; // Unused initialize_logging(); - helplog(ASL_LEVEL_INFO, "Starting"); initialize_id(); + mDNSHelperLogEnabled = HelperPrefsGetValueBool(kPreferencesKey_mDNSHelperLog, mDNSHelperLogEnabled); + +// Currently on Fuji/Whitetail releases we are keeping the logging always enabled. +// Hence mDNSHelperLogEnabled is set to true below by default. + mDNSHelperLogEnabled = 1; + + os_log_info(log_handle,"mDNSResponderHelper Starting to run"); + #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); + if (opt_debug) + SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); #endif - gPort = register_service(kmDNSHelperServiceName); - if (!gPort) - exit(EXIT_FAILURE); - if (maxidle) actualidle = maxidle; + 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 (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"); + os_log(log_handle, "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); + init_helper_service(kHelperService); + os_log_info(log_handle,"mDNSResponderHelper is now running"); + dispatch_main(); + } // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion diff --git a/mDNSMacOSX/helper-server.h b/mDNSMacOSX/helper-server.h index 1c391a0..8a34274 100644 --- a/mDNSMacOSX/helper-server.h +++ b/mDNSMacOSX/helper-server.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-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. @@ -18,14 +18,11 @@ #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/mDNSMacOSX/helper-stubs.c b/mDNSMacOSX/helper-stubs.c index 29fd9ae..a1e804f 100644 --- a/mDNSMacOSX/helper-stubs.c +++ b/mDNSMacOSX/helper-stubs.c @@ -1,5 +1,6 @@ -/* - * Copyright (c) 2007-2012 Apple Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2007-2015 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. @@ -22,9 +23,10 @@ #include #include "mDNSDebug.h" #include "helper.h" -#include "helpermsg.h" #include #include +#include +#include // // Implementation Notes about the HelperQueue: @@ -39,67 +41,176 @@ // an argument to the function, the blocks can reference them as they are passed in as pointers. But care should // be taken to copy them locally as they may cease to exist when the function returns. // + + +//************************************************************************************************************* +// Globals static dispatch_queue_t HelperQueue; +static xpc_connection_t helper_xpc_conn = NULL; + +static int64_t maxwait_secs = 5LL; -#define ERROR(x, y) y, -static const char *errorstring[] = +#define mDNSHELPER_DEBUG LogOperation + +//************************************************************************************************************* +// Utility Functions + +static void LogDebug(const char *prefix, xpc_object_t o) { - #include "helper-error.h" - NULL -}; -#undef ERROR + char *desc = xpc_copy_description(o); + mDNSHELPER_DEBUG("LogDebug %s: %s", prefix, desc); + free(desc); +} -mDNSexport mStatus mDNSHelperInit() +//************************************************************************************************************* +// XPC Funcs: +//************************************************************************************************************* + + +mDNSlocal void Init_Connection(const char *servname) { - HelperQueue = dispatch_queue_create("com.apple.mDNSResponder.HelperQueue", NULL); - if (HelperQueue == NULL) + helper_xpc_conn = xpc_connection_create_mach_service(servname, HelperQueue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + + xpc_connection_set_event_handler(helper_xpc_conn, ^(xpc_object_t event) { - LogMsg("dispatch_queue_create: Helper queue NULL"); - return mStatus_NoMemoryErr; - } - return mStatus_NoError; + mDNSHELPER_DEBUG("Init_Connection xpc: [%s] \n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION)); + }); + + xpc_connection_resume(helper_xpc_conn); } -static mach_port_t getHelperPort(int retry) +mDNSlocal int SendDict_ToServer(xpc_object_t msg) { - 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; + __block int errorcode = kHelperErr_NoResponse; + + LogDebug("SendDict_ToServer Sending msg to Daemon", msg); + + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + dispatch_retain(sem); // for the block below + + xpc_connection_send_message_with_reply(helper_xpc_conn, msg, HelperQueue, ^(xpc_object_t recv_msg) + { + xpc_type_t type = xpc_get_type(recv_msg); + + if (type == XPC_TYPE_DICTIONARY) + { + LogDebug("SendDict_ToServer Received reply msg from Daemon", recv_msg); + uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus); + errorcode = xpc_dictionary_get_int64(recv_msg, kHelperErrCode); + + switch (reply_status) + { + case kHelperReply_ACK: + mDNSHELPER_DEBUG("NoError: successful reply"); + break; + default: + LogMsg("default: Unexpected reply from Helper"); + break; + } + } + else + { + LogMsg("SendDict_ToServer Received unexpected reply from daemon [%s]", + xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION)); + LogDebug("SendDict_ToServer Unexpected Reply contents", recv_msg); + } + + dispatch_semaphore_signal(sem); + dispatch_release(sem); + + }); + + if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (maxwait_secs * NSEC_PER_SEC))) != 0) + LogMsg("SendDict_ToServer: UNEXPECTED WAIT_TIME in dispatch_semaphore_wait"); + + dispatch_release(sem); + + mDNSHELPER_DEBUG("SendDict_ToServer returning with errorcode[%d]", errorcode); + + return errorcode; } -const char *mDNSHelperError(int err) +mDNSlocal xpc_object_t SendDict_GetReply(xpc_object_t msg) { - static const char *p = ""; - if (mDNSHelperErrorBase < err && mDNSHelperErrorEnd > err) - p = errorstring[err - mDNSHelperErrorBase - 1]; - return p; + // Create empty dictionary + __block xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + if (!dict) return NULL; + xpc_retain(dict); + + LogDebug("SendDict_GetReply Sending msg to Daemon", msg); + + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + dispatch_retain(sem); // for the block below + + xpc_connection_send_message_with_reply(helper_xpc_conn, msg, HelperQueue, ^(xpc_object_t recv_msg) + { + xpc_type_t type = xpc_get_type(recv_msg); + + if (type == XPC_TYPE_DICTIONARY) + { + LogDebug("SendDict_GetReply Received reply msg from Daemon", recv_msg); + uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus); + + switch (reply_status) + { + case kHelperReply_ACK: + mDNSHELPER_DEBUG("NoError: successful reply"); + break; + default: + LogMsg("default: Unexpected reply from Helper"); + break; + } + // Copy result into dict reply + xpc_dictionary_apply(recv_msg, ^bool(const char *key, xpc_object_t value) + { + xpc_dictionary_set_value(dict, key, value); + return true; + }); + } + else + { + LogMsg("SendDict_GetReply Received unexpected reply from daemon [%s]", + xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION)); + LogDebug("SendDict_GetReply Unexpected Reply contents", recv_msg); + } + + dispatch_semaphore_signal(sem); + dispatch_release(sem); + xpc_release(dict); + + }); + + if (dispatch_semaphore_wait(sem, dispatch_time(DISPATCH_TIME_NOW, (maxwait_secs * NSEC_PER_SEC))) != 0) + { + LogMsg("SendDict_GetReply: UNEXPECTED WAIT_TIME in dispatch_semaphore_wait"); + xpc_release(dict); + dispatch_release(sem); + + return NULL; + } + + dispatch_release(sem); + + return dict; } -/* 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 (;;) \ +mDNSexport mStatus mDNSHelperInit() +{ + HelperQueue = dispatch_queue_create("com.apple.mDNSResponder.HelperQueue", NULL); + if (HelperQueue == NULL) { -#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; } + LogMsg("dispatch_queue_create: Helper queue NULL"); + return mStatus_NoMemoryErr; + } + return mStatus_NoError; +} void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new) { - struct { + struct + { char oldname[MAX_DOMAIN_LABEL+1]; char newname[MAX_DOMAIN_LABEL+1]; } names; @@ -108,248 +219,272 @@ void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new) 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; - }); + + if (new) + ConvertDomainLabelToCString_unescaped(new, names.newname); + + + mDNSHELPER_DEBUG("mDNSPreferencesSetName: XPC IPC Test oldname %s newname %s", names.oldname, names.newname); + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, set_name); + + xpc_dictionary_set_uint64(dict, kPrefsNameKey, key); + xpc_dictionary_set_string(dict, kPrefsOldName, names.oldname); + xpc_dictionary_set_string(dict, kPrefsNewName, names.newname); + + SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; + } -void mDNSRequestBPF(void) +void mDNSRequestBPF() { - 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; - }); + mDNSHELPER_DEBUG("mDNSRequestBPF: Using XPC IPC"); + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, bpf_request); + SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; + } int mDNSPowerRequest(int key, int interval) { - 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 err_code = kHelperErr_NotConnected; + + mDNSHELPER_DEBUG("mDNSPowerRequest: Using XPC IPC calling out to Helper key is [%d] interval is [%d]", key, interval); + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, power_req); + xpc_dictionary_set_uint64(dict, "powerreq_key", key); + xpc_dictionary_set_uint64(dict, "powerreq_interval", interval); + + err_code = SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; + + mDNSHELPER_DEBUG("mDNSPowerRequest: Using XPC IPC returning error_code %d", err_code); + return err_code; } int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth) { - 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; + int err_code = kHelperErr_NotConnected; + + mDNSHELPER_DEBUG("mDNSSetLocalAddressCacheEntry: Using XPC IPC calling out to Helper: ifindex is [%d] family is [%d]", ifindex, family); + + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, set_localaddr_cacheentry); + + xpc_dictionary_set_uint64(dict, "slace_ifindex", ifindex); + xpc_dictionary_set_uint64(dict, "slace_family", family); + + xpc_dictionary_set_data(dict, "slace_ip", (uint8_t*)ip, sizeof(v6addr_t)); + xpc_dictionary_set_data(dict, "slace_eth", (uint8_t*)eth, sizeof(ethaddr_t)); + + err_code = SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; + + mDNSHELPER_DEBUG("mDNSSetLocalAddressCacheEntry: Using XPC IPC returning error_code %d", err_code); + return err_code; } + void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text { - 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; + mDNSHELPER_DEBUG("mDNSNotify() calling out to Helper XPC IPC title[%s] msg[%s]", title, msg); - 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; - }); + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, user_notify); + + xpc_dictionary_set_string(dict, "notify_title", title); + xpc_dictionary_set_string(dict, "notify_msg", msg); + + SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; + } + int mDNSKeychainGetSecrets(CFArrayRef *result) { + CFPropertyListRef plist = NULL; CFDataRef bytes = NULL; - 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); + unsigned int secretsCnt = 0; + int error_code = kHelperErr_NotConnected; + xpc_object_t reply_dict = NULL; + const void *sec = NULL; + size_t secrets_size = 0; + + mDNSHELPER_DEBUG("mDNSKeychainGetSecrets: Using XPC IPC calling out to Helper"); + + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, keychain_getsecrets); - if (NULL == (bytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (void*)secrets, secretsCnt, kCFAllocatorNull))) + reply_dict = SendDict_GetReply(dict); + + if (reply_dict != NULL) + { + numsecrets = xpc_dictionary_get_uint64(reply_dict, "keychain_num_secrets"); + sec = xpc_dictionary_get_data(reply_dict, "keychain_secrets", &secrets_size); + secretsCnt = xpc_dictionary_get_uint64(reply_dict, "keychain_secrets_count"); + error_code = xpc_dictionary_get_int64(reply_dict, kHelperErrCode); + } + + mDNSHELPER_DEBUG("mDNSKeychainGetSecrets: Using XPC IPC calling out to Helper: numsecrets is %d, secretsCnt is %d error_code is %d secret_size is %d", + numsecrets, secretsCnt, error_code, secrets_size); + + if (NULL == (bytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (void*)sec, secretsCnt, kCFAllocatorNull))) { - err = kmDNSHelperCreationFailed; - LogMsg("%s: CFDataCreateWithBytesNoCopy failed", __func__); + error_code = kHelperErr_ApiErr; + LogMsg("mDNSKeychainGetSecrets: CFDataCreateWithBytesNoCopy failed"); goto fin; } - if (NULL == (plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault, bytes, kCFPropertyListImmutable, NULL))) + + if (NULL == (plist = CFPropertyListCreateWithData(kCFAllocatorDefault, bytes, kCFPropertyListImmutable, NULL, NULL))) { - err = kmDNSHelperInvalidPList; - LogMsg("%s: CFPropertyListCreateFromXMLData failed", __func__); + error_code = kHelperErr_ApiErr; + LogMsg("mDNSKeychainGetSecrets: CFPropertyListCreateFromXMLData failed"); goto fin; } + if (CFArrayGetTypeID() != CFGetTypeID(plist)) { - err = kmDNSHelperTypeError; - LogMsg("%s: Unexpected result type", __func__); + error_code = kHelperErr_ApiErr; + LogMsg("mDNSKeychainGetSecrets: Unexpected result type"); 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; + if (bytes) + CFRelease(bytes); + if (dict) + xpc_release(dict); + if (reply_dict) + xpc_release(reply_dict); + + dict = NULL; + reply_dict = NULL; + + return error_code; } -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; + + int err_code = kHelperErr_NotConnected; + + mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC calling out to Helper. Parameters are repdel[%d], lport[%d], rport[%d], prefix[%s], fqdn[%##s]", + replacedelete, local_port, remote_port, prefix, fqdn->c); + + + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + char buf3[INET6_ADDRSTRLEN]; + char buf4[INET6_ADDRSTRLEN]; + + buf1[0] = 0; + buf2[0] = 0; + buf3[0] = 0; + buf4[0] = 0; + + inet_ntop(AF_INET6, local_inner, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, local_outer, buf2, sizeof(buf2)); + inet_ntop(AF_INET6, remote_inner, buf3, sizeof(buf3)); + inet_ntop(AF_INET6, remote_outer, buf4, sizeof(buf4)); + char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars if (fqdn) { mDNSPlatformStrCopy(fqdnStr, prefix); ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)); } - 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; -} + + mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC calling out to Helper: Parameters are local_inner is %s, local_outeris %s, remote_inner is %s, remote_outer is %s", + buf1, buf2, buf3, buf4); + + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, autotunnel_setkeys); + + xpc_dictionary_set_data(dict, "autotunnelsetkeys_localinner", (uint8_t*)local_inner, sizeof(v6addr_t)); + xpc_dictionary_set_data(dict, "autotunnelsetkeys_localouter", (uint8_t*)local_outer, sizeof(v6addr_t)); + xpc_dictionary_set_data(dict, "autotunnelsetkeys_remoteinner", (uint8_t*)remote_inner, sizeof(v6addr_t)); + xpc_dictionary_set_data(dict, "autotunnelsetkeys_remoteouter", (uint8_t*)remote_outer, sizeof(v6addr_t)); + + xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_lport", local_port); + xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_rport", remote_port); + xpc_dictionary_set_uint64(dict, "autotunnelsetkeys_repdel", replacedelete); -void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration) -{ - char *ip_addr_copy = NULL; - char *eth_addr_copy = NULL; + // xpc_dictionary_set_string(dict, "autotunnelsetkeys_prefix", prefix); + xpc_dictionary_set_string(dict, "autotunnelsetkeys_fqdnStr", fqdnStr); + + err_code = SendDict_ToServer(dict); + + xpc_release(dict); + dict = NULL; - 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, ^{ + mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: Using XPC IPC returning error_code %d", err_code); + + mDNSHELPER_DEBUG("mDNSAutoTunnelSetKeys: this should NOT be done in mDNSResponder/Helper. For future we shall be using "); + return err_code; +} - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; +void mDNSSendWakeupPacket(unsigned int ifid, char *eth_addr, char *ip_addr, int iteration) +{ + // (void) ip_addr; // unused + // (void) iteration; // unused - LogInfo("%s: Entered ethernet address %s, ip address %s", __func__, eth_addr_copy, ip_addr_copy); + mDNSHELPER_DEBUG("mDNSSendWakeupPacket: Entered ethernet address[%s],ip_address[%s], interface_id[%d], iteration[%d]", + eth_addr, ip_addr, ifid, iteration); + + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, send_wakepkt); + + xpc_dictionary_set_uint64(dict, "interface_index", ifid); + xpc_dictionary_set_string(dict, "ethernet_address", eth_addr); + xpc_dictionary_set_string(dict, "ip_address", ip_addr); + xpc_dictionary_set_uint64(dict, "swp_iteration", iteration); + + SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; - 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) @@ -359,138 +494,127 @@ void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pf 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); + mDNSHELPER_DEBUG("mDNSPacketFilterControl: XPC IPC, ifname %s", ifname); + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, p2p_packetfilter); + + xpc_dictionary_set_uint64(dict, "pf_opcode", command); + if (ifname) + xpc_dictionary_set_string(dict, "pf_ifname", ifname); + xpc_dictionary_set_uint64(dict, "pf_count", count); - 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; - }); + xpc_dictionary_set_uint64(dict, "pf_port0", pfa.portArray[0]); + xpc_dictionary_set_uint64(dict, "pf_port1", pfa.portArray[1]); + xpc_dictionary_set_uint64(dict, "pf_port2", pfa.portArray[2]); + xpc_dictionary_set_uint64(dict, "pf_port3", pfa.portArray[3]); + + xpc_dictionary_set_uint64(dict, "pf_protocol0", pfa.protocolArray[0]); + xpc_dictionary_set_uint64(dict, "pf_protocol1", pfa.protocolArray[1]); + xpc_dictionary_set_uint64(dict, "pf_protocol2", pfa.protocolArray[2]); + xpc_dictionary_set_uint64(dict, "pf_protocol3", pfa.protocolArray[3]); + SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; + + mDNSHELPER_DEBUG("mDNSPacketFilterControl: portArray0[%d] portArray1[%d] portArray2[%d] portArray3[%d] protocolArray0[%d] protocolArray1[%d] protocolArray2[%d] protocolArray3[%d]", + pfa.portArray[0], pfa.portArray[1], pfa.portArray[2], pfa.portArray[3], pfa.protocolArray[0], pfa.protocolArray[1], pfa.protocolArray[2], pfa.protocolArray[3]); + } -void mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win) +void mDNSSendKeepalive(const v6addr_t sadd, const v6addr_t dadd, uint16_t lport, uint16_t rport, uint32_t seq, uint32_t ack, uint16_t win) { - 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; - }); + mDNSHELPER_DEBUG("mDNSSendKeepalive: Using XPC IPC calling out to Helper: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]", + lport, rport, seq, ack, win); + + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + buf1[0] = 0; + buf2[0] = 0; + + inet_ntop(AF_INET6, sadd, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, dadd, buf2, sizeof(buf2)); + mDNSHELPER_DEBUG("mDNSSendKeepalive: Using XPC IPC calling out to Helper: sadd is %s, dadd is %s", buf1, buf2); + + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, send_keepalive); + + xpc_dictionary_set_data(dict, "send_keepalive_sadd", (uint8_t*)sadd, sizeof(v6addr_t)); + xpc_dictionary_set_data(dict, "send_keepalive_dadd", (uint8_t*)dadd, sizeof(v6addr_t)); + + xpc_dictionary_set_uint64(dict, "send_keepalive_lport", lport); + xpc_dictionary_set_uint64(dict, "send_keepalive_rport", rport); + xpc_dictionary_set_uint64(dict, "send_keepalive_seq", seq); + xpc_dictionary_set_uint64(dict, "send_keepalive_ack", ack); + xpc_dictionary_set_uint64(dict, "send_keepalive_win", win); + + SendDict_ToServer(dict); + xpc_release(dict); + dict = NULL; + } int mDNSRetrieveTCPInfo(int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid) { - 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; - }); + int error_code = kHelperErr_NotConnected; + xpc_object_t reply_dict = NULL; + + mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo: Using XPC IPC calling out to Helper: lport is[%d] rport is[%d] family is[%d]", + lport, rport, family); + + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + buf1[0] = 0; + buf2[0] = 0; + + inet_ntop(AF_INET6, laddr, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, raddr, buf2, sizeof(buf2)); + mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo:: Using XPC IPC calling out to Helper: laddr is %s, raddr is %s", buf1, buf2); + + Init_Connection(kHelperService); + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_uint64(dict, kHelperMode, retreive_tcpinfo); + + xpc_dictionary_set_data(dict, "retreive_tcpinfo_laddr", (uint8_t*)laddr, sizeof(v6addr_t)); + xpc_dictionary_set_data(dict, "retreive_tcpinfo_raddr", (uint8_t*)raddr, sizeof(v6addr_t)); + + xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_family", family); + xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_lport", lport); + xpc_dictionary_set_uint64(dict, "retreive_tcpinfo_rport", rport); + + reply_dict = SendDict_GetReply(dict); + + if (reply_dict != NULL) + { + *seq = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_seq"); + *ack = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_ack"); + *win = xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_win"); + *intfid = (int32_t)xpc_dictionary_get_uint64(reply_dict, "retreive_tcpinfo_ifid"); + error_code = xpc_dictionary_get_int64(reply_dict, kHelperErrCode); + } + + mDNSHELPER_DEBUG("mDNSRetrieveTCPInfo: Using XPC IPC calling out to Helper: seq is %d, ack is %d, win is %d, intfid is %d, error is %d", + *seq, *ack, *win, *intfid, error_code); + + if (dict) + xpc_release(dict); + if (reply_dict) + xpc_release(reply_dict); + dict = NULL; + reply_dict = NULL; + + return error_code; } diff --git a/mDNSMacOSX/helper.c b/mDNSMacOSX/helper.c index deb33e9..f5b4f3b 100644 --- a/mDNSMacOSX/helper.c +++ b/mDNSMacOSX/helper.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007-2012 Apple Inc. All rights reserved. + * Copyright (c) 2007-2015 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. @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -53,14 +52,14 @@ #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 #include +#include + #ifndef RTF_IFSCOPE #define RTF_IFSCOPE 0x1000000 #endif @@ -69,6 +68,7 @@ #ifndef MDNS_NO_IPSEC #define MDNS_NO_IPSEC 1 #endif + #define NO_CFUSERNOTIFICATION 1 #define NO_SECURITYFRAMEWORK 1 #endif @@ -79,79 +79,51 @@ 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) +void helper_exit() { - debug("entry"); - if (!authorized(&token)) - goto fin; - helplog(ASL_LEVEL_INFO, "exit"); + os_log_info(log_handle,"mDNSResponderHelper exiting"); exit(0); - -fin: - debug("fin"); - return KERN_SUCCESS; } -kern_return_t do_mDNSRequestBPF(__unused mach_port_t port, audit_token_t token) +mDNSexport void RequestBPF() { - 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; } - + if (err) + { + os_log(log_handle, "RequestBPF: ConnectToServer %d", err); + return; + } + 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; } + if (!hdr) + { + os_log(log_handle, "RequestBPF: No mem to allocate"); + DNSServiceRefDeallocate(ref); + return; + } + put_flags(0, &ptr); deliver_request(hdr, ref); // Will free hdr for us DNSServiceRefDeallocate(ref); update_idle_timer(); - return KERN_SUCCESS; + + os_log_info(log_handle,"RequestBPF: Successful"); } -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; } +void PowerRequest(int key, int interval, int *err) +{ + *err = kHelperErr_DefaultErr; + + os_log_info(log_handle,"PowerRequest: key %d interval %d, err %d", key, interval, *err); + CFArrayRef events = IOPMCopyScheduledPowerEvents(); if (events) { @@ -166,58 +138,79 @@ kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interv 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); + //os_log(log_handle, "Deleting old event %s"); + if (result) + os_log(log_handle, "IOPMCancelScheduledPowerEvent %d failed %d", i, result); } } CFRelease(events); } - - if (key < 0) // mDNSPowerRequest(-1,-1) means "clear any stale schedules" (see above) - *err = 0; + + if (key < 0) // mDNSPowerRequest(-1,-1) means "clear any stale schedules" (see above) + { + *err = kHelperErr_NoErr; + } 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); } + if (r) + { + usleep(100000); + os_log_info(log_handle, "IOPMSleepSystem %d", r); + } *err = r; } else if (key > 0) { - CFDateRef w = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); - if (w) + CFDateRef wakeTime = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent() + interval); + if (wakeTime) { - 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); } + CFMutableDictionaryRef scheduleDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + + CFDictionaryAddValue(scheduleDict, CFSTR(kIOPMPowerEventTimeKey), wakeTime); + CFDictionaryAddValue(scheduleDict, CFSTR(kIOPMPowerEventAppNameKey), CFSTR("mDNSResponderHelper")); + CFDictionaryAddValue(scheduleDict, CFSTR(kIOPMPowerEventTypeKey), key ? CFSTR(kIOPMAutoWake) : CFSTR(kIOPMAutoSleep)); + + IOReturn r = IOPMRequestSysWake(scheduleDict); + if (r) + { + usleep(100000); + os_log_info(log_handle, "IOPMRequestSysWake(%d) %d %x", interval, r, r); + } *err = r; - CFRelease(w); + CFRelease(wakeTime); + CFRelease(scheduleDict); } } -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) +void SetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth, int *err) { - #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 + +#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 (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]); + { + os_log_info(log_handle,"SetLocalAddressCacheEntry %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; } - + { + os_log_info(log_handle,"SetLocalAddressCacheEntry %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]); + } + + *err = kHelperErr_DefaultErr; + 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) + os_log(log_handle, "SetLocalAddressCacheEntry: socket(PF_ROUTE, SOCK_RAW, 0) failed %d (%s)", errno, strerror(errno)); } if (s >= 0) @@ -228,7 +221,7 @@ kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int if { 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; @@ -241,7 +234,7 @@ kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int if 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; @@ -249,7 +242,7 @@ kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int if 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; @@ -257,26 +250,26 @@ kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int if 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)", + os_log(log_handle, "SetLocalAddressCacheEntry: write(%zu) 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", + os_log(log_handle, "SetLocalAddressCacheEntry: read (%zu) 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; + + *err = kHelperErr_NoErr; } 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; @@ -289,14 +282,14 @@ kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int if 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; @@ -304,35 +297,33 @@ kern_return_t do_mDNSSetLocalAddressCacheEntry(__unused mach_port_t port, int if 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)", + os_log(log_handle, "SetLocalAddressCacheEntry: write(%zu) 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", + os_log(log_handle, "SetLocalAddressCacheEntry: read (%zu) 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; + + *err = kHelperErr_NoErr; } - } - -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; +void UserNotify(const char *title, const char *msg) +{ + #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.)"; + static const char footer[] = "(Note: This message only appears on machines with 17.x.x.x IP addresses" + " or on debugging builds with ForceAlerts set — i.e. only at Apple — not on customer machines.)"; CFStringRef alertHeader = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8); CFStringRef alertBody = CFStringCreateWithCString(NULL, msg, kCFStringEncodingUTF8); CFStringRef alertFooter = CFStringCreateWithCString(NULL, footer, kCFStringEncodingUTF8); @@ -340,18 +331,19 @@ kern_return_t do_mDNSNotify(__unused mach_port_t port, const char *title, const 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); + if (err) + os_log(log_handle, "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 @@ -372,9 +364,9 @@ static CFRunLoopSourceRef gNotificationRLS = NULL; static void NotificationCallBackDismissed(CFUserNotificationRef userNotification, CFOptionFlags responseFlags) { - debug("entry"); + os_log_debug(log_handle,"entry"); (void)responseFlags; // Unused - if (userNotification != gNotification) helplog(ASL_LEVEL_ERR, "NotificationCallBackDismissed: Wrong CFUserNotificationRef"); + if (userNotification != gNotification) os_log(log_handle, "NotificationCallBackDismissed: Wrong CFUserNotificationRef"); if (gNotificationRLS) { // Caution: don't use CFRunLoopGetCurrent() here, because the currently executing thread may not be our "CFRunLoopRun" thread. @@ -402,7 +394,7 @@ static void ShowNameConflictNotification(CFMutableArrayRef header, CFStringRef s CFMutableDictionaryRef dictionary = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (!dictionary) return; - debug("entry"); + os_log_debug(log_handle,"entry"); CFDictionarySetValue(dictionary, kCFUserNotificationAlertHeaderKey, header); CFDictionarySetValue(dictionary, kCFUserNotificationAlertMessageKey, subtext); @@ -416,20 +408,20 @@ static void ShowNameConflictNotification(CFMutableArrayRef header, CFStringRef s { SInt32 error; gNotification = CFUserNotificationCreate(NULL, 0, kCFUserNotificationCautionAlertLevel, &error, dictionary); - if (!gNotification || error) { helplog(ASL_LEVEL_ERR, "ShowNameConflictNotification: CFUserNotificationRef: Error %d", error); return; } + if (!gNotification || error) { os_log(log_handle, "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; } + if (!gNotificationRLS) { os_log(log_handle, "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); + os_log_debug(log_handle,"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) +static CFMutableArrayRef CreateAlertHeader(const char* oldname, const char* newname, const CFStringRef msg, const char* suffix) { CFMutableArrayRef alertHeader = NULL; @@ -440,9 +432,13 @@ static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, con // 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); + { + os_log(log_handle, "Could not construct CFStrings for old=%s", newname); + } else if (newname && !cfnewname) - helplog(ASL_LEVEL_ERR,"Could not construct CFStrings for new=%s", newname); + { + os_log(log_handle, "Could not construct CFStrings for new=%s", newname); + } else { const CFStringRef s1 = CFStringCreateWithFormat(NULL, NULL, CFS_Format, cfoldname, suffix); @@ -451,11 +447,17 @@ static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, con alertHeader = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (!s1) - helplog(ASL_LEVEL_ERR, "Could not construct secondary CFString for old=%s", oldname); + { + os_log(log_handle, "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); + { + os_log(log_handle, "Could not construct secondary CFString for new=%s", newname); + } else if (!alertHeader) - helplog(ASL_LEVEL_ERR, "Could not construct CFArray for notification"); + { + os_log(log_handle, "Could not construct CFArray for notification"); + } else { // Make sure someone is logged in. We don't want this popping up over the login window @@ -464,18 +466,23 @@ static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, con 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) + if (!CFEqual(userName, CFSTR("_mbsetupuser"))) { - CFArrayAppendValue(alertHeader, CFSTR("The name has been changed to ")); - CFArrayAppendValue(alertHeader, CFS_OQ); CFArrayAppendValue(alertHeader, s2); CFArrayAppendValue(alertHeader, CFS_CQ); - CFArrayAppendValue(alertHeader, CFSTR(".")); + 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.")); + } } - else - CFArrayAppendValue(alertHeader, CFSTR("All attempts to find an available name by adding a number to the name were also unsuccessful.")); + CFRelease(userName); } } if (s1) CFRelease(s1); @@ -491,19 +498,23 @@ static CFMutableArrayRef GetHeader(const char* oldname, const char* newname, con static void update_notification(void) { #ifndef NO_CFUSERNOTIFICATION - debug("entry ucn=%s, uhn=%s, lcn=%s, lhn=%s", usercompname, userhostname, lastcompname, lasthostname); + os_log_debug(log_handle,"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. + // Note: The "\xEF\xBB\xBF" byte sequence (U+FEFF) 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 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_Format = CFStringCreateWithCString(NULL, "%@%s\xEF\xBB\xBF", kCFStringEncodingUTF8); + + // The strings CFS_OQ, CFS_CQ and the others below are the localization keys for the “Localizable.strings” files, + // and MUST NOT BE CHANGED, or localization substitution will be broken. + // To change the text displayed to the user, edit the values in the appropriate “Localizable.strings” file, not the keys here. + // This includes making changes for adding appropriate directionality overrides like LRM, LRE, RLE, PDF, etc. These need to go in the values + // in the appropriate “Localizable.strings” entries, not in the keys here (which then won’t match *any* entry in the localization files). + // These localization keys here were broken in and then subsequently repaired in + // [mDNSResponder]: TA: Gala15A185: Incorrect punctuation marks when Change the host name to an exist one + CFS_OQ = CFStringCreateWithCString(NULL, "“", kCFStringEncodingUTF8); // DO NOT CHANGE THIS STRING + CFS_CQ = CFStringCreateWithCString(NULL, "”", kCFStringEncodingUTF8); // DO NOT CHANGE THIS STRING 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); @@ -518,7 +529,7 @@ static void update_notification(void) { if (gNotificationRLS) { - debug("canceling notification %p", gNotification); + os_log_debug(log_handle,"canceling notification %p", gNotification); CFUserNotificationCancel(gNotification); unpause_idle_timer(); } @@ -529,17 +540,17 @@ static void update_notification(void) 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"); + header = CreateAlertHeader(userhostname, NULL, CFS_LocalHostName, ".local"); subtext = &CFS_Problem; } else if (usercompname[0]) { - header = GetHeader(usercompname, lastcompname, CFS_ComputerName, ""); + header = CreateAlertHeader(usercompname, lastcompname, CFS_ComputerName, ""); subtext = &CFS_ComputerNameMsg; } else { - header = GetHeader(userhostname, lasthostname, CFS_LocalHostName, ".local"); + header = CreateAlertHeader(userhostname, lasthostname, CFS_LocalHostName, ".local"); subtext = &CFS_LocalHostNameMsg; } ShowNameConflictNotification(header, *subtext); @@ -548,8 +559,7 @@ static void update_notification(void) #endif } -kern_return_t -do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, const char* new, audit_token_t token) +void PreferencesSetName(int key, const char* old, const char* new) { SCPreferencesRef session = NULL; Boolean ok = FALSE; @@ -558,37 +568,37 @@ do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, c 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; - + + os_log_info(log_handle,"PreferencesSetName: entry %s old=%s new=%s", + key==kmDNSComputerName ? "ComputerName" : (key==kmDNSLocalHostName ? "LocalHostName" : "UNKNOWN"), old, new); + 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; + case kmDNSComputerName: + user = usercompname; + last = lastcompname; + break; + case kmDNSLocalHostName: + user = userhostname; + last = lasthostname; + break; + default: + os_log(log_handle, "PreferencesSetName: unrecognized key: %d", key); + goto fin; } - + if (!last) { - helplog(ASL_LEVEL_ERR, "%s: no last ptr", __func__); + os_log(log_handle, "PreferencesSetName: no last ptr"); goto fin; } - + if (!user) { - helplog(ASL_LEVEL_ERR, "%s: no user ptr", __func__); + os_log(log_handle, "PreferencesSetName:: no user ptr"); 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. @@ -613,7 +623,7 @@ do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, c 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. Subsequent conflicts don't // update the "old" value. @@ -622,57 +632,66 @@ do_mDNSPreferencesSetName(__unused mach_port_t port, int key, const char* old, c 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); - + + session = SCPreferencesCreate(NULL, CFSTR(kHelperService), NULL); + if (cfstr == NULL || session == NULL) { - debug("SCPreferencesCreate failed"); + os_log(log_handle, "PreferencesSetName: SCPreferencesCreate failed"); goto fin; } if (!SCPreferencesLock(session, 0)) { - debug("lock failed"); + os_log(log_handle,"PreferencesSetName: 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; - } - + 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"); + os_log(log_handle, "PreferencesSetName: SCPreferences update failed"); goto fin; } - debug("succeeded"); - + os_log_info(log_handle,"PreferencesSetName: succeeded"); + fin: if (NULL != cfstr) CFRelease(cfstr); @@ -683,10 +702,12 @@ fin: CFRelease(session); } update_idle_timer(); - if (needUpdate) update_notification(); - return KERN_SUCCESS; + if (needUpdate) + update_notification(); + } + enum DNSKeyFormat { formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem @@ -704,8 +725,7 @@ static const char dnsprefix[] = "dns:"; static const char ddns[] = "ddns"; static const char ddnsrev[] = "sndd"; -static enum DNSKeyFormat -getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) +static enum DNSKeyFormat getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) { static UInt32 tags[4] = { @@ -720,13 +740,11 @@ getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) Boolean malformed = FALSE; OSStatus status = noErr; int i = 0; - + *attributesp = NULL; - if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, - &attributeInfo, NULL, &attributes, NULL, NULL))) + if (noErr != (status = SecKeychainItemCopyAttributesAndData(item, &attributeInfo, NULL, &attributes, NULL, NULL))) { - debug("SecKeychainItemCopyAttributesAndData %d - skipping", - status); + os_log_info(log_handle,"getDNSKeyFormat: SecKeychainItemCopyAttributesAndData %d - skipping", status); goto skip; } if (attributeInfo.count != attributes->count) @@ -736,137 +754,127 @@ getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAttributeList **attributesp) malformed = TRUE; if (malformed) { - debug( - "malformed result from SecKeychainItemCopyAttributesAndData - skipping"); + os_log(log_handle, "getDNSKeyFormat: malformed result from SecKeychainItemCopyAttributesAndData - skipping"); goto skip; } + + os_log_info(log_handle,"getDNSKeyFormat: 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); - 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", + os_log(log_handle, "getDNSKeyFormat: 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", + os_log(log_handle, "getDNSKeyFormat: 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)) + 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)) + 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)) + 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)) + 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"); + os_log_info(log_handle,"getDNSKeyFormat: uninterested in this entry"); goto skip; } + *attributesp = attributes; - debug("accepting this entry"); + os_log_info(log_handle,"getDNSKeyFormat: 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) +static CFPropertyListRef copyKeychainItemInfo(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))) + + if (NULL == (entry = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks))) { - debug("CFArrayCreateMutable failed"); + os_log(log_handle, "copyKeychainItemInfo: 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; + 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: + os_log(log_handle, "copyKeychainItemInfo: unknown DNSKeyFormat value"); + break; } if (NULL == data) { - debug("CFDataCreate for attr[1] failed"); + os_log(log_handle, "copyKeychainItemInfo: 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))) + if (NULL == (data = CFDataCreate(kCFAllocatorDefault, attributes->attr[2].data, attributes->attr[2].length))) { - debug("CFDataCreate for attr[2] failed"); + os_log(log_handle, "copyKeychainItemInfo: 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))) + 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); + os_log(log_handle, "copyKeychainItemInfo: 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"); + os_log(log_handle, "copyKeychainItemInfo: 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))) + if (NULL == (data = CFDataCreate(kCFAllocatorDefault, attributes->attr[3].data, attributes->attr[3].length))) { - debug("CFDataCreate for attr[3] failed"); + os_log(log_handle, "copyKeychainItemInfo: CFDataCreate for attr[3] failed"); goto error; } + CFArrayAppendValue(entry, data); CFRelease(data); return entry; - + error: if (NULL != entry) CFRelease(entry); @@ -874,10 +882,7 @@ error: } #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) +void KeychainGetSecrets(__unused unsigned int *numsecrets,__unused unsigned long *secrets, __unused unsigned int *secretsCnt, __unused int *err) { #ifndef NO_SECURITYFRAMEWORK CFWriteStreamRef stream = NULL; @@ -890,41 +895,34 @@ do_mDNSKeychainGetSecrets(__unused mach_port_t port, __unused unsigned int *nums SecKeychainAttributeList *attributes = NULL; enum DNSKeyFormat format; OSStatus status = 0; - - debug("entry"); - *err = 0; + + os_log_info(log_handle,"KeychainGetSecrets: entry"); + *err = kHelperErr_NoErr; *numsecrets = 0; *secrets = (vm_offset_t)NULL; - if (!authorized(&token)) - { - *err = kmDNSHelperNotAuthorized; - goto fin; - } - if (NULL == (keys = CFArrayCreateMutable(NULL, 0, - &kCFTypeArrayCallBacks))) + + if (NULL == (keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks))) { - debug("CFArrayCreateMutable failed"); - *err = kmDNSHelperCreationFailed; + os_log(log_handle, "KeychainGetSecrets: CFArrayCreateMutable failed"); + *err = kHelperErr_ApiErr; goto fin; } if (noErr != (status = SecKeychainCopyDefault(&skc))) { - *err = kmDNSHelperKeychainCopyDefaultFailed; + *err = kHelperErr_ApiErr; goto fin; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" if (noErr != (status = SecKeychainSearchCreateFromAttributes(skc, kSecGenericPasswordItemClass, NULL, &search))) { - *err = kmDNSHelperKeychainSearchCreationFailed; + *err = kHelperErr_ApiErr; goto fin; } - for (status = SecKeychainSearchCopyNext(search, &item); - noErr == status; - status = SecKeychainSearchCopyNext(search, &item)) + for (status = SecKeychainSearchCopyNext(search, &item); noErr == status; status = SecKeychainSearchCopyNext(search, &item)) { - if (formatNotDNSKey != (format = getDNSKeyFormat(item, - &attributes)) && - NULL != (entry = getKeychainItemInfo(item, attributes, - format))) + if (formatNotDNSKey != (format = getDNSKeyFormat(item, &attributes)) && + NULL != (entry = copyKeychainItemInfo(item, attributes, format))) { CFArrayAppendValue(keys, entry); CFRelease(entry); @@ -932,42 +930,43 @@ do_mDNSKeychainGetSecrets(__unused mach_port_t port, __unused unsigned int *nums SecKeychainItemFreeAttributesAndData(attributes, NULL); CFRelease(item); } +#pragma clang diagnostic pop if (errSecItemNotFound != status) - helplog(ASL_LEVEL_ERR, "%s: SecKeychainSearchCopyNext failed: %d", - __func__, status); - if (NULL == (stream = - CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, - kCFAllocatorDefault))) - { - *err = kmDNSHelperCreationFailed; - debug("CFWriteStreamCreateWithAllocatedBuffers failed"); + os_log(log_handle, "KeychainGetSecrets: SecKeychainSearchCopyNext failed: %d", status); + + if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(kCFAllocatorDefault, kCFAllocatorDefault))) + { + *err = kHelperErr_ApiErr; + os_log(log_handle, "KeychainGetSecrets:CFWriteStreamCreateWithAllocatedBuffers failed"); goto fin; } + CFWriteStreamOpen(stream); - if (0 == CFPropertyListWriteToStream(keys, stream, - kCFPropertyListBinaryFormat_v1_0, NULL)) + if (0 == CFPropertyListWrite(keys, stream, kCFPropertyListBinaryFormat_v1_0, 0, NULL)) { - *err = kmDNSHelperPListWriteFailed; - debug("CFPropertyListWriteToStream failed"); + *err = kHelperErr_ApiErr; + os_log(log_handle, "KeychainGetSecrets:CFPropertyListWriteToStream failed"); goto fin; } - result = CFWriteStreamCopyProperty(stream, - kCFStreamPropertyDataWritten); - if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets, - CFDataGetLength(result), VM_FLAGS_ANYWHERE)) + result = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten); + + if (KERN_SUCCESS != vm_allocate(mach_task_self(), secrets, CFDataGetLength(result), VM_FLAGS_ANYWHERE)) { - *err = kmDNSHelperCreationFailed; - debug("vm_allocate failed"); + *err = kHelperErr_ApiErr; + os_log(log_handle, "KeychainGetSecrets: vm_allocate failed"); goto fin; } - CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)), - (void *)*secrets); + + CFDataGetBytes(result, CFRangeMake(0, CFDataGetLength(result)), (void *)*secrets); *secretsCnt = CFDataGetLength(result); *numsecrets = CFArrayGetCount(keys); - debug("succeeded"); - + + os_log_info(log_handle,"KeychainGetSecrets: succeeded"); + fin: - debug("returning %u secrets", *numsecrets); + os_log_info(log_handle,"KeychainGetSecrets: returning numsecrets[%u] secrets[%lu] secrets addr[%p] secretscount[%u]", + *numsecrets, *secrets, secrets, *secretsCnt); + if (NULL != stream) { CFWriteStreamClose(stream); @@ -982,460 +981,679 @@ fin: if (NULL != skc) CFRelease(skc); update_idle_timer(); - return KERN_SUCCESS; + + *err = KERN_SUCCESS; + #else - return KERN_FAILURE; + + *err = 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]=""; - 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() +void SendWakeupPacket(unsigned int ifid, const char *eth_addr, const char *ip_addr, int iteration) { - static int g_oldRacoon = -1; - - if (g_oldRacoon == -1) + 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 + + os_log_info(log_handle,"SendWakeupPacket() ether_addr[%s] ip_addr[%s] if_id[%d] iteration[%d]", + eth_addr, ip_addr, ifid, iteration); + + if (if_indextoname(ifid, ifname) == NULL) { - char letter = 0; - int minor = 0; - g_oldRacoon = (MacOSXSystemBuildNumber(&letter, &minor) < 10); - debug("%s", g_oldRacoon ? "old" : "new"); + os_log(log_handle, "SendWakeupPacket: invalid interface index %u", ifid); + return; } - - 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) + + ea = ether_aton(eth_addr); + if (ea == NULL) { - helplog(ASL_LEVEL_NOTICE, "open \"%s\" failed: %s", racoon_config_path, strerror(errno)); - return 0; + os_log(log_handle, "SendWakeupPacket: invalid ethernet address %s", eth_addr); + return; } - else + + for (i = 0; i < 100; i++) { - 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)); + 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; } -} - -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 (bpf_fd == -1) { - 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; - } + os_log(log_handle, "SendWakeupPacket: cannot find a bpf device"); + return; } - else if (errno != ENOENT) + + 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_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path, strerror(errno)); + os_log(log_handle, "SendWakeupPacket: BIOCSETIF failed %s", 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) + + // 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) { - 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); + os_log(log_handle, "SendWakeupPacket: write failed %s", strerror(errno)); + return; } - else if (errno != ENOENT) + os_log(log_handle, "SendWakeupPacket: 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_NOTICE, "stat failed for \"%s\", leaving in place: %s", racoon_config_path_orig, strerror(errno)); + os_log(log_handle, "SendWakeupPacket: write failed %s", strerror(errno)); return; } + os_log(log_handle, "SendWakeupPacket: sent broadcast eth_addr %s, ip_addr %s", eth_addr, ip_addr); + + close(bpf_fd); + } -static void -moveAsideAnonymousRacoonConfiguration(const char* dir) -{ - if (!dir) return; - debug("entry %s", dir); +// Open the specified port for protocol in the P2P firewall. +void PacketFilterControl(uint32_t command, const char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray) +{ + int error; + + os_log_info(log_handle,"PacketFilterControl: command %d ifname %s, count %d", + command, ifname, count); + os_log_info(log_handle,"PacketFilterControl: portArray0[%d] portArray1[%d] portArray2[%d] portArray3[%d] protocolArray0[%d] protocolArray1[%d] protocolArray2[%d] protocolArray3[%d]", portArray[0], portArray[1], portArray[2], portArray[3], protocolArray[0], protocolArray[1], protocolArray[2], protocolArray[3]); + + switch (command) + { + case PF_SET_RULES: + error = P2PPacketFilterAddBonjourRuleSet(ifname, count, portArray, protocolArray); + if (error) + os_log(log_handle, "P2PPacketFilterAddBonjourRuleSet failed %s", strerror(error)); + break; + + case PF_CLEAR_RULES: + error = P2PPacketFilterClearBonjourRules(); + if (error) + os_log(log_handle, "P2PPacketFilterClearBonjourRules failed %s", strerror(error)); + break; + + default: + os_log(log_handle, "PacketFilterControl: invalid command %d", command); + break; + } - 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) +static unsigned long in_cksum(unsigned short *ptr, int nbytes) { - struct stat s; - int ret = stat(racoon_config_dir, &s); - if (ret != 0) + 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) { - 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); - } + sum += *ptr++; + nbytes -= 2; } - else if (!(s.st_mode & S_IFDIR)) + + /* mop up an odd byte, if necessary */ + if (nbytes == 1) { - helplog(ASL_LEVEL_ERR, "\"%s\" is not a directory!", - racoon_config_dir); - return -1; + /* make sure top half is zero */ + oddbyte = 0; + + /* one byte only */ + *((u_char *)&oddbyte) = *(u_char *)ptr; + sum += oddbyte; } - - return 0; + /* Add back carry outs from top 16 bits to low 16 bits. */ + sum = (sum >> 16) + (sum & 0xffff); + + /* add carry */ + sum += (sum >> 16); + + return sum; } -static int -createAnonymousRacoonConfiguration(const char *fqdn) +static unsigned short InetChecksum(unsigned short *ptr, int nbytes) { - 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); + unsigned long sum; + + sum = in_cksum(ptr, nbytes); + return (unsigned short)~sum; +} - if (0 > fd) +static void TCPCheckSum(int af, struct tcphdr *t, int tcplen, const v6addr_t sadd6, const v6addr_t dadd6) +{ + unsigned long sum = 0; + unsigned short *ptr; + + /* TCP header checksum */ + sum = in_cksum((unsigned short *)t, tcplen); + + if (af == AF_INET) { - helplog(ASL_LEVEL_ERR, "mkstemp \"%s\" failed: %s", - tmp_config_path, strerror(errno)); - return -1; + /* Pseudo header */ + ptr = (unsigned short *)sadd6; + sum += *ptr++; + sum += *ptr++; + ptr = (unsigned short *)dadd6; + sum += *ptr++; + sum += *ptr++; } - 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)) + else if (af == AF_INET6) { - 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; + /* 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++; } - - debug("successfully renamed \"%s\" \"%s\"", tmp_config_path, racoon_config_path); - return 0; + + sum += htons(tcplen); + sum += htons(IPPROTO_TCP); + + while (sum >> 16) + sum = (sum >> 16) + (sum & 0xFFFF); + + t->th_sum = ~sum; + } -static int -notifyRacoon(void) +void SendKeepalive(const v6addr_t sadd6, const v6addr_t dadd6, uint16_t lport, uint16_t rport, uint32_t seq, uint32_t ack, uint16_t win) { - 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); + +#define IPv6FMTSTRING "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X" +#define IPv6FMTSARGS sadd6[0], sadd6[1], sadd6[2], sadd6[3], sadd6[4], sadd6[5], sadd6[6], sadd6[7], sadd6[8], sadd6[9], sadd6[10], sadd6[11], sadd6[12], sadd6[13], sadd6[14], sadd6[15] +#define IPv6FMTDARGS dadd6[0], dadd6[1], dadd6[2], dadd6[3], dadd6[4], dadd6[5], dadd6[6], dadd6[7], dadd6[8], dadd6[9], dadd6[10], dadd6[11], dadd6[12], dadd6[13], dadd6[14], dadd6[15] - if (0 > fd) + os_log_info(log_handle, "SendKeepalive: "IPv6FMTSTRING" :space: "IPv6FMTSTRING"", + IPv6FMTSARGS, IPv6FMTDARGS); + + struct packet4 { - debug("open \"%s\" failed, and that's OK: %s", racoon_pid_path, - strerror(errno)); - return kmDNSHelperRacoonNotificationFailed; + 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; + + os_log_info(log_handle,"SendKeepalive invoked: lport is[%d] rport is[%d] seq is[%d] ack is[%d] win is[%d]", + lport, rport, seq, ack, win); + + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + buf1[0] = 0; + buf2[0] = 0; + + inet_ntop(AF_INET6, sadd6, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, dadd6, buf2, sizeof(buf2)); + + os_log_info(log_handle,"SendKeepalive invoked: sadd6 is %s, dadd6 is %s", buf1, buf2); + + // 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) } - n = read(fd, buf, sizeof(buf)-1); - close(fd); - if (1 > n) + else { - debug("read of \"%s\" failed: %s", racoon_pid_path, - n == 0 ? "empty file" : strerror(errno)); - return kmDNSHelperRacoonNotificationFailed; + 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; } - buf[n] = '\0'; - m = strtoul(buf, &p, 10); - if (*p != '\0' && !isspace(*p)) + + /* 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) { - debug("invalid PID \"%s\" (around '%c')", buf, *p); - return kmDNSHelperRacoonNotificationFailed; + os_log(log_handle, "SendKeepalive: socket %s", strerror(errno)); + return; } - if (2 > m) + + if (af == AF_INET) { - debug("refusing to kill PID %lu", m); - return kmDNSHelperRacoonNotificationFailed; + on = 1; + if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof (on))) + { + close(sock); + os_log(log_handle, "SendKeepalive: setsockopt %s", strerror(errno)); + return; + } + + 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; + } - if (0 != kill(m, RacoonSignal())) + else { - debug("Could not signal racoon (%lu): %s", m, strerror(errno)); - return kmDNSHelperRacoonNotificationFailed; + 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; } - 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) + + 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) { - /* fall back to the erroneous getdtablesize method */ - for (fd = from; fd < getdtablesize(); ++fd) - close(fd); - return; + if (errno == EINTR) + goto again; } - while (0 == readdir_r(dirp, &entry, &entryp) && NULL != entryp) + + if (len != packetlen) { - fd = atoi(entryp->d_name); - if (fd >= from && fd != dirfd(dirp)) - close(fd); + os_log(log_handle, "SendKeepalive: sendmsg failed %s", strerror(errno)); } - closedir(dirp); -} - -static int -startRacoonOld(void) -{ - debug("entry"); + else + { + char source[INET6_ADDRSTRLEN], dest[INET6_ADDRSTRLEN]; + + inet_ntop(af, (void *)sadd6, source, sizeof(source)); + inet_ntop(af, (void *)dadd6, dest, sizeof(dest)); + + os_log(log_handle, "SendKeepalive: Success Source %s:%d, Dest %s:%d, %u, %u, %u", + source, ntohs(lport), dest, ntohs(rport), ntohl(seq), ntohl(ack), ntohs(win)); + + } + close(sock); + +} + + +void RetrieveTCPInfo(int family, const v6addr_t laddr, uint16_t lport, const v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid, int *err) +{ + + 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)); + + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + buf1[0] = 0; + buf2[0] = 0; + + inet_ntop(AF_INET6, laddr, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, raddr, buf2, sizeof(buf2)); + + os_log_info(log_handle, "RetrieveTCPInfo invoked: laddr is %s, raddr is %s", buf1, buf2); + + os_log_info(log_handle,"RetrieveTCPInfo invoked: lport is[%d] rport is[%d] family is [%d]", + lport, rport, family); + + 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) + { + os_log(log_handle, "RetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno)); + *err = errno; + } + miblen = (unsigned int)sz; + len = sizeof(struct tcp_info); + if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1) + { + os_log(log_handle, "RetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno)); + *err = 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; + *err = KERN_SUCCESS; + +} + +#ifndef MDNS_NO_IPSEC + +static const char configHeader[] = "# BackToMyMac\n"; +static const char g_racoon_config_dir[] = "/var/run/racoon/"; +static const char g_racoon_config_dir_old[] = "/etc/racoon/remote/"; + +static int MacOSXSystemBuildNumber(char* letter_out, int* minor_out) +{ + int major = 0, minor = 0; + char letter = 0, buildver[256]=""; + 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 + os_log_info(log_handle, "_CFCopySystemVersionDictionary failed"); + + if (!major) { major=16; letter = 'A'; minor = 300; os_log_info(log_handle, "Note: No Major Build Version number found; assuming 16A300"); } + 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); + os_log_debug(log_handle, "%s", g_oldRacoon ? "old" : "new"); + } + + return g_oldRacoon; +} + +static int RacoonSignal() +{ + return UseOldRacoon() ? SIGHUP : SIGUSR1; +} + +static int notifyRacoon(void) +{ + os_log_debug(log_handle,"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) + { + os_log_debug(log_handle,"open \"%s\" failed, and that's OK: %s", racoon_pid_path, + strerror(errno)); + return kHelperErr_RacoonNotificationFailed; + } + n = read(fd, buf, sizeof(buf)-1); + close(fd); + if (1 > n) + { + os_log_debug(log_handle,"read of \"%s\" failed: %s", racoon_pid_path, + n == 0 ? "empty file" : strerror(errno)); + return kHelperErr_RacoonNotificationFailed; + } + buf[n] = '\0'; + m = strtoul(buf, &p, 10); + if (*p != '\0' && !isspace(*p)) + { + os_log_debug(log_handle,"invalid PID \"%s\" (around '%c')", buf, *p); + return kHelperErr_RacoonNotificationFailed; + } + if (2 > m) + { + os_log_debug(log_handle,"refusing to kill PID %lu", m); + return kHelperErr_RacoonNotificationFailed; + } + if (0 != kill(m, RacoonSignal())) + { + os_log_debug(log_handle,"Could not signal racoon (%lu): %s", m, strerror(errno)); + return kHelperErr_RacoonNotificationFailed; + } + + os_log_debug(log_handle, "Sent racoon (%lu) signal %d", m, RacoonSignal()); + return 0; +} + +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 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) +{ + os_log_debug(log_handle,"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", + os_log_info(log_handle, "execve of \"%s\" failed: %s", racoon_args[0], strerror(errno)); exit(2); } - helplog(ASL_LEVEL_NOTICE, "racoon (pid=%lu) started", + os_log_info(log_handle,"racoon (pid=%lu) started", (unsigned long)pid); n = waitpid(pid, &status, 0); if (-1 == n) { - helplog(ASL_LEVEL_ERR, "Unexpected waitpid failure: %s", + os_log(log_handle, "Unexpected waitpid failure: %s", strerror(errno)); - return kmDNSHelperRacoonStartFailed; + return kHelperErr_RacoonStartFailed; } else if (pid != n) { - helplog(ASL_LEVEL_ERR, "Unexpected waitpid return value %d", - (int)n); - return kmDNSHelperRacoonStartFailed; + os_log(log_handle, "Unexpected waitpid return value %d", (int)n); + return kHelperErr_RacoonStartFailed; } else if (WIFSIGNALED(status)) { - helplog(ASL_LEVEL_ERR, + os_log(log_handle, "racoon (pid=%lu) terminated due to signal %d", (unsigned long)pid, WTERMSIG(status)); - return kmDNSHelperRacoonStartFailed; + return kHelperErr_RacoonStartFailed; } else if (WIFSTOPPED(status)) { - helplog(ASL_LEVEL_ERR, + os_log(log_handle, "racoon (pid=%lu) has stopped due to signal %d", (unsigned long)pid, WSTOPSIG(status)); - return kmDNSHelperRacoonStartFailed; + return kHelperErr_RacoonStartFailed; } else if (0 != WEXITSTATUS(status)) { - helplog(ASL_LEVEL_ERR, + os_log(log_handle, "racoon (pid=%lu) exited with status %d", (unsigned long)pid, WEXITSTATUS(status)); - return kmDNSHelperRacoonStartFailed; + return kHelperErr_RacoonStartFailed; } - debug("racoon (pid=%lu) daemonized normally", (unsigned long)pid); + os_log_debug(log_handle, "racoon (pid=%lu) daemonized normally", (unsigned long)pid); return 0; } @@ -1451,18 +1669,17 @@ typedef struct vpnctl_hdr_struct u_int16_t len; } vpnctl_hdr; -static int -startRacoon(void) +static int startRacoon(void) { - debug("entry"); + os_log_debug(log_handle,"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", + os_log(log_handle,"Could not create endpoint for racoon control socket: %d %s", errno, strerror(errno)); - return kmDNSHelperRacoonStartFailed; + return kHelperErr_RacoonStartFailed; } - + struct sockaddr_un saddr; memset(&saddr, 0, sizeof(saddr)); saddr.sun_family = AF_UNIX; @@ -1472,1396 +1689,752 @@ startRacoon(void) 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", + os_log(log_handle, "Could not connect racoon control socket %s: %d %s", racoon_control_sock_path, errno, strerror(errno)); - return kmDNSHelperRacoonStartFailed; + return kHelperErr_RacoonStartFailed; } - + 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; + os_log(log_handle, "Could not write to racoon control socket: %d %s", errno, strerror(errno)); + return kHelperErr_RacoonStartFailed; } 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)); + os_log(log_handle,"Could not read from racoon control socket: %d %s", errno, 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))) + else + { + os_log_debug(log_handle, "select returned but fd_isset not on expected fd"); + } + } + else if (result < 0) { - close(sock); - helplog(ASL_LEVEL_ERR, "mDNSSendKeepalive: setsockopt %s", strerror(errno)); - return errno; + os_log_debug(log_handle, "select returned %d errno %d %s", result, errno, strerror(errno)); + if (errno != EINTR) break; } - - 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; + + close(fd); + + if (bytes < sizeof(vpnctl_hdr) || h.cookie != btmm_cookie) + return kHelperErr_RacoonStartFailed; + + os_log_debug(log_handle, "racoon started"); + return 0; +} - 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)); +static int kickRacoon(void) +{ + if ( 0 == notifyRacoon() ) + return 0; + return UseOldRacoon() ? startRacoonOld() : startRacoon(); +} - sin6_to->sin6_port = rport; - sin6_to->sin6_flowinfo = 0; +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; - 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; - } +static const uint8_t kWholeV6Mask = 128; - 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; - } +static unsigned int routeSeq = 1; - if (len != packetlen) +static int setupTunnelRoute(const v6addr_t local, const 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, "mDNSSendKeepalive: sendmsg failed %s", strerror(errno)); + os_log(log_handle,"socket(PF_ROUTE, ...) failed: %s", strerror(errno)); + + err = kHelperErr_RoutingSocketCreationFailed; + goto fin; } - else + + 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)) { - 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)); - + int errno_ = errno; + + os_log_info(log_handle,"write to routing socket failed: %s", strerror(errno_)); + if (EEXIST != errno_) + { + err = kHelperErr_RouteAdditionFailed; + goto fin; + } } - close(sock); - return KERN_SUCCESS; + +fin: + if (0 <= s) + close(s); + return err; } - -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) +static int teardownTunnelRoute(const v6addr_t remote) { - 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)) + 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))) + { + os_log(log_handle, "socket(PF_ROUTE, ...) failed: %s", strerror(errno)); + err = kHelperErr_RoutingSocketCreationFailed; + 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)) { - helplog(ASL_LEVEL_ERR, "mDNSRetrieveTCPInfo: Not authorized"); - return kmDNSHelperNotAuthorized; + int errno_ = errno; + + os_log_debug(log_handle,"write to routing socket failed: %s", strerror(errno_)); + + if (ESRCH != errno_) + { + err = kHelperErr_RouteDeletionFailed; + goto fin; + } } + +fin: + if (0 <= s) + close(s); + return err; +} - if (family == AF_INET) +static int v4addr_to_string(v4addr_t addr, char *buf, size_t buflen) +{ + if (NULL == inet_ntop(AF_INET, addr, buf, buflen)) { - 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; + os_log(log_handle, "v4addr_to_string() inet_ntop failed: %s", strerror(errno)); + return kHelperErr_InvalidNetworkAddress; } 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; + return 0; } - itpl.itpl_proto = IPPROTO_TCP; - sz = sizeof(mib)/sizeof(mib[0]); - if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1) +} + +static int v6addr_to_string(const v6addr_t addr, char *buf, size_t buflen) +{ + if (NULL == inet_ntop(AF_INET6, addr, buf, buflen)) { - helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno)); - return errno; + os_log(log_handle, "v6addr_to_string inet_ntop failed: %s", strerror(errno)); + return kHelperErr_InvalidNetworkAddress; } - miblen = (unsigned int)sz; - len = sizeof(struct tcp_info); - if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1) + else { - helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno)); - return errno; + return 0; } - - *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) +static int ensureExistenceOfRacoonConfigDir(const char* const racoon_config_dir) { - struct + struct stat s; + int ret = stat(racoon_config_dir, &s); + if (ret != 0) { - 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; \ + if (errno != ENOENT) + { + os_log(log_handle, "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) + { + os_log(log_handle, "mkdir \"%s\" failed: %s", + racoon_config_dir, strerror(errno)); + return -1; + } + else + { + os_log_info(log_handle, "created directory \"%s\"", racoon_config_dir); + } + } } - - 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) + else if (!(s.st_mode & S_IFDIR)) { - helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Can not open the socket - %s", strerror(errno)); - return errno; + os_log(log_handle, "\"%s\" is not a directory!", + racoon_config_dir); + return -1; } + + return 0; +} - 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) + +/* 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, + const v6addr_t src6, const 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) { - 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); + 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 = kHelperErr_IPsecPolicyCreationFailed; + goto fin; } - 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) + + if (n >= (int)sizeof(buf)) { - helplog(ASL_LEVEL_INFO, "do_mDNSGetRemoteMAC: writing to routing socket: %s", strerror(errno)); - close(sock); - return errno; + err = kHelperErr_ResultTooLarge; + goto fin; } - - do + + os_log_info(log_handle, "policy=\"%s\"", buf); + + if (NULL == (*policy = (ipsec_policy_t)ipsec_set_policy(buf, n))) { - rlen = read(sock, (char *)&m_rtmsg, sizeof(m_rtmsg)); + os_log_info(log_handle, "Could not create IPsec policy from \"%s\"", buf); + err = kHelperErr_IPsecPolicyCreationFailed; + goto fin; } - 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"); + *len = ((ipsec_policy_t)(*policy))->sadb_x_policy_len * 8; + +fin: + return err; +} - if (family == AF_INET) +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; + + os_log_debug(log_handle, "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) { - sin = (struct sockaddr_in *) (rtm + 1); - sdl = (struct sockaddr_dl *) (sin->sin_len + (char *) sin); + os_log(log_handle, "Could not set IPsec policy: %s", ipsec_strerror()); + err = kHelperErr_IPsecPolicySetFailed; + goto fin; } - else if (family == AF_INET6) + else { - sin6 = (struct sockaddr_in6 *) (rtm +1); - sdl = (struct sockaddr_dl *) (sin6->sin6_len + (char *) sin6); + err = 0; } - // 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) + + os_log_debug(log_handle, "succeeded"); + +fin: + return err; +} + +static int removeSA(int s, struct sockaddr *src, struct sockaddr *dst) +{ + int err = 0; + + os_log_debug(log_handle, "entry"); + + err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, src, dst); + if (0 > err) { - 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; + os_log(log_handle, "Could not remove IPsec SA: %s", ipsec_strerror()); + err = kHelperErr_IPsecRemoveSAFailed; + goto fin; } - else if (sdl->sdl_family == AF_INET6) + err = pfkey_send_delete_all(s, SADB_SATYPE_ESP, IPSEC_MODE_ANY, dst, src); + if (0 > err) { - 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; + os_log(log_handle, "Could not remove IPsec SA: %s", ipsec_strerror()); + err = kHelperErr_IPsecRemoveSAFailed; + goto fin; } - - unsigned char *ptr = (unsigned char *)LLADDR(sdl); - for (i = 0; i < ETHER_ADDR_LEN; i++) - (eth)[i] = *(ptr +i); - - close(sock); - return KERN_SUCCESS; + else + err = 0; + + os_log_debug(log_handle, "succeeded"); + +fin: + return err; } -kern_return_t do_mDNSGetRemoteMAC(__unused mach_port_t port, int family, v6addr_t raddr, ethaddr_t eth, audit_token_t token) +static int doTunnelPolicy(mDNSTunnelPolicyWhich which, mDNSTunnelType type, + const v6addr_t loc_inner, uint8_t loc_bits, + v4addr_t loc_outer, uint16_t loc_port, + const v6addr_t rmt_inner, uint8_t rmt_bits, + v4addr_t rmt_outer, uint16_t rmt_port, + const v6addr_t loc_outer6, const v6addr_t rmt_outer6) { - int ret = 0; - v6addr_t gateway; - int gfamily; - int count = 0; - - if (!authorized(&token)) + 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; + + os_log_debug(log_handle,"entry"); + if (0 > (s = pfkey_open())) { - helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Not authorized"); - return kmDNSHelperNotAuthorized; + os_log(log_handle, "Could not create IPsec policy socket: %s", ipsec_strerror()); + err = kHelperErr_IPsecPolicySocketCreationFailed; + goto fin; } - - do + + 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) { - ret = getMACAddress(family, raddr, gateway, &gfamily, eth); - if (ret == -1) + 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 { - memcpy(raddr, gateway, sizeof(family)); - family = gfamily; - count++; + 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; + } } } - while ((ret == -1) && (count < 5)); - return ret; + + os_log_debug(log_handle,"succeeded"); + +fin: + if (s >= 0) + pfkey_close(s); + if (NULL != policy) + free(policy); + return err; } +#endif /* ndef MDNS_NO_IPSEC */ -kern_return_t do_mDNSStoreSPSMACAddress(__unused mach_port_t port, int family, v6addr_t spsaddr, const char *ifname, audit_token_t token) +int HelperAutoTunnelSetKeys(int replacedelete, const v6addr_t loc_inner, const v6addr_t loc_outer6, uint16_t loc_port, const v6addr_t rmt_inner, + const v6addr_t rmt_outer6, uint16_t rmt_port, const char *id, int *err) { - 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)) +#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; + + os_log_debug(log_handle,"HelperAutoTunnelSetKeys: entry"); + *err = kHelperErr_NoErr; + + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + char buf3[INET6_ADDRSTRLEN]; + char buf4[INET6_ADDRSTRLEN]; + + buf1[0] = 0; + buf2[0] = 0; + buf3[0] = 0; + buf4[0] = 0; + + inet_ntop(AF_INET6, loc_inner, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, loc_outer6, buf2, sizeof(buf2)); + inet_ntop(AF_INET6, rmt_inner, buf3, sizeof(buf3)); + inet_ntop(AF_INET6, rmt_outer6, buf4, sizeof(buf4)); + + os_log_info(log_handle, "HelperAutoTunnelSetKeys: Parameters are local_inner is %s, local_outer is %s, remote_inner is %s, remote_outer is %s id is %s", + buf1, buf2, buf3, buf4, id); + + switch ((enum mDNSAutoTunnelSetKeysReplaceDelete)replacedelete) { - helplog(ASL_LEVEL_ERR, "Unable to access SC Dynamic Store"); - return KERN_FAILURE; + case kmDNSAutoTunnelSetKeysReplace: + case kmDNSAutoTunnelSetKeysDelete: + break; + default: + *err = kHelperErr_InvalidTunnelSetKeysOperation; + goto fin; } - - // 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"); + + if (0 != (*err = v6addr_to_string(loc_inner, li, sizeof(li)))) 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; + if (0 != (*err = v6addr_to_string(rmt_inner, ri, sizeof(ri)))) goto fin; + + os_log_debug(log_handle, "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; + + os_log_debug(log_handle, "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 = kHelperErr_ResultTooLarge; + 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))) + else { - helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", strerror(errno)); - ret = kmDNSHelperInvalidNetworkAddress; - goto fin; + 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; + + os_log_debug(log_handle, "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 = kHelperErr_ResultTooLarge; + 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 (kmDNSAutoTunnelSetKeysReplace == replacedelete) { - if ((ipdict = SCDynamicStoreCopyValue(ipstore, entityname)) != NULL) + if (0 > ensureExistenceOfRacoonConfigDir(GetRacoonConfigDir())) { - if((addrs = CFDictionaryGetValue(ipdict, CFSTR("Addresses"))) != NULL) - { - addrs = CFRetain(addrs); - CFDictionarySetValue(dict, CFSTR("RegisteredAddresses"), addrs); - } + *err = kHelperErr_RacoonConfigCreationFailed; + goto fin; + } + if ((int)sizeof(tmp_path) <= + snprintf(tmp_path, sizeof(tmp_path), "%s.XXXXXX", path)) + { + *err = kHelperErr_ResultTooLarge; + goto fin; + } + if (0 > (fd = mkstemp(tmp_path))) + { + os_log(log_handle, "mkstemp \"%s\" failed: %s", tmp_path, strerror(errno)); + *err = kHelperErr_RacoonConfigCreationFailed; + goto fin; + } + if (NULL == (fp = fdopen(fd, "w"))) + { + os_log(log_handle, "fdopen: %s", strerror(errno)); + *err = kHelperErr_RacoonConfigCreationFailed; + 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)) + { + os_log(log_handle, "rename \"%s\" \"%s\" failed: %s", tmp_path, path, strerror(errno)); + *err = kHelperErr_RacoonConfigCreationFailed; + goto fin; } } - SCDynamicStoreSetValue(store, sckey, dict); - + else + { + if (0 != unlink(path)) + os_log_debug(log_handle, "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; + + os_log_debug(log_handle, "succeeded"); + 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); - + 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; + + *err = kHelperErr_IPsecDisabled; +#endif /* MDNS_NO_IPSEC */ update_idle_timer(); - return ret; + return KERN_SUCCESS; } + + + + diff --git a/mDNSMacOSX/helper.h b/mDNSMacOSX/helper.h index a298237..eedf738 100644 --- a/mDNSMacOSX/helper.h +++ b/mDNSMacOSX/helper.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007-2012 Apple Inc. All rights reserved. + * Copyright (c) 2007-2013 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. @@ -18,7 +18,71 @@ #ifndef H_HELPER_H #define H_HELPER_H -#define kmDNSHelperServiceName "com.apple.mDNSResponderHelper" +#include + +#define kHelperService "com.apple.mDNSResponder_Helper" + +#define kmDNSHelperProgramArgs CFSTR("com.apple.mDNSResponderHelper") +#define kPreferencesKey_mDNSHelperLog CFSTR("mDNSHelperDebugLogging") + +#define kHelperMode "HelperMode" +#define kHelperReplyStatus "HelperReplyStatusToClient" +#define kHelperErrCode "HelperErrorCodefromCall" + +#define kPrefsNameKey "PreferencesNameKey" +#define kPrefsOldName "PreferencesOldName" +#define kPrefsNewName "PreferencesNewName" + +extern int mDNSHelperLogEnabled; + +extern os_log_t log_handle; + +typedef enum +{ + bpf_request = 1, + set_name = 2, + p2p_packetfilter = 3, + user_notify = 4, + power_req = 5, + send_wakepkt = 6, + set_localaddr_cacheentry = 7, + send_keepalive = 8, + retreive_tcpinfo = 9, + keychain_getsecrets = 10, + autotunnel_setkeys = 11, + request_other, +} HelperModes; + +typedef enum +{ + kHelperReply_ACK = 0, +} HelperReplyStatusCodes; + + +typedef enum +{ + kHelperErr_NoErr = 0, + kHelperErr_DefaultErr = -1, + kHelperErr_NotConnected = -2, + kHelperErr_NoResponse = -3, + kHelperErr_UndefinedMode = -4, + kHelperErr_ApiErr = -5, + kHelperErr_InvalidTunnelSetKeysOperation = -6, + kHelperErr_InvalidNetworkAddress = -7, + kHelperErr_ResultTooLarge = -8, + kHelperErr_RacoonConfigCreationFailed = -9, + kHelperErr_IPsecPolicySocketCreationFailed = -10, + kHelperErr_IPsecPolicyCreationFailed = -11, + kHelperErr_IPsecPolicySetFailed = -12, + kHelperErr_IPsecRemoveSAFailed = -13, + kHelperErr_IPsecDisabled = -14, + kHelperErr_RoutingSocketCreationFailed = -15, + kHelperErr_RouteDeletionFailed = -16, + kHelperErr_RouteAdditionFailed = -17, + kHelperErr_RacoonStartFailed = -18, + kHelperErr_RacoonNotificationFailed = -19, +} HelperErrorCodes; + enum mDNSPreferencesSetNameKey { @@ -38,6 +102,7 @@ enum mDNSAutoTunnelSetKeysReplaceDelete 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). @@ -49,36 +114,40 @@ enum mDNSKeyChainAttributes 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 void mDNSSendKeepalive(const v6addr_t sadd, const 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); + +extern void RequestBPF(void); +extern void PreferencesSetName(int key, const char* old, const char* new); +extern void PacketFilterControl(uint32_t command, const char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray); +extern void UserNotify(const char *title, const char *msg); // Both strings are UTF-8 text +extern void PowerRequest(int key, int interval, int *error); +extern void SendWakeupPacket(unsigned int ifid, const char *eth_addr, const char *ip_addr, int iteration); +extern void SetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth, int *err); +extern void SendKeepalive(const v6addr_t sadd6, const v6addr_t dadd6, uint16_t lport, uint16_t rport, uint32_t seq, uint32_t ack, uint16_t win); +extern void RetrieveTCPInfo(int family, const v6addr_t laddr, uint16_t lport, const v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid, int *err); +extern void KeychainGetSecrets(__unused unsigned int *numsecrets,__unused unsigned long *secrets, __unused unsigned int *secretsCnt, __unused int *err); +extern int HelperAutoTunnelSetKeys(int replacedelete, const v6addr_t loc_inner, const v6addr_t loc_outer6, uint16_t loc_port, const v6addr_t rmt_inner, + const v6addr_t rmt_outer6, uint16_t rmt_port, const char *id, int *err); +extern void helper_exit(void); #endif /* H_HELPER_H */ diff --git a/mDNSMacOSX/helpermsg-types.h b/mDNSMacOSX/helpermsg-types.h index ca5b140..689b249 100644 --- a/mDNSMacOSX/helpermsg-types.h +++ b/mDNSMacOSX/helpermsg-types.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2007 Apple Inc. All rights reserved. + * Copyright (c) 2007-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. @@ -24,7 +24,7 @@ typedef uint8_t ethaddr_t[ 6]; typedef uint8_t v6addr_t [16]; typedef const char *string_t; -#define PFPortArraySize 16 +#define PFPortArraySize 4 typedef uint16_t pfArray_t [PFPortArraySize]; #endif /* H_HELPERMSG_TYPES_H */ diff --git a/mDNSMacOSX/helpermsg.defs b/mDNSMacOSX/helpermsg.defs deleted file mode 100644 index 5836308..0000000 --- a/mDNSMacOSX/helpermsg.defs +++ /dev/null @@ -1,143 +0,0 @@ -/* -*- 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 -#include - -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/mDNSMacOSX/ipsec_strerror.h b/mDNSMacOSX/ipsec_strerror.h index ecacf3b..b15fc28 100644 --- a/mDNSMacOSX/ipsec_strerror.h +++ b/mDNSMacOSX/ipsec_strerror.h @@ -1,5 +1,6 @@ -/* - * Copyright (c) 2003-2007 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2015 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. @@ -13,6 +14,7 @@ * 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 $ */ diff --git a/mDNSMacOSX/libpfkey.h b/mDNSMacOSX/libpfkey.h index 98d192d..24f1b08 100644 --- a/mDNSMacOSX/libpfkey.h +++ b/mDNSMacOSX/libpfkey.h @@ -1,5 +1,6 @@ -/* - * Copyright (c) 2003-2007 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-2015 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. @@ -13,6 +14,7 @@ * 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 $ */ diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c index f904c8b..84e9de8 100644 --- a/mDNSMacOSX/mDNSMacOSX.c +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2016 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. @@ -29,6 +29,7 @@ #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 "dns_sd_private.h" #include "PlatformCommon.h" #include "uds_daemon.h" #include "CryptoSupport.h" @@ -60,8 +61,7 @@ #include // For n_long, required by below #include // For IPTOS_LOWDELAY etc. -#include // For IN6_IFF_NOTREADY etc. -#include // For ND6_INFINITE_LIFETIME etc. +#include // For IN6_IFF_TENTATIVE etc. #include @@ -86,13 +86,23 @@ #include #include +#if TARGET_OS_IPHONE +// For WiFiManagerClientRef etc, declarations. +#include +#include +#include +#endif // TARGET_OS_IPHONE + // Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic. #include #if APPLE_OSX_mDNSResponder #include #include +#include // for ne_session_set_socket_attributes() #if !NO_D2D +#include "BLE.h" + 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)); @@ -104,6 +114,9 @@ void D2DStartResolvingPairOnTransport(const Byte *key, const size_t keySize, con 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)); +void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize); +void xD2DRemoveFromCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize); + #endif // ! NO_D2D #else @@ -122,8 +135,10 @@ D2DStatus D2DTerminate() __attribute__((weak_import)); #define mDNS_IOREG_KA_KEY "mDNS_Keepalive" #define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS' +#define DARK_WAKE_TIME 16 // Time we hold an idle sleep assertion for maintenance after a wake notification + // cache the InterfaceID of the AWDL interface -static mDNSInterfaceID AWDLInterfaceID; +mDNSInterfaceID AWDLInterfaceID; // *************************************************************************** // Globals @@ -167,9 +182,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 @@ -213,9 +225,9 @@ mDNSexport void D2D_start_advertising_interface(NetworkInterfaceInfo *interface) LogInfo("D2D_start_advertising_interface: %s", interface->ifname); if (interface->RR_A.resrec.RecordType) - external_start_advertising_service(&interface->RR_A.resrec, NULL); + external_start_advertising_service(&interface->RR_A.resrec, 0); if (interface->RR_PTR.resrec.RecordType) - external_start_advertising_service(&interface->RR_PTR.resrec, NULL); + external_start_advertising_service(&interface->RR_PTR.resrec, 0); } } @@ -228,9 +240,29 @@ mDNSexport void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface) LogInfo("D2D_stop_advertising_interface: %s", interface->ifname); if (interface->RR_A.resrec.RecordType) - external_stop_advertising_service(&interface->RR_A.resrec, NULL); + external_stop_advertising_service(&interface->RR_A.resrec, 0); if (interface->RR_PTR.resrec.RecordType) - external_stop_advertising_service(&interface->RR_PTR.resrec, NULL); + external_stop_advertising_service(&interface->RR_PTR.resrec, 0); + } +} + +// If record would have been advertised to the D2D plugin layer, stop that advertisement. +mDNSexport void D2D_stop_advertising_record(AuthRecord *ar) +{ + DNSServiceFlags flags = deriveD2DFlagsFromAuthRecType(ar->ARType); + if (callExternalHelpers(ar->resrec.InterfaceID, ar->resrec.name, flags)) + { + external_stop_advertising_service(&ar->resrec, flags); + } +} + +// If record should be advertised to the D2D plugin layer, start that advertisement. +mDNSexport void D2D_start_advertising_record(AuthRecord *ar) +{ + DNSServiceFlags flags = deriveD2DFlagsFromAuthRecType(ar->ARType); + if (callExternalHelpers(ar->resrec.InterfaceID, ar->resrec.name, flags)) + { + external_start_advertising_service(&ar->resrec, flags); } } @@ -295,65 +327,6 @@ mDNSlocal void DomainnameToLower(const domainname * const in, domainname * const 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); @@ -450,8 +423,8 @@ mDNSlocal void xD2DClearCache(const domainname *regType, DNS_TypeValues qtype) { 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)); + mDNS_Deregister(&mDNSStorage, &ptr->ar); } } } @@ -489,11 +462,12 @@ mDNSlocal void D2DBrowseListRetain(const domainname *const name, mDNSu16 type) LogInfo("D2DBrowseListRetain: %##s %s refcount now %u", (*ptr)->name.c, DNSTypeName((*ptr)->type), (*ptr)->refCount); } -mDNSlocal void D2DBrowseListRelease(const domainname *const name, mDNSu16 type) +// Returns true if found in list, false otherwise +mDNSlocal bool 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; } + if (!*ptr) { LogMsg("D2DBrowseListRelease: Didn't find %##s %s in list", name->c, DNSTypeName(type)); return false; } (*ptr)->refCount -= 1; @@ -505,35 +479,125 @@ mDNSlocal void D2DBrowseListRelease(const domainname *const name, mDNSu16 type) *ptr = (*ptr)->next; mDNSPlatformMemFree(tmp); } + return true; } -mDNSlocal mStatus xD2DParse(mDNS *const m, const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, AuthRecord *rr) +mDNSlocal mStatus xD2DParse(mDNS *const m, const mDNSu8 * const lhs, const mDNSu16 lhs_len, const mDNSu8 * const rhs, const mDNSu16 rhs_len, D2DRecordListElem **D2DListp) { - if (*(lhs + (lhs_len - 1)) == compression_packet_v1) - return DNSNameCompressionParseBytes(m, lhs, lhs_len, rhs, rhs_len, rr); - else + // Sanity check that key array (lhs) has one domain name, followed by the record type and single byte D2D + // plugin protocol version number. + // Note, we don't have a DNSMessage pointer at this point, so just pass in the lhs value as the lower bound + // of the input bytes we are processing. skipDomainName() does not try to follow name compression pointers, + // so it is safe to pass it the key byte array since it will stop parsing the DNS name and return a pointer + // to the byte after the first name compression pointer it encounters. + const mDNSu8 *keyp = skipDomainName((const DNSMessage *const) lhs, lhs, lhs + lhs_len); + + // There should be 3 bytes remaining in a valid key, + // two for the DNS record type, and one for the D2D protocol version number. + if (keyp == NULL || (keyp + 3 != (lhs + lhs_len))) + { + LogInfo("xD2DParse: Could not parse DNS name in key"); + return mStatus_Incompatible; + } + keyp += 2; // point to D2D compression packet format version byte + if (*keyp != compression_packet_v1) + { + LogInfo("xD2DParse: Invalid D2D packet version: %d", *keyp); return mStatus_Incompatible; + } + + 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("xD2DParse: failed to get large RR"); + m->rec.r.resrec.RecordType = 0; + return mStatus_UnknownErr; + } + else + { + LogInfo("xD2DParse: got rr: %s", CRDisplayString(m, &m->rec.r)); + } + + *D2DListp = mDNSPlatformMemAllocate(sizeof(D2DRecordListElem) + (m->rec.r.resrec.rdlength <= sizeof(RDataBody) ? 0 : m->rec.r.resrec.rdlength - sizeof(RDataBody))); + if (!*D2DListp) return mStatus_NoMemoryErr; + + AuthRecord *rr = &(*D2DListp)->ar; + 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 void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstance instanceHandle, D2DTransportType transportType, const Byte *key, size_t keySize, const Byte *value, size_t valueSize) +mDNSexport 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; } + D2DRecordListElem *ptr = NULL; - err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr->ar); + err = xD2DParse(m, (const mDNSu8 * const)key, (const mDNSu16)keySize, (const mDNSu8 * const)value, (const mDNSu16)valueSize, &ptr); if (err) { LogMsg("xD2DAddToCache: xD2DParse returned error: %d", err); PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); - mDNSPlatformMemFree(ptr); + if (ptr) + mDNSPlatformMemFree(ptr); return; } + + // If the record was created based on a BLE beacon, update the interface index to indicate + // this and thus match BLE specific queries. + if (transportType == D2DBLETransport) + ptr->ar.resrec.InterfaceID = mDNSInterface_BLE; + err = mDNS_Register(m, &ptr->ar); if (err) { @@ -555,17 +619,17 @@ mDNSlocal void xD2DAddToCache(mDNS *const m, D2DStatus result, D2DServiceInstanc 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; + D2DRecordListElem *arptr = NULL; 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) + mStatus err = xD2DParse(m, (const mDNSu8 *const)key, (const mDNSu16)keySize, (const mDNSu8 *const)value, (const mDNSu16)valueSize, &arptr); + if (err) { - LogMsg("xD2DFindInList: xD2DParse failed for key: %p (%u) value: %p (%u)", key, keySize, value, valueSize); - mDNSPlatformMemFree(arptr); + LogMsg("xD2DFindInList: xD2DParse returned error: %d", err); + PrintHelper(__func__, (mDNSu8 *)key, (mDNSu16)keySize, (mDNSu8 *)value, (mDNSu16)valueSize); + if (arptr) + mDNSPlatformMemFree(arptr); return NULL; } @@ -580,7 +644,7 @@ mDNSlocal D2DRecordListElem * xD2DFindInList(mDNS *const m, const Byte *const ke 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) +mDNSexport 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. @@ -768,23 +832,45 @@ mDNSlocal D2DTransportType xD2DInterfaceToTransportType(mDNSInterfaceID Interfac return D2DTransportMax; } -mDNSexport void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags) +// Similar to callExternalHelpers(), but without the checks for the BLE specific interface or flags. +// It's assumed that the domain was already verified to be .local once we are at this level. +mDNSlocal mDNSBool callInternalHelpers(mDNSInterfaceID InterfaceID, DNSServiceFlags flags) { - domainname lower; + if ( ((InterfaceID == mDNSInterface_Any) && (flags & (kDNSServiceFlagsIncludeP2P | kDNSServiceFlagsIncludeAWDL))) + || mDNSPlatformInterfaceIsD2D(InterfaceID)) + return mDNStrue; + else + return mDNSfalse; +} - if (qtype == kDNSServiceType_A || qtype == kDNSServiceType_AAAA) +mDNSexport void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags, DNSQuestion * q) +{ + // BLE support currently not handled by a D2D plugin + if (applyToBLE(InterfaceID, flags)) { - LogInfo("external_start_browsing_for_service: ignoring address record"); - return; + domainname lower; + + DomainnameToLower(typeDomain, &lower); + // pass in the key and keySize + mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); + start_BLE_browse(q, &lower, qtype, flags, compression_lhs, end - compression_lhs); } + if (callInternalHelpers(InterfaceID, flags)) + internal_start_browsing_for_service(InterfaceID, typeDomain, qtype, flags); +} + +mDNSexport void internal_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags) +{ + domainname lower; + 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)); + LogInfo("%s: Starting browse for: %##s %s", __func__, lower.c, DNSTypeName(qtype)); mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); @@ -808,22 +894,34 @@ mDNSexport void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, 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) + // BLE support currently not handled by a D2D plugin + if (applyToBLE(InterfaceID, flags)) { - LogInfo("external_stop_browsing_for_service: ignoring address record"); - return; + domainname lower; + + // If this is the last instance of this browse, clear any cached records recieved for it. + // We are not guaranteed to get a D2DServiceLost event for all key, value pairs cached over BLE. + DomainnameToLower(typeDomain, &lower); + if (stop_BLE_browse(&lower, qtype, flags)) + xD2DClearCache(&lower, qtype); } + if (callInternalHelpers(InterfaceID, flags)) + internal_stop_browsing_for_service(InterfaceID, typeDomain, qtype, flags); +} + +mDNSexport void internal_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const typeDomain, DNS_TypeValues qtype, DNSServiceFlags flags) +{ + domainname lower; + DomainnameToLower(typeDomain, &lower); - D2DBrowseListRelease(&lower, qtype); - if (!D2DBrowseListRefCount(&lower, qtype)) + // If found in list and this is the last reference to this browse, remove the key from the D2D plugins. + if (D2DBrowseListRelease(&lower, qtype) && !D2DBrowseListRefCount(&lower, qtype)) { D2DTransportType transportType, excludedTransport; - LogInfo("external_stop_browsing_for_service: Stopping browse for: %##s %s", lower.c, DNSTypeName(qtype)); + LogInfo("%s: Stopping browse for: %##s %s", __func__, lower.c, DNSTypeName(qtype)); mDNSu8 *end = DNSNameCompressionBuildLHS(&lower, qtype); PrintHelper(__func__, compression_lhs, end - compression_lhs, mDNSNULL, 0); @@ -851,13 +949,24 @@ mDNSexport void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, mDNSexport void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { + // Note, start_BLE_advertise() is currently called directly from external_start_advertising_helper() since + // it needs to pass the ServiceRecordSet so that we can promote the record advertisements to AWDL + // when we see the corresponding browse indication over BLE. + + if (callInternalHelpers(resourceRecord->InterfaceID, flags)) + internal_start_advertising_service(resourceRecord, flags); +} + +mDNSexport void internal_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)); + LogInfo("%s: %s", __func__, 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) @@ -885,14 +994,30 @@ mDNSexport void external_start_advertising_service(const ResourceRecord *const r mDNSexport void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { + // BLE support currently not handled by a D2D plugin + if (applyToBLE(resourceRecord->InterfaceID, flags)) + { + domainname lower; + + DomainnameToLower(resourceRecord->name, &lower); + stop_BLE_advertise(&lower, resourceRecord->rrtype, flags); + } + + if (callInternalHelpers(resourceRecord->InterfaceID, flags)) + internal_stop_advertising_service(resourceRecord, flags); +} + +mDNSexport void internal_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)); + LogInfo("%s: %s", __func__, RRDisplayString(&mDNSStorage, resourceRecord)); + // For SRV records, update packet filter if p2p interface already exists, otherwise, // For SRV records, update packet filter to to remove this port from list if (resourceRecord->rrtype == kDNSType_SRV) mDNSUpdatePacketFilter(resourceRecord); @@ -960,8 +1085,8 @@ mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, co 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); + external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, 0, 0); + external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, 0, 0); } } @@ -1006,14 +1131,19 @@ mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, con 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); + external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, 0); + external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, 0); } } #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 internal_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 internal_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 internal_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;} +mDNSexport void internal_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags) { (void)resourceRecord; (void)flags;} + +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; (void)q } 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;} @@ -1037,22 +1167,15 @@ mDNSexport void external_stop_resolving_service(const domainname *const fqdn, DN // 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. -// We also don't want to use multicast on *any* interface on very constrained devices. -#if TARGET_OS_NANO -#define MulticastInterface(i) (mDNSfalse) +#if BONJOUR_ON_DEMAND +#define MulticastInterface(i) ((i)->m->BonjourEnabled && ((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT)) #else #define MulticastInterface(i) (((i)->ifa_flags & IFF_MULTICAST) && !((i)->ifa_flags & IFF_POINTOPOINT)) #endif +#define SPSInterface(i) ((i)->ifinfo.McastTxRx && !((i)->ifa_flags & IFF_LOOPBACK) && !(i)->D2DInterface) 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 { @@ -1061,20 +1184,67 @@ mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both 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 + 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++; + LogMsg("NotifyOfElusiveBug: %s", title); + LogMsg("NotifyOfElusiveBug: %s", msg); + + // 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)) + { + LogMsg("Suppressing notification early in boot: %d", mDNSPlatformRawTime()); + return; + } #ifndef NO_CFUSERNOTIFICATION - mDNSNotify(title, msg); + static int notifyCount = 0; // To guard against excessive display of warning notifications + if (notifyCount < 5) + { + notifyCount++; + mDNSNotify(title, msg); + } #endif /* NO_CFUSERNOTIFICATION */ + } +// Write a syslog message and display an alert, then if ForceAlerts is set, generate a stack trace +#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1 +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 + *(volatile long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want +#endif +} +#endif + +// Like LogMemCorruption above, but only display the alert if ForceAlerts is set and we're going to generate a stack trace +#if APPLE_OSX_mDNSResponder +mDNSexport void LogFatalError(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); +#if ForceAlerts + NotifyOfElusiveBug("Fatal Error. See /Library/Logs/DiagnosticReports", buffer); + *(volatile long*)0 = 0; // Trick to crash and get a stack trace right here, if that's what we want +#endif +} +#endif + // Returns true if it is an AppleTV based hardware running iOS, false otherwise mDNSlocal mDNSBool IsAppleTV(void) { @@ -1121,7 +1291,6 @@ mDNSlocal void DynamicStoreWrite(int key, const char* subkey, uintptr_t value, s Boolean release_sckey = FALSE; CFDataRef bytes = NULL; CFPropertyListRef plist = NULL; - SCDynamicStoreRef store = NULL; switch ((enum mDNSDynamicStoreSetConfigKey)key) { @@ -1161,29 +1330,20 @@ mDNSlocal void DynamicStoreWrite(int key, const char* subkey, uintptr_t value, s LogMsg("CFDataCreateWithBytesNoCopy of value failed"); goto fin; } - if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes, - kCFPropertyListImmutable, NULL))) + if (NULL == (plist = CFPropertyListCreateWithData(NULL, bytes, kCFPropertyListImmutable, NULL, NULL))) { - LogMsg("CFPropertyListCreateFromXMLData of bytes failed"); + LogMsg("CFPropertyListCreateWithData 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); + SCDynamicStoreSetValue(NULL, sckey, plist); fin: if (NULL != bytes) CFRelease(bytes); if (NULL != plist) CFRelease(plist); - if (NULL != store) - CFRelease(store); if (release_sckey && sckey) CFRelease(sckey); } @@ -1210,17 +1370,18 @@ mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropert if (!subkeyCopy) { LogMsg("mDNSDynamicStoreSetConfig: ERROR subkeyCopy NULL"); + CFRelease(valueCopy); return; } mDNSPlatformMemCopy(subkeyCopy, subkey, len); subkeyCopy[len] = 0; } - dispatch_async(DynamicStoreQueue, ^{ + dispatch_async(dispatch_get_main_queue(), ^{ CFWriteStreamRef stream = NULL; CFDataRef bytes = NULL; - CFStringRef error; CFIndex ret; + KQueueLock(&mDNSStorage); if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL))) { @@ -1228,7 +1389,7 @@ mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropert goto END; } CFWriteStreamOpen(stream); - ret = CFPropertyListWriteToStream(valueCopy, stream, kCFPropertyListBinaryFormat_v1_0, &error); + ret = CFPropertyListWrite(valueCopy, stream, kCFPropertyListBinaryFormat_v1_0, 0, NULL); if (ret == 0) { LogMsg("mDNSDynamicStoreSetConfig : CFPropertyListWriteToStream failed (Could not write property list to stream)"); @@ -1242,7 +1403,6 @@ mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropert 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: @@ -1256,6 +1416,8 @@ mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropert CFRelease(bytes); if (subkeyCopy) mDNSPlatformMemFree(subkeyCopy); + + KQueueUnlock(&mDNSStorage, "mDNSDynamicStoreSetConfig"); }); } @@ -1271,52 +1433,6 @@ mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const 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; @@ -1343,6 +1459,7 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const { if (ifindex == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly); if (ifindex == kDNSServiceInterfaceIndexP2P ) return(mDNSInterface_P2P); + if (ifindex == kDNSServiceInterfaceIndexBLE ) return(mDNSInterface_BLE); if (ifindex == kDNSServiceInterfaceIndexAny ) return(mDNSNULL); NetworkInterfaceInfoOSX* ifi = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); @@ -1363,9 +1480,11 @@ mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange) { NetworkInterfaceInfoOSX *i; + if (id == mDNSInterface_Any ) return(0); if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly); + if (id == mDNSInterface_Unicast ) return(0); if (id == mDNSInterface_P2P ) return(kDNSServiceInterfaceIndexP2P); - if (id == mDNSInterface_Any ) return(0); + if (id == mDNSInterface_BLE ) return(kDNSServiceInterfaceIndexBLE); mDNSu32 scope_id = (mDNSu32)(uintptr_t)id; @@ -1697,56 +1816,74 @@ mDNSlocal void setTrafficClass(int socketfd, mDNSBool useBackgroundTrafficClass) (void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class)); } -mDNSexport void mDNSPlatformSetuDNSSocktOpt(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +mDNSlocal int mDNSPlatformGetSocktFd(void *sockCxt, mDNSTransport_Type transType, mDNSAddr_Type addrType) { - if (src) + if (transType == mDNSTransport_UDP) { - int s; + UDPSocket* sock = (UDPSocket*) sockCxt; + return (addrType == mDNSAddrType_IPv4) ? sock->ss.sktv4 : sock->ss.sktv6; + } + else if (transType == mDNSTransport_TCP) + { + TCPSocket* sock = (TCPSocket*) sockCxt; + return (addrType == mDNSAddrType_IPv4) ? sock->ss.sktv4 : sock->ss.sktv6; + } + else + { + LogInfo("mDNSPlatformGetSocktFd: invalid transport %d", transType); + return kInvalidSocketRef; + } +} - if (dst->type == mDNSAddrType_IPv4) - s = src->ss.sktv4; - else - s = src->ss.sktv6; +mDNSexport void mDNSPlatformSetSocktOpt(void *sockCxt, mDNSTransport_Type transType, mDNSAddr_Type addrType, const DNSQuestion *q) +{ + int sockfd; + char unenc_name[MAX_ESCAPED_DOMAIN_NAME]; - if (q->pid) - { - if (setsockopt(s, SOL_SOCKET, SO_DELEGATED, &q->pid, sizeof(q->pid)) == -1) - LogInfo("mDNSPlatformSetuDNSSocktOpt: Delegate PID failed %s for PID %d", strerror(errno), q->pid); - } - else + // verify passed-in arguments exist and that sockfd is valid + if (q == mDNSNULL || sockCxt == mDNSNULL || (sockfd = mDNSPlatformGetSocktFd(sockCxt, transType, addrType)) < 0) + return; + + if (q->pid) + { + if (setsockopt(sockfd, SOL_SOCKET, SO_DELEGATED, &q->pid, sizeof(q->pid)) == -1) + LogMsg("mDNSPlatformSetSocktOpt: Delegate PID failed %s for PID %d", strerror(errno), q->pid); + } + else + { + if (setsockopt(sockfd, SOL_SOCKET, SO_DELEGATED_UUID, &q->uuid, sizeof(q->uuid)) == -1) + LogMsg("mDNSPlatformSetSocktOpt: Delegate UUID failed %s", strerror(errno)); + } + + // set the domain on the socket + ConvertDomainNameToCString(&q->qname, unenc_name); + if (!(ne_session_set_socket_attributes(sockfd, unenc_name, NULL))) + LogInfo("mDNSPlatformSetSocktOpt: ne_session_set_socket_attributes()-> setting domain failed for %s", unenc_name); + + int nowake = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_NOWAKEFROMSLEEP, &nowake, sizeof(nowake)) == -1) + LogInfo("mDNSPlatformSetSocktOpt: SO_NOWAKEFROMSLEEP failed %s", strerror(errno)); + + if ((q->flags & kDNSServiceFlagsDenyCellular) || (q->flags & kDNSServiceFlagsDenyExpensive)) + { +#if defined(SO_RESTRICT_DENY_CELLULAR) + if (q->flags & kDNSServiceFlagsDenyCellular) { - if (setsockopt(s, SOL_SOCKET, SO_DELEGATED_UUID, &q->uuid, sizeof(q->uuid)) == -1) - LogInfo("mDNSPlatformSetuDNSSocktOpt: Delegate UUID failed %s", strerror(errno)); + int restrictions = 0; + restrictions = SO_RESTRICT_DENY_CELLULAR; + if (setsockopt(sockfd, SOL_SOCKET, SO_RESTRICTIONS, &restrictions, sizeof(restrictions)) == -1) + LogMsg("mDNSPlatformSetSocktOpt: SO_RESTRICT_DENY_CELLULAR failed %s", strerror(errno)); } - -#if defined(SO_NOWAKEFROMSLEEP) - int nowake = 1; - if (setsockopt(s, SOL_SOCKET, SO_NOWAKEFROMSLEEP, &nowake, sizeof(nowake)) == -1) - LogInfo("mDNSPlatformSetuDNSSocktOpt: SO_NOWAKEFROMSLEEP failed %s", strerror(errno)); -#endif - - if (q->DenyOnCellInterface || q->DenyOnExpInterface) - { -#if defined(SO_RESTRICT_DENY_CELLULAR) - if (q->DenyOnCellInterface) - { - int restrictions = 0; - restrictions = SO_RESTRICT_DENY_CELLULAR; - if (setsockopt(s, SOL_SOCKET, SO_RESTRICTIONS, &restrictions, sizeof(restrictions)) == -1) - LogInfo("mDNSPlatformSetuDNSSocktOpt: SO_RESTRICT_DENY_CELLULAR failed %s", strerror(errno)); - } #endif #if defined(SO_RESTRICT_DENY_EXPENSIVE) - if (q->DenyOnExpInterface) - { - int restrictions = 0; - restrictions = SO_RESTRICT_DENY_EXPENSIVE; - if (setsockopt(s, SOL_SOCKET, SO_RESTRICTIONS, &restrictions, sizeof(restrictions)) == -1) - LogInfo("mDNSPlatformSetuDNSSocktOpt: SO_RESTRICT_DENY_EXPENSIVE failed %s", strerror(errno)); - } + if (q->flags & kDNSServiceFlagsDenyExpensive) + { + int restrictions = 0; + restrictions = SO_RESTRICT_DENY_EXPENSIVE; + if (setsockopt(sockfd, SOL_SOCKET, SO_RESTRICTIONS, &restrictions, sizeof(restrictions)) == -1) + LogMsg("mDNSPlatformSetSocktOpt: SO_RESTRICT_DENY_EXPENSIVE failed %s", strerror(errno)); + } #endif - } - } } @@ -1788,51 +1925,40 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms 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)) { - 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 + #ifdef IP_BOUND_IF + const mDNSu32 ifindex = info ? info->scope_id : IFSCOPE_NONE; + setsockopt(s, IPPROTO_IP, IP_BOUND_IF, &ifindex, sizeof(ifindex)); + #else + static int displayed = 0; + if (displayed < 1000) { - 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)); - } + displayed++; + LogInfo("IP_BOUND_IF socket option not defined -- cannot specify interface for unicast packets"); } - #else + #endif + } + else if (info) + { + #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) + if (err < 0 && !m->NetworkChanged) LogMsg("setsockopt - IP_MULTICAST_IF error %.4a %d errno %d (%s)", &info->ifa_v4addr, err, errno, strerror(errno)); - } - #endif + #else + err = setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &info->ifa_v4addr, sizeof(info->ifa_v4addr)); + if (err < 0 && !m->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; @@ -1855,14 +1981,23 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms LogInfo("setsockopt - IPV6_MUTLICAST_IF scopeid %d, not a valid interface", info->scope_id); } } +#ifdef IPV6_BOUND_IF + if (info) // Specify outgoing interface for non-multicast destination + { + if (!mDNSAddrIsDNSMulticast(dst)) + { + if (info->scope_id == 0) + LogInfo("IPV6_BOUND_IF socket option not set -- info %p (%s) scope_id is zero", info, ifa_name); + else + setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF, &info->scope_id, sizeof(info->scope_id)); + } + } +#endif } else { - LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); -#if ForceAlerts - *(long*)0 = 0; -#endif + LogFatalError("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); return mStatus_BadParamErr; } @@ -1893,13 +2028,16 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms 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)); if (!mDNSAddressIsAllDNSLinkGroup(dst)) - if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr); + { + if (errno == EHOSTUNREACH) return(mStatus_HostUnreachErr); + if (errno == EHOSTDOWN || errno == ENETDOWN || 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 ) // 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 == EADDRNOTAVAIL && m->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)); @@ -1917,14 +2055,6 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms 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); } @@ -2006,6 +2136,7 @@ mDNSexport ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, return(n); } +// What is this for, and why does it use xor instead of a simple quality check? -- SC mDNSlocal mDNSInterfaceID FindMyInterface(mDNS *const m, const mDNSAddr *addr) { NetworkInterfaceInfo *intf; @@ -2043,27 +2174,6 @@ mDNSlocal mDNSInterfaceID FindMyInterface(mDNS *const m, const mDNSAddr *addr) 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; @@ -2081,7 +2191,7 @@ mDNSexport void myKQSocketCallBack(int s1, short filter, void *context) while (!closed) { - mDNSAddr senderAddr, destAddr; + mDNSAddr senderAddr, destAddr = zeroAddr; mDNSIPPort senderPort; struct sockaddr_storage from; size_t fromlen = sizeof(from); @@ -2152,12 +2262,12 @@ mDNSexport void myKQSocketCallBack(int s1, short filter, void *context) if (ss->proxy) { - m->p->UDPProxyCallback(m, &m->p->UDPProxy, (unsigned char *)&m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, + m->p->UDPProxyCallback(m, &m->p->UDPProxy, &m->imsg.m, (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); + mDNSCoreReceive(m, &m->imsg.m, (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 @@ -2165,6 +2275,15 @@ mDNSexport void myKQSocketCallBack(int s1, short filter, void *context) if (!closed) ss->closeFlag = mDNSNULL; } + // If a client application is put in the background, it's socket to us can go defunct and + // we'll get an ENOTCONN error on that connection. Just close the socket in that case. + if (err < 0 && errno == ENOTCONN) + { + LogInfo("myKQSocketCallBack: ENOTCONN, closing socket"); + close(s1); + return; + } + if (err < 0 && (errno != EWOULDBLOCK || count == 0)) { // Something is busted here. @@ -2619,16 +2738,16 @@ mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort 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; } + if (err < 0) { LogMsg("ERROR: bind %s", strerror(errno)); close(skt); 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; } + if (err < 0) { LogMsg("setsockopt IP_RECVIF - %s", strerror(errno)); close(skt); 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; } + if (err < 0) { LogMsg("getsockname - %s", strerror(errno)); close(skt); return err; } port->NotAnInteger = addr.sin_port; } @@ -2640,16 +2759,16 @@ mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort 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; } + if (err < 0) { LogMsg("ERROR: bind6 %s", strerror(errno)); close(skt); 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; } + if (err < 0) { LogMsg("ERROR: setsockopt IPV6_RECVPKTINFO %s", strerror(errno)); close(skt); 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; } + if (err < 0) { LogMsg("getsockname6 - %s", strerror(errno)); close(skt); return err; } port->NotAnInteger = addr6.sin6_port; @@ -3045,6 +3164,14 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa if (err < 0) { errstr = "setsockopt - SO_REUSEPORT"; goto fail; } } + // Don't want to wake from sleep for inbound packets on the mDNS sockets + if (mDNSSameIPPort(port, MulticastDNSPort)) + { + int nowake = 1; + if (setsockopt(skt, SOL_SOCKET, SO_NOWAKEFROMSLEEP, &nowake, sizeof(nowake)) == -1) + LogInfo("SetupSocket: SO_NOWAKEFROMSLEEP failed %s", strerror(errno)); + } + if (sa_family == AF_INET) { // We want to receive destination addresses @@ -3057,7 +3184,7 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa // 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 + if (err < 0) { errstr = "setsockopt - IP_RECVTTL"; goto fail; } // Send unicast packets with TTL 255 err = setsockopt(skt, IPPROTO_IP, IP_TTL, &twofivefive, sizeof(twofivefive)); @@ -3079,7 +3206,7 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa 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; } + if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort; close(skt); return mStatus_NoError; } // We want to receive destination addresses and receive interface identifiers err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); @@ -3139,7 +3266,7 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa #endif KQueueSet(*s, EV_ADD, EVFILT_READ, k); - return(err); + return(mStatus_NoError); fail: // For "bind" failures, only write log messages for our shared mDNS port, or for binding to zero @@ -3247,7 +3374,7 @@ mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSA 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)) + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa)) LogSPS("Don't need address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); else { @@ -3268,7 +3395,7 @@ mDNSlocal void CloseBPF(NetworkInterfaceInfoOSX *const i) // 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); + CFRunLoopRemoveSource(CFRunLoopGetMain(), i->BPF_rls, kCFRunLoopDefaultMode); CFRelease(i->BPF_rls); CFSocketInvalidate(i->BPF_cfs); CFRelease(i->BPF_cfs); @@ -3338,46 +3465,382 @@ mDNSlocal void bpf_callback(const CFSocketRef cfs, const CFSocketCallBackType Ca (void)data; bpf_callback_common((NetworkInterfaceInfoOSX *)context); } -#endif +#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 mDNSPlatformClearSPSData(void) +{ + CFStringRef spsAddress = NULL; + CFStringRef ownerOPTRec = NULL; + + if ((spsAddress = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", "[^/]", "/BonjourSleepProxyAddress"))) + { + if (SCDynamicStoreRemoveValue(NULL, spsAddress) == false) + LogSPS("mDNSPlatformClearSPSData: Unable to remove sleep proxy address key"); + } + + if((ownerOPTRec = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", "[^/]", "/BonjourSleepProxyOPTRecord"))) + { + if (SCDynamicStoreRemoveValue(NULL, ownerOPTRec) == false) + LogSPS("mDNSPlatformClearSPSData: Unable to remove sleep proxy owner option record key"); + } + + if (spsAddress) CFRelease(spsAddress); + if (ownerOPTRec) CFRelease(ownerOPTRec); + return KERN_SUCCESS; +} + +mDNSlocal 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) + { + LogMsg("getMACAddress: 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) + { + LogMsg("getMACAddress: 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) + LogMsg("getMACAddress: 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 (!sdl) + { + LogMsg("getMACAddress: sdl is NULL for family %d", family); + close(sock); + return -1; + } + + // 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) + { + if (sin) + { + struct sockaddr_in *new_sin = (struct sockaddr_in *)(sin->sin_len +(char*) sin); + memcpy(gaddr, &new_sin->sin_addr, sizeof(struct in_addr)); + } + else + { + LogMsg("getMACAddress: sin is NULL"); + } + close(sock); + return -1; + } + else if (sdl->sdl_family == AF_INET6) + { + if (sin6) + { + struct sockaddr_in6 *new_sin6 = (struct sockaddr_in6 *)(sin6->sin6_len +(char*) sin6); + memcpy(gaddr, &new_sin6->sin6_addr, sizeof(struct in6_addr)); + } + else + { + LogMsg("getMACAddress: sin6 is NULL"); + } + 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; +} + +mDNSlocal int GetRemoteMacinternal(int family, v6addr_t raddr, ethaddr_t eth) +{ + int ret = 0; + v6addr_t gateway; + int gfamily = 0; + int count = 0; + + 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; +} + +mDNSlocal int StoreSPSMACAddressinternal(int family, v6addr_t spsaddr, const char *ifname) +{ + 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 ((store == NULL) || (ipstore == NULL)) + { + LogMsg("StoreSPSMACAddressinternal: Unable to accesss SC Dynamic Store"); + ret = -1; + goto fin; + } + + // Get the MAC address of the Sleep Proxy Server + memset(eth, 0, sizeof(eth)); + ret = GetRemoteMacinternal(family, spsaddr, eth); + if (ret != 0) + { + LogMsg("StoreSPSMACAddressinternal: 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) + { + LogMsg("StoreSPSMACAddressinternal: SPSCreateDict() Could not create CFDictionary dict"); + ret = -1; + 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))) + { + LogMsg("StoreSPSMACAddressinternal: inet_ntop failed: %s", strerror(errno)); + ret = -1; + 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); + + return ret; +} -mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win) +mDNSlocal void mDNSStoreSPSMACAddress(int family, v6addr_t spsaddr, char *ifname) { - LogMsg("mDNSPlatformSendKeepalive called\n"); - mDNSSendKeepalive(sadd->ip.v6.b, dadd->ip.v6.b, lport->NotAnInteger, rport->NotAnInteger, seq, ack, win); + struct + { + v6addr_t saddr; + } addr; + int err = 0; + + mDNSPlatformMemCopy(addr.saddr, spsaddr, sizeof(v6addr_t)); + + err = StoreSPSMACAddressinternal(family, (uint8_t *)addr.saddr, ifname); + if (err != 0) + LogMsg("mDNSStoreSPSMACAddress : failed"); +} + +mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) +{ + int family = (spsaddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + + LogInfo("mDNSPlatformStoreSPSMACAddr : Storing %#a on interface %s", spsaddr, ifname); + mDNSStoreSPSMACAddress(family, spsaddr->ip.v6.b, ifname); + + return KERN_SUCCESS; } -mDNSexport mStatus mDNSPlatformClearSPSMACAddr(void) + +mDNSexport mStatus mDNSPlatformStoreOwnerOptRecord(char *ifname, DNSMessage* msg, int length) { - SCDynamicStoreRef store = NULL; - CFStringRef entityname = NULL; + int ret = 0; + CFStringRef sckey = NULL; + SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:StoreOwnerOPTRecord"), NULL, NULL); + CFMutableDictionaryRef dict = NULL; - if ((store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:ClearSPSMACAddress"), NULL, NULL))) + if (store == 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"); - } + LogMsg("mDNSPlatformStoreOwnerOptRecord: Unable to accesss SC Dynamic Store"); + ret = -1; + goto fin; } - if (entityname) CFRelease(entityname); - if (store) CFRelease(store); - return KERN_SUCCESS; + // Create/Update the dynamic store entry for the specified interface + sckey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", ifname, "/BonjourSleepProxyOPTRecord"); + dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) + { + LogMsg("mDNSPlatformStoreOwnerOptRecord: Could not create CFDictionary dictionary to store OPT Record"); + ret =-1; + goto fin; + } + + CFDataRef optRec = NULL; + optRec = CFDataCreate(NULL, (const uint8_t *)msg, (CFIndex)length); + CFDictionarySetValue(dict, CFSTR("OwnerOPTRecord"), optRec); + if (NULL != optRec) CFRelease(optRec); + + SCDynamicStoreSetValue(store, sckey, dict); + +fin: + if (NULL != store) CFRelease(store); + if (NULL != sckey) CFRelease(sckey); + if (NULL != dict) CFRelease(dict); + return ret; } -mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) +mDNSlocal void mDNSGet_RemoteMAC(mDNS *const m, int family, v6addr_t raddr) { - 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; + ethaddr_t eth; + IPAddressMACMapping *addrMapping; + int kr = KERN_FAILURE; + struct + { + v6addr_t addr; + } dst; + + mDNSPlatformMemCopy(dst.addr, raddr, sizeof(v6addr_t)); + + kr = GetRemoteMacinternal(family, (uint8_t *)dst.addr, eth); + + // 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 == 0) + { + addrMapping = mDNSPlatformMemAllocate(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, raddr, sizeof(v6addr_t)); + } + else + { + addrMapping->ipaddr.type = mDNSAddrType_IPv6; + mDNSPlatformMemCopy(addrMapping->ipaddr.ip.v6.b, raddr, sizeof(v6addr_t)); + } + UpdateRMAC(m, addrMapping); + } } 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); + + LogInfo("mDNSPlatformGetRemoteMacAddr calling mDNSGet_RemoteMAC"); + mDNSGet_RemoteMAC(m, family, raddr->ip.v6.b); + return KERN_SUCCESS; } @@ -3428,7 +3891,7 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID 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; + for (x = m->p->InterfaceList; x; x = x->next) if ((x->ifinfo.InterfaceID == InterfaceID) && (x->BPF_fd >= 0)) break; if (!x) { LogMsg("mDNSPlatformUpdateProxyList: ERROR InterfaceID %p not found", InterfaceID); return; } @@ -3578,7 +4041,7 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID 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); + if (!m->NetworkChanged) m->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 } @@ -3654,7 +4117,7 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) 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); + CFRunLoopAddSource(CFRunLoopGetMain(), i->BPF_rls, kCFRunLoopDefaultMode); #endif mDNSPlatformUpdateProxyList(m, i->ifinfo.InterfaceID); } @@ -3671,46 +4134,48 @@ mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) #endif #ifndef NO_SECURITYFRAMEWORK -mDNSlocal CFArrayRef GetCertChain(SecIdentityRef identity) +mDNSlocal CFArrayRef CopyCertChain(SecIdentityRef identity) { CFMutableArrayRef certChain = NULL; - if (!identity) { LogMsg("getCertChain: identity is NULL"); return(NULL); } + if (!identity) { LogMsg("CopyCertChain: identity is NULL"); return(NULL); } SecCertificateRef cert; OSStatus err = SecIdentityCopyCertificate(identity, &cert); - if (err || !cert) LogMsg("getCertChain: SecIdentityCopyCertificate() returned %d", (int) err); + if (err || !cert) LogMsg("CopyCertChain: SecIdentityCopyCertificate() returned %d", (int) err); else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" SecPolicySearchRef searchRef; err = SecPolicySearchCreate(CSSM_CERT_X_509v3, &CSSMOID_APPLE_X509_BASIC, NULL, &searchRef); - if (err || !searchRef) LogMsg("getCertChain: SecPolicySearchCreate() returned %d", (int) err); + if (err || !searchRef) LogMsg("CopyCertChain: SecPolicySearchCreate() returned %d", (int) err); else { SecPolicyRef policy; err = SecPolicySearchCopyNext(searchRef, &policy); - if (err || !policy) LogMsg("getCertChain: SecPolicySearchCopyNext() returned %d", (int) err); + if (err || !policy) LogMsg("CopyCertChain: SecPolicySearchCopyNext() returned %d", (int) err); else { CFArrayRef wrappedCert = CFArrayCreate(NULL, (const void**) &cert, 1, &kCFTypeArrayCallBacks); - if (!wrappedCert) LogMsg("getCertChain: wrappedCert is NULL"); + if (!wrappedCert) LogMsg("CopyCertChain: wrappedCert is NULL"); else { SecTrustRef trust; err = SecTrustCreateWithCertificates(wrappedCert, policy, &trust); - if (err || !trust) LogMsg("getCertChain: SecTrustCreateWithCertificates() returned %d", (int) err); + if (err || !trust) LogMsg("CopyCertChain: SecTrustCreateWithCertificates() returned %d", (int) err); else { err = SecTrustEvaluate(trust, NULL); - if (err) LogMsg("getCertChain: SecTrustEvaluate() returned %d", (int) err); + if (err) LogMsg("CopyCertChain: 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); + if (err || !rawCertChain || !statusChain) LogMsg("CopyCertChain: SecTrustGetResult() returned %d", (int) err); else { certChain = CFArrayCreateMutableCopy(NULL, 0, rawCertChain); - if (!certChain) LogMsg("getCertChain: certChain is NULL"); + if (!certChain) LogMsg("CopyCertChain: certChain is NULL"); else { // Replace the SecCertificateRef at certChain[0] with a SecIdentityRef per documentation for SSLSetCertificate: @@ -3734,6 +4199,7 @@ mDNSlocal CFArrayRef GetCertChain(SecIdentityRef identity) } CFRelease(searchRef); } +#pragma clang diagnostic pop CFRelease(cert); } return certChain; @@ -3749,6 +4215,8 @@ mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) SecIdentitySearchRef srchRef = nil; OSStatus err; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // 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); @@ -3756,13 +4224,14 @@ mDNSexport mStatus mDNSPlatformTLSSetupCerts(void) err = SecIdentitySearchCopyNext(srchRef, &identity); if (err) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: SecIdentitySearchCopyNext returned %d", (int) err); return err; } +#pragma clang diagnostic pop 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; } + // Found one. Call CopyCertChain to create the correct certificate chain. + ServerCerts = CopyCertChain(identity); + if (ServerCerts == nil) { LogMsg("ERROR: mDNSPlatformTLSSetupCerts: CopyCertChain error"); return mStatus_UnknownErr; } return mStatus_NoError; #endif /* NO_SECURITYFRAMEWORK */ @@ -3841,26 +4310,22 @@ mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) 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) { - CFStringRef entityname = CFStringCreateWithFormat(NULL, NULL, CFSTR("State:/Network/Interface/%s/AirPort"), ifa_name); - if (entityname) + CFDictionaryRef dict = SCDynamicStoreCopyValue(NULL, entityname); + if (dict) { - 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); + 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(store); + CFRelease(entityname); } + return(eth); } @@ -3940,8 +4405,8 @@ mDNSlocal mDNSBool InterfaceSupportsKeepAlive(NetworkInterfaceInfo *const intf) 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 + // We only use Sleep Proxy Service on multicast-capable interfaces, except loopback and D2D. + if (!SPSInterface(i)) return(mDNSfalse); // 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 @@ -4005,6 +4470,139 @@ mDNSlocal u_int64_t getExtendedFlags(char * ifa_name) return ifr.ifr_eflags; } +#if TARGET_OS_IPHONE + +// Function pointers for the routines we use in the MobileWiFi framework. +static WiFiManagerClientRef (*WiFiManagerClientCreate_p)(CFAllocatorRef allocator, WiFiClientType type) = mDNSNULL; +static CFArrayRef (*WiFiManagerClientCopyDevices_p)(WiFiManagerClientRef manager) = mDNSNULL; +static WiFiNetworkRef (*WiFiDeviceClientCopyCurrentNetwork_p)(WiFiDeviceClientRef device) = mDNSNULL; +static bool (*WiFiNetworkIsCarPlay_p)(WiFiNetworkRef network) = mDNSNULL; + +mDNSlocal mDNSBool MobileWiFiLibLoad(void) +{ + static mDNSBool isInitialized = mDNSfalse; + static void *MobileWiFiLib_p = mDNSNULL; + static const char path[] = "/System/Library/PrivateFrameworks/MobileWiFi.framework/MobileWiFi"; + + if (!isInitialized) + { + if (!MobileWiFiLib_p) + { + MobileWiFiLib_p = dlopen(path, RTLD_LAZY | RTLD_LOCAL); + if (!MobileWiFiLib_p) + { + LogInfo("MobileWiFiLibLoad: dlopen() failed."); + goto exit; + } + } + + if (!WiFiManagerClientCreate_p) + { + WiFiManagerClientCreate_p = dlsym(MobileWiFiLib_p, "WiFiManagerClientCreate"); + if (!WiFiManagerClientCreate_p) + { + LogInfo("MobileWiFiLibLoad: load of WiFiManagerClientCreate symbol failed."); + goto exit; + } + } + + if (!WiFiManagerClientCopyDevices_p) + { + WiFiManagerClientCopyDevices_p = dlsym(MobileWiFiLib_p, "WiFiManagerClientCopyDevices"); + if (!WiFiManagerClientCopyDevices_p) + { + LogInfo("MobileWiFiLibLoad: load of WiFiManagerClientCopyDevices symbol failed."); + goto exit; + } + } + + if (!WiFiDeviceClientCopyCurrentNetwork_p) + { + WiFiDeviceClientCopyCurrentNetwork_p = dlsym(MobileWiFiLib_p, "WiFiDeviceClientCopyCurrentNetwork"); + if (!WiFiDeviceClientCopyCurrentNetwork_p) + { + LogInfo("MobileWiFiLibLoad: load of WiFiDeviceClientCopyCurrentNetwork symbol failed."); + goto exit; + } + } + + if (!WiFiNetworkIsCarPlay_p) + { + WiFiNetworkIsCarPlay_p = dlsym(MobileWiFiLib_p, "WiFiNetworkIsCarPlay"); + if (!WiFiNetworkIsCarPlay_p) + { + LogInfo("MobileWiFiLibLoad: load of WiFiNetworkIsCarPlay symbol failed."); + goto exit; + } + } + + isInitialized = mDNStrue; + } + +exit: + return isInitialized; +} + +// Return true if the interface is associate to a CarPlay hosted SSID. +mDNSlocal mDNSBool IsCarPlaySSID(char *ifa_name) +{ + static WiFiManagerClientRef manager = NULL; + mDNSBool rvalue = mDNSfalse; + + if (!MobileWiFiLibLoad()) + return mDNSfalse; + + // If we have associated with a CarPlay hosted SSID, then use the same + // optimizations that are used if an interface has the IFEF_DIRECTLINK flag set. + + // Get one WiFiManagerClientRef to use for all calls. + if (manager == NULL) + manager = WiFiManagerClientCreate_p(NULL, kWiFiClientTypeNormal); + + if (manager == NULL) + { + LogInfo("IsCarPlaySSID: WiFiManagerClientCreate() failed!"); + } + else + { + CFArrayRef devices; + + devices = WiFiManagerClientCopyDevices_p(manager); + if (devices != NULL) + { + WiFiDeviceClientRef device; + WiFiNetworkRef network; + + device = (WiFiDeviceClientRef)CFArrayGetValueAtIndex(devices, 0); + network = WiFiDeviceClientCopyCurrentNetwork_p(device); + if (network != NULL) + { + if (WiFiNetworkIsCarPlay_p(network)) + { + LogInfo("%s is CarPlay hosted", ifa_name); + rvalue = mDNStrue; + } + CFRelease(network); + } + CFRelease(devices); + } + } + + return rvalue; +} + +#else // TARGET_OS_IPHONE + +mDNSlocal mDNSBool IsCarPlaySSID(char *ifa_name) +{ + (void)ifa_name; // unused + + // OSX WifiManager currently does not implement WiFiNetworkIsCarPlay() + return mDNSfalse;; +} + +#endif // TARGET_OS_IPHONE + // 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 @@ -4050,7 +4648,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad { mDNS_Lock(m); if (NetWake) mDNS_ActivateNetWake_internal (m, &(*p)->ifinfo); - else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo); + else mDNS_DeactivateNetWake_internal(m, &(*p)->ifinfo); mDNS_Unlock(m); } } @@ -4075,7 +4673,13 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad 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; + + // Setting DirectLink indicates we can do the optimization of skipping the probe phase + // for the interface address records since they should be unique. + if (eflags & IFEF_DIRECTLINK) + i->ifinfo.DirectLink = mDNStrue; + else + i->ifinfo.DirectLink = IsCarPlaySSID(ifa->ifa_name); i->next = mDNSNULL; i->m = m; @@ -4085,9 +4689,18 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad i->D2DInterface = (eflags & IFEF_LOCALNET_PRIVATE) ? mDNStrue: mDNSfalse; if (eflags & IFEF_AWDL) { + // Set SupportsUnicastMDNSResponse false for the AWDL interface since unicast reserves + // limited AWDL resources so we don't set the kDNSQClass_UnicastResponse bit in + // Bonjour requests over the AWDL interface. + i->ifinfo.SupportsUnicastMDNSResponse = mDNSfalse; AWDLInterfaceID = i->ifinfo.InterfaceID; + i->ifinfo.DirectLink = mDNStrue; LogInfo("AddInterfaceToList: AWDLInterfaceID = %d", (int) AWDLInterfaceID); } + else + { + i->ifinfo.SupportsUnicastMDNSResponse = mDNStrue; + } i->AppearanceTime = utc; // Brand new interface; AppearanceTime is now i->LastSeen = utc; i->ifa_flags = ifa->ifa_flags; @@ -4100,7 +4713,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad i->Registered = mDNSNULL; // Do this AFTER i->BSSID has been set up - i->ifinfo.NetWake = NetWakeInterface(i); + i->ifinfo.NetWake = (eflags & IFEF_EXPENSIVE)? mDNSfalse : 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); @@ -4203,7 +4816,6 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut 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; @@ -4222,7 +4834,7 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut if (!dict) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFDictionary dict"); return; } - buflen = mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); + mDNS_snprintf(buffer, sizeof(buffer), "%##s", info->domain.c); domain = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); if (!domain) { LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString domain"); return; } @@ -4357,7 +4969,7 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut else if (llqStatus == mStatus_NoSuchRecord) { status = llqStatus; - mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); + mDNS_snprintf(buffer, sizeof(buffer), "%s", llqBuffer); } else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT)) { @@ -4384,7 +4996,7 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut else { status = llqStatus; - mDNS_snprintf(buffer, sizeof(buffer), llqBuffer); + mDNS_snprintf(buffer, sizeof(buffer), "%s", llqBuffer); LogInfo("UpdateAutoTunnelDomainStatus: LLQ Status %d, %s", status, buffer); } @@ -4451,8 +5063,7 @@ mDNSlocal void UpdateAnonymousRacoonConfig(mDNS *m) // Determine whether we 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); + LogInfo("UpdateAnonymousRacoonConfig need not be done in mDNSResponder"); } } @@ -5190,8 +5801,6 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) p->q.ForceMCast = mDNSfalse; p->q.ReturnIntermed = mDNStrue; p->q.SuppressUnusable = mDNSfalse; - p->q.DenyOnCellInterface = mDNSfalse; - p->q.DenyOnExpInterface = mDNSfalse; p->q.SearchListIndex = 0; p->q.AppendSearchDomains = 0; p->q.RetryWithSearchDomains = mDNSfalse; @@ -5204,6 +5813,7 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) p->q.qnameOrig = mDNSNULL; p->q.AnonInfo = mDNSNULL; p->q.pid = mDNSPlatformGetPID(); + p->q.euid = 0; p->q.QuestionCallback = AutoTunnelCallback; p->q.QuestionContext = p; @@ -5222,7 +5832,7 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) { mDNSBool foundav4 = mDNSfalse; mDNSBool foundav6 = mDNSfalse; - struct ifaddrs *ifa = myGetIfAddrs(1); + struct ifaddrs *ifa = myGetIfAddrs(0); struct ifaddrs *v4Loopback = NULL; struct ifaddrs *v6Loopback = NULL; char defaultname[64]; @@ -5308,7 +5918,7 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) 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 (!(ifru_flags6 & (IN6_IFF_TENTATIVE | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) { if (ifa->ifa_flags & IFF_LOOPBACK) { @@ -5402,14 +6012,14 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) 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 { +#if APPLE_OSX_mDNSResponder DomainAuthInfo *info; for (info = m->AuthInfoList; info; info = info->next) if (info->AutoTunnel) AutoTunnelHostNameChanged(m, info); - } #endif // APPLE_OSX_mDNSResponder + } return(mStatus_NoError); } @@ -5430,11 +6040,14 @@ mDNSlocal int CountMaskBits(mDNSAddr *mask) return(bits); } -// returns count of non-link local V4 addresses registered +// Returns count of non-link local V4 addresses registered (why? -- SC) mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) { NetworkInterfaceInfoOSX *i; int count = 0; + + // Recalculate SuppressProbes time based on the current set of active interfaces. + m->SuppressProbes = 0; for (i = m->p->InterfaceList; i; i = i->next) if (i->Exists) { @@ -5452,9 +6065,7 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) { // 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 - // - + // 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. @@ -5463,7 +6074,7 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) 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 + // every time 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. @@ -5488,7 +6099,42 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) 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); +#if TARGET_OS_EMBEDDED + // We join the Bonjour multicast group on Apple embedded platforms ONLY when a client request is active, + // so we leave the multicast group here to clear any residual group membership. + 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 (SearchForInterfaceByName(m, i->ifinfo.ifname, AF_INET) == i) + { + LogInfo("SetupActiveInterfaces: %5s(%lu) Doing 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)); + } + } + 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 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); + } + } +#endif // TARGET_OS_EMBEDDED + } else { if (i->sa_family == AF_INET) @@ -5559,7 +6205,7 @@ mDNSlocal void MarkAllInterfacesInactive(mDNS *const m, mDNSs32 utc) } } -// returns count of non-link local V4 addresses deregistered +// Returns count of non-link local V4 addresses deregistered (why? -- SC) mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) { // First pass: @@ -5585,7 +6231,7 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) 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 + // every time 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. @@ -5731,21 +6377,38 @@ mDNSexport const char *DNSScopeToString(mDNSu32 scope) } } -mDNSlocal void ConfigSearchDomains(mDNS *const m, dns_resolver_t *resolver, mDNSInterfaceID interface, mDNSu32 scope, MD5_CTX *sdc) +mDNSlocal void ConfigSearchDomains(mDNS *const m, dns_resolver_t *resolver, mDNSInterfaceID interfaceId, mDNSu32 scope, MD5_CTX *sdc, uint64_t generation) { const char *scopeString = DNSScopeToString(scope); int j; + domainname d; + + if (scope == kScopeNone) + interfaceId = mDNSInterface_Any; - if (scope != kScopeNone) + if (scope == kScopeNone || scope == kScopeInterfaceID) { - LogInfo("ConfigSearchDomains: (%s) Ignoring search domain for Interface %p", scopeString, interface); - return; + for (j = 0; j < resolver->n_search; j++) + { + if (MakeDomainNameFromDNSNameString(&d, resolver->search[j]) != NULL) + { + static char interface_buf[32]; + mDNS_snprintf(interface_buf, sizeof(interface_buf), "for interface %s", InterfaceNameForID(m, interfaceId)); + LogInfo("ConfigSearchDomains: (%s) configuring search domain %s %s (generation= %llu)", scopeString, + resolver->search[j], (interfaceId == mDNSInterface_Any) ? "" : interface_buf, generation); + UpdateSearchDomainHash(m, sdc, resolver->search[j], interfaceId); + mDNS_AddSearchDomain_CString(resolver->search[j], interfaceId); + } + else + { + LogInfo("ConfigSearchDomains: An invalid search domain was detected for %s domain %s n_nameserver %d, (generation= %llu)", + DNSScopeToString(scope), resolver->domain, resolver->n_nameserver, generation); + } + } } - for (j = 0; j < resolver->n_search; j++) + else { - 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); + LogInfo("ConfigSearchDomains: (%s) Ignoring search domain for interface %s", scopeString, InterfaceNameForID(m,interfaceId)); } } @@ -5801,7 +6464,6 @@ mDNSlocal void ConfigDNSServers(mDNS *const m, dns_resolver_t *r, mDNSInterfaceI domainname d; int serviceID = 0; mDNSBool cellIntf = mDNSfalse; - mDNSBool scopedDNS = mDNSfalse; mDNSBool reqA, reqAAAA; if (!r->domain || !*r->domain) @@ -5814,11 +6476,7 @@ mDNSlocal void ConfigDNSServers(mDNS *const m, dns_resolver_t *r, mDNSInterfaceI return; } // Parse the resolver specific attributes that affects all the DNS servers. - if (scope == kScopeInterfaceID) - { - scopedDNS = mDNStrue; - } - else if (scope == kScopeServiceID) + if (scope == kScopeServiceID) { serviceID = r->service_identifier; } @@ -5913,7 +6571,8 @@ mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSu32 scop if (setsearch) { - ConfigSearchDomains(m, resolver[i], interface, scope, sdc); + ConfigSearchDomains(m, resolver[i], interface, scope, sdc, config->generation); + // Parse other scoped resolvers for search lists if (!setservers) continue; @@ -6107,124 +6766,114 @@ mDNSlocal void SetupActiveDirectoryDomain(dns_config_t *config) 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 + char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NULL domainname d; - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:SetupDDNSDomains"), NULL, NULL); - if (!store) + CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(NULL, NetworkChangedKey_DynamicDNS); + if (ddnsdict) { - LogMsg("SetupDDNSDomains: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - } - else - { - CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS); - if (ddnsdict) + if (fqdn) { - if (fqdn) + CFArrayRef fqdnArray = CFDictionaryGetValue(ddnsdict, CFSTR("HostNames")); + if (fqdnArray && CFArrayGetCount(fqdnArray) > 0) { - 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)) { - // 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) { - 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 (!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) + } + if (RegDomains) + { + CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains")); + if (regArray && CFArrayGetCount(regArray) > 0) { - CFArrayRef regArray = CFDictionaryGetValue(ddnsdict, CFSTR("RegistrationDomains")); - if (regArray && CFArrayGetCount(regArray) > 0) + CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0); + if (regDict && DictionaryIsEnabled(regDict)) { - CFDictionaryRef regDict = CFArrayGetValueAtIndex(regArray, 0); - if (regDict && DictionaryIsEnabled(regDict)) + CFStringRef name = CFDictionaryGetValue(regDict, CFSTR("Domain")); + if (name) { - 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 { - 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); - } + debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS registration domain: %s", buf); + AppendDNameListElem(&RegDomains, 0, &d); } } } } - - if (BrowseDomains) + } + if (BrowseDomains) + { + CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains")); + if (browseArray) { - CFArrayRef browseArray = CFDictionaryGetValue(ddnsdict, CFSTR("BrowseDomains")); - if (browseArray) + for (i = 0; i < CFArrayGetCount(browseArray); i++) { - for (i = 0; i < CFArrayGetCount(browseArray); i++) + CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i); + if (browseDict && DictionaryIsEnabled(browseDict)) { - CFDictionaryRef browseDict = CFArrayGetValueAtIndex(browseArray, i); - if (browseDict && DictionaryIsEnabled(browseDict)) + CFStringRef name = CFDictionaryGetValue(browseDict, CFSTR("Domain")); + if (name) { - 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 { - 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); - } + debugf("GetUserSpecifiedDDNSConfig SCDynamicStore DDNS browsing domain: %s", buf); + AppendDNameListElem(&BrowseDomains, 0, &d); } } } } } - CFRelease(ddnsdict); } - - if (RegDomains) + CFRelease(ddnsdict); + } + if (RegDomains) + { + CFDictionaryRef btmm = SCDynamicStoreCopyValue(NULL, NetworkChangedKey_BackToMyMac); + if (btmm) { - 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++) { - 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 { - 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]) { - 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); - } + LogInfo("BackToMyMac %d %d %##s", i, uid, d.c); + AppendDNameListElem(&RegDomains, uid, &d); } } - CFRelease(btmm); } + CFRelease(btmm); } - CFRelease(store); } + } // Returns mDNSfalse, if it does not set the configuration i.e., if the DNS configuration did not change @@ -6296,7 +6945,6 @@ mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, 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. @@ -6306,9 +6954,20 @@ mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, } else { - LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d, generation %llu", config->n_resolver, config->generation); - // SameDomainName check below is to fix Dynamic DNS hostname changes not noticed - if (m->p->LastConfigGeneration == config->generation && (!fqdn || (SameDomainName(fqdn, &m->FQDN)))) + LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d, generation %llu, last %llu", config->n_resolver, config->generation, m->p->LastConfigGeneration); + + // For every network change, the search domain list is updated. + // This update is done without regard for generation number because it is + // not an expensive update and it keeps the search domain list in sync (even when + // a network change occurs, while currently processing a network + // change). + // + // For every DNS configuration change, the DNS server list is updated. + // This update is NOT done every network change because it may involve + // updating cache entries which worst-case is expensive. Setting the generation + // per DNS server list change keeps the list in sync with configd. + + if (setservers && m->p->LastConfigGeneration == config->generation) { LogInfo("mDNSPlatformSetDNSConfig: generation number %llu same, not processing", config->generation); dns_configuration_free(config); @@ -6334,29 +6993,19 @@ mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, 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. + // Acking provides a hint to other processes that the current DNS configuration has completed + // its update. When configd receives the ack, it publishes a notification. + // Applications monitoring the notification then know when to re-issue their DNS queries + // after a network change occurs. 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 @@ -6369,81 +7018,78 @@ mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, { 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(NULL, NetworkChangedKey_IPv4); + if (dict) { - CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_IPv4); - if (dict) + r->type = mDNSAddrType_IPv4; + r->ip.v4 = zerov4Addr; + CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetIPv4Router); + if (string) { - 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 { - 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; - } + 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; - string = CFDictionaryGetValue(dict, kSCDynamicStorePropNetPrimaryInterface); - if (string) + 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)) { - 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)) { - mDNSAddr tmp6 = zeroAddr; - if (!strcmp(buf, ifa->ifa_name)) + if (ifa->ifa_addr->sa_family == AF_INET) { - 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; } - } + if (mDNSIPv4AddressIsZero(v4->ip.v4) || mDNSv4AddressIsLinkLocal(&v4->ip.v4)) + SetupAddr(v4, ifa->ifa_addr); } - else + else if (ifa->ifa_addr->sa_family == AF_INET6) { - // 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; + SetupAddr(&tmp6, ifa->ifa_addr); + if (tmp6.ip.v6.b[0] >> 5 == 1) // global prefix: 001 + { + HavePrimaryGlobalv6 = mDNStrue; + *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 + 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); + CFRelease(dict); } return mStatus_NoError; } @@ -6644,7 +7290,7 @@ mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect"); } } -#else +#elif !TARGET_OS_EMBEDDED mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) { (void) m; // Unused @@ -6652,12 +7298,14 @@ mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) } #endif // ! NO_AWACS +#if !TARGET_OS_EMBEDDED mDNSlocal void ProcessConndConfigChanges(mDNS *const m); +#endif #endif // APPLE_OSX_mDNSResponder // MUST be called holding the lock -mDNSexport void SetDomainSecrets(mDNS *m) +mDNSlocal void SetDomainSecrets_internal(mDNS *m) { #ifdef NO_SECURITYFRAMEWORK (void) m; @@ -6923,6 +7571,15 @@ mDNSexport void SetDomainSecrets(mDNS *m) #endif /* NO_SECURITYFRAMEWORK */ } +mDNSexport void SetDomainSecrets(mDNS *m) +{ +#if DEBUG + // Don't get secrets for BTMM if running in debug mode + if (!IsDebugSocketInUse()) +#endif + SetDomainSecrets_internal(m); +} + mDNSlocal void SetLocalDomains(void) { CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); @@ -6941,57 +7598,20 @@ mDNSlocal void SetLocalDomains(void) 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) + + CFDictionaryRef dict = SCDynamicStoreCopyValue(NULL, NetworkChangedKey_PowerSettings); + if (!dict) { - char buf[256]; - if (!CFStringGetCString(str, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; - LogMsg("GetCurrentPMSetting: CFDictionaryGetValue (%s) failed!", buf); - goto end; + LogSPS("GetCurrentPMSetting: Could not get IOPM CurrentSettings dict"); } - - 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); + CFNumberRef number = CFDictionaryGetValue(dict, name); + if (!number || CFGetTypeID(number) != CFNumberGetTypeID() || !CFNumberGetValue(number, kCFNumberSInt32Type, val)) + *val = 0; + CFRelease(dict); } - -#endif + } #if APPLE_OSX_mDNSResponder @@ -7178,6 +7798,7 @@ mDNSlocal void SetSPS(mDNS *const m) mDNSEthAddr bssid = zeroEthAddr; for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) { + if (intf->InterfaceID == AWDLInterfaceID) continue; bssid = GetBSSID(intf->ifname); if (!mDNSSameEthAddress(&bssid, &zeroEthAddr)) { @@ -7222,14 +7843,28 @@ typedef struct 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; + 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 (!portarray) + count++; + else + { + int i; + for (i = 0; i < count; i++) + if (mDNSSameIPPort(portarray[i], rr->resrec.rdata->u.srv.port)) + break; + + // Add it into the port list only if it not already present in the list + if (i >= count) + portarray[count++] = rr->resrec.rdata->u.srv.port; + } } + } // If Back to My Mac is on, also wake for packets to the IPSEC UDP port (4500) if (trans == mDNSTransport_UDP && m->AutoTunnelNAT.clientContext) @@ -7301,6 +7936,10 @@ mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes, Net // 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)); + + // Offload only Valid Keepalive records + if (isKeepAliveRecord && !mDNSValidKeepAliveRecord(rr)) + continue; #else (void) TCPKAOnly; // unused (void) supportsTCPKA; // unused @@ -7338,6 +7977,10 @@ mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *c // 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; + + // Offload only Valid Keepalive records + if (isKeepAliveRecord && !mDNSValidKeepAliveRecord(rr)) + continue; #else (void) TCPKAOnly; // unused (void) supportsTCPKA; // unused @@ -7360,45 +8003,22 @@ mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *c *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"); + LogMsg("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 +mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf, mDNSBool *keepaliveOnly) // Called with the lock held { mStatus result = mStatus_UnknownErr; mDNSBool TCPKAOnly = mDNSfalse; mDNSBool supportsTCPKA = mDNSfalse; - mDNSBool onbattery = mDNSfalse; + mDNSBool onbattery = mDNSfalse; io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname)); #if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED @@ -7448,10 +8068,10 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const 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)); + DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize); + cmd.rrRecords.ptr = cmd.numRRRecords ? mallocL("mDNSOffloadCmd rrRecords", cmd.numRRRecords * sizeof(FatPtr)) : NULL; + cmd.udpPorts.ptr = cmd.numUDPPorts ? mallocL("mDNSOffloadCmd udpPorts" , cmd.numUDPPorts * sizeof(mDNSIPPort)) : NULL; + cmd.tcpPorts.ptr = cmd.numTCPPorts ? mallocL("mDNSOffloadCmd tcpPorts" , cmd.numTCPPorts * sizeof(mDNSIPPort)) : NULL; LogSPS("ActivateLocalProxy: msg %p %d RR %p %d, UDP %p %d, TCP %p %d", msg, cmd.rrBufferSize, @@ -7459,23 +8079,15 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const 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 (msg && cmd.rrRecords.ptr) GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr, TCPKAOnly, supportsTCPKA); + if (cmd.udpPorts.ptr) cmd.numUDPPorts = GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr); + if (cmd.tcpPorts.ptr) cmd.numTCPPorts = 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); @@ -7489,6 +8101,7 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const IOObjectRelease(parent); } IOObjectRelease(service); + *keepaliveOnly = TCPKAOnly; return result; } @@ -7499,10 +8112,15 @@ mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void) mDNSs32 val = 0; mDNSu8 ret = (mDNSu8)mDNS_NoWake; +#if TARGET_OS_IOS + LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); + return ret; +#endif + if (DisableSleepProxyClient) { LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); - return mDNSfalse; + return ret; } GetCurrentPMSetting(CFSTR("Wake On LAN"), &val); @@ -7523,10 +8141,15 @@ mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void) mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void) { mDNSs32 val = 0; - GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val); + // PrioritizeNetworkReachabilityOverSleep has been deprecated. + // GetCurrentPMSetting(CFSTR("PrioritizeNetworkReachabilityOverSleep"), &val); + // Statically set the PrioritizeNetworkReachabilityOverSleep value to 1 for AppleTV + if (IsAppleTV()) + val = 1; 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 @@ -7584,6 +8207,7 @@ mDNSexport void RemoveAutoTunnel6Record(mDNS *const m) UpdateAutoTunnel6Record(m, info); } +#if !TARGET_OS_EMBEDDED mDNSlocal mDNSBool IPv6AddressIsOnInterface(mDNSv6Addr ipv6Addr, char *ifname) { struct ifaddrs *ifa; @@ -7638,18 +8262,10 @@ mDNSlocal mDNSv6Addr IPv6AddressFromString(char* buf) 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); + connd = SCDynamicStoreCopyValue(NULL, NetworkChangedKey_BTMMConnectivity); if (!connd) { LogInfo("CopyConnectivityBackToMyMacDict: SCDynamicStoreCopyValue failed: %s", SCErrorString(SCError())); @@ -7675,7 +8291,6 @@ mDNSlocal CFDictionaryRef CopyConnectivityBackToMyMacDict() end: if (connd) CFRelease(connd); - if (store) CFRelease(store); return BTMMDict; } @@ -7827,6 +8442,7 @@ mDNSlocal void ProcessConndConfigChanges(mDNS *const m) // If awacsd crashes or exits for some reason, restart it UpdateBTMMRelayConnection(m); } +#endif // !TARGET_OS_EMBEDDED #endif /* APPLE_OSX_mDNSResponder */ mDNSlocal mDNSBool IsAppleNetwork(mDNS *const m) @@ -7844,12 +8460,80 @@ mDNSlocal mDNSBool IsAppleNetwork(mDNS *const m) return mDNSfalse; } +// Called with KQueueLock & mDNS lock +// SetNetworkChanged is allowed to shorten (but not extend) the pause while we wait for configuration changes to settle +mDNSlocal void SetNetworkChanged(mDNS *const m, mDNSs32 delay) +{ + mDNS_CheckLock(m); + if (!m->NetworkChanged || m->NetworkChanged - NonZeroTime(m->timenow + delay) > 0) + { + m->NetworkChanged = NonZeroTime(m->timenow + delay); + LogInfo("SetNetworkChanged: Scheduling in %d ticks", delay); + } + else + LogInfo("SetNetworkChanged: *NOT* increasing delay from %d to %d", m->NetworkChanged - m->timenow, 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); + } +} + 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 + LogInfo("*** Network Configuration Change *** %d ticks late%s", + m->NetworkChanged ? mDNS_TimeNow(m) - m->NetworkChanged : 0, + m->NetworkChanged ? "" : " (no scheduled configuration change)"); + m->NetworkChanged = 0; // If we received a network change event and deferred processing, we're now dealing with it + + // If we have *any* TENTATIVE IPv6 addresses, wait until they've finished configuring + int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); + if (InfoSocket > 0) + { + mDNSBool tentative = mDNSfalse; + struct ifaddrs *ifa = myGetIfAddrs(1); + while (ifa) + { + if (ifa->ifa_addr->sa_family == AF_INET6) + { + struct in6_ifreq ifr6; + mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6)); + strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name)); + ifr6.ifr_addr = *(struct sockaddr_in6 *)ifa->ifa_addr; + // We need to check for IN6_IFF_TENTATIVE here, not IN6_IFF_NOTREADY, because + // IN6_IFF_NOTREADY includes both IN6_IFF_TENTATIVE and IN6_IFF_DUPLICATED addresses. + // We can expect that an IN6_IFF_TENTATIVE address will shortly become ready, + // but an IN6_IFF_DUPLICATED address may not. + if (ioctl(InfoSocket, SIOCGIFAFLAG_IN6, &ifr6) != -1) + { + if (ifr6.ifr_ifru.ifru_flags6 & IN6_IFF_TENTATIVE) + { + LogInfo("*** Network Configuration Change *** IPv6 address %.16a TENTATIVE, will retry", &ifr6.ifr_addr.sin6_addr); + tentative = mDNStrue; + // no need to check other interfaces if we already found out that one interface is TENTATIVE + break; + } + } + } + ifa = ifa->ifa_next; + } + close(InfoSocket); + if (tentative) + { + mDNS_Lock(m); + SetNetworkChanged(m, mDNSPlatformOneSecond / 2); + mDNS_Unlock(m); + return; + } + LogInfo("*** Network Configuration Change *** No IPv6 address TENTATIVE, will continue"); + } + mDNSs32 utc = mDNSPlatformUTC(); m->SystemWakeOnLANEnabled = SystemWakeForNetworkAccess(); m->SystemSleepOnlyIfWakeOnLAN = SystemSleepOnlyIfWakeOnLAN(); @@ -7859,7 +8543,7 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) SetupActiveInterfaces(m, utc); #if APPLE_OSX_mDNSResponder - +#if !TARGET_OS_EMBEDDED mDNS_Lock(m); ProcessConndConfigChanges(m); mDNS_Unlock(m); @@ -7908,20 +8592,26 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) } } } +#endif //!TARGET_OS_EMBEDDED 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 (!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); + if (i->BPF_fd >= 0 && CountProxyTargets(m, i, mDNSNULL, mDNSNULL) == 0) + CloseBPF(i); } - else // else, we're Sleep Proxy Server; open BPF fds + 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(); } + if (i->Exists && (i->Registered == i) && SPSInterface(i) && i->BPF_fd == -1) + { + LogMsg("%s mDNSMacOSXNetworkChanged: requesting BPF", i->ifinfo.ifname); + i->BPF_fd = -2; + mDNSRequestBPF(); + } } } @@ -7933,36 +8623,12 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) if (IsAppleNetwork(m) != mDNS_McastTracingEnabled) { mDNS_McastTracingEnabled = mDNS_McastTracingEnabled ? mDNSfalse : mDNStrue; - LogMsg("mDNSMacOSXNetworkChanged: Multicast Tracing %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + LogInfo("mDNSMacOSXNetworkChanged: Multicast Tracing %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); UpdateDebugState(); } } -// Called with KQueueLock & mDNS lock -// SetNetworkChanged is allowed to extend (but not reduce) the pause while we wait for configuration changes to settle -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); - } - else - LogInfo("SetNetworkChanged: *NOT* reducing delay from %d to %d", m->p->NetworkChanged - m->timenow, 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//IPv4 // or @@ -7983,7 +8649,6 @@ mDNSlocal CFStringRef CopyNameFromKey(CFStringRef key) // an IP service that is explicitly configured for IPv4 Link Local mDNSlocal int ChangedKeysHaveIPv4LL(CFArrayRef inkeys) { - SCDynamicStoreRef store = NULL; CFDictionaryRef dict = NULL; CFMutableArrayRef a; const void **keys = NULL, **vals = NULL; @@ -7994,9 +8659,6 @@ mDNSlocal int ChangedKeysHaveIPv4LL(CFArrayRef inkeys) 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; @@ -8012,7 +8674,7 @@ mDNSlocal int ChangedKeysHaveIPv4LL(CFArrayRef inkeys) CFArrayAppendValue(a, pattern); CFRelease(pattern); - dict = SCDynamicStoreCopyMultiple(store, NULL, a); + dict = SCDynamicStoreCopyMultiple(NULL, NULL, a); CFRelease(a); if (!dict) @@ -8094,7 +8756,6 @@ done: if (vals != NULL) mDNSPlatformMemFree(vals); if (keys != NULL) mDNSPlatformMemFree(keys); if (dict != NULL) CFRelease(dict); - if (store != NULL) CFRelease(store); return found; } @@ -8106,20 +8767,22 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v KQueueLock(m); mDNS_Lock(m); - mDNSs32 delay = mDNSPlatformOneSecond * 2; // Start off assuming a two-second delay + //mDNSs32 delay = mDNSPlatformOneSecond * 2; // Start off assuming a two-second delay + const mDNSs32 delay = (mDNSPlatformOneSecond + 39) / 40; // 25 ms 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_DNS ) != 0); - int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); - int c5 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac ) != 0); - int c6 = ChangedKeysHaveIPv4LL(changedKeys); - int c7 = 0; - + int c_host = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames ) != 0); + int c_comp = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0); + int c_udns = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0); + int c_ddns = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); + int c_btmm = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac ) != 0); + int c_v4ll = ChangedKeysHaveIPv4LL(changedKeys); + int c_fast = 0; + // Do immediate network changed processing for "p2p*" interfaces and - // for interfaces with the IFEF_DIRECTLINK flag set. + // for interfaces with the IFEF_DIRECTLINK flag set or association with a CarPlay + // hosted SSID. { CFArrayRef labels; CFIndex n; @@ -8149,10 +8812,10 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v // 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))) + && (strstr(buf, "p2p") || (getExtendedFlags(buf) & IFEF_DIRECTLINK) || IsCarPlaySSID(buf))) { LogInfo("NetworkChanged: interface %s qualifies for reduced change handling delay", buf); - c7++; + c_fast++; CFRelease(labels); break; } @@ -8161,22 +8824,8 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v } } - if (c && c - c1 - c2 - c3 - c4 - c5 - c6 - c7 == 0) - delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay - - // Immediately force a reconfig (esp. cache flush) if any of the following is true: - // 1. DNS Settings changed. - // 2 An interface changed that is explicitly IPv4 link local - // 3. There are P2P/IFEF_DIRECTLINK/IsCarPlaySSID changes - if (c3 || ChangedKeysHaveIPv4LL(changedKeys) || c7) - { - LogInfo("NetworkChanged: %s : Handling this change immediately", - c3 ? "DNS Settings Changed" : - c7 ? "P2P/IFEF_DIRECTLINK/IsCarPlaySSID Changed" : - "An interface changed that is explicitly IPv4 link local"); - m->p->NetworkChanged = NonZeroTime(m->timenow); - delay = 0; // for the logs below. - } + //if (c && c - c_host - c_comp - c_udns - c_ddns - c_btmm - c_v4ll - c_fast == 0) + // delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay if (mDNS_LoggingEnabled) { @@ -8185,19 +8834,19 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v { char buf[256]; if (!CFStringGetCString(CFArrayGetValueAtIndex(changedKeys, i), buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; - LogInfo("*** NetworkChanged SC key: %s", buf); + LogInfo("*** Network Configuration Change *** SC key: %s", buf); } - LogInfo("*** NetworkChanged *** %d change%s %s%s%s%s%s%s%sdelay %d%s", + LogInfo("*** Network Configuration Change *** %d change%s %s%s%s%s%s%s%sdelay %d%s", c, c>1 ? "s" : "", - c1 ? "(Local Hostname) " : "", - c2 ? "(Computer Name) " : "", - c3 ? "(DNS) " : "", - c4 ? "(DynamicDNS) " : "", - c5 ? "(BTMM) " : "", - c6 ? "(kSCValNetIPv4ConfigMethodLinkLocal) " : "", - c7 ? "(P2P/IFEF_DIRECTLINK/IsCarPlaySSID) " : "", + c_host ? "(Local Hostname) " : "", + c_comp ? "(Computer Name) " : "", + c_udns ? "(DNS) " : "", + c_ddns ? "(DynamicDNS) " : "", + c_btmm ? "(BTMM) " : "", + c_v4ll ? "(kSCValNetIPv4ConfigMethodLinkLocal) " : "", + c_fast ? "(P2P/IFEF_DIRECTLINK/IsCarPlaySSID) " : "", delay, - (c4 || c5) ? " + SetKeyChainTimer" : ""); + (c_ddns || c_btmm) ? " + SetKeyChainTimer" : ""); } SetNetworkChanged(m, delay); @@ -8207,11 +8856,12 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v // 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 (c4 || c5) + if (c_ddns || c_btmm) SetKeyChainTimer(m, delay); - mDNS_Unlock(m); + // Don't try to call mDNSMacOSXNetworkChanged() here -- we're running on the wrong thread + mDNS_Unlock(m); KQueueUnlock(m, "NetworkChanged"); } @@ -8236,6 +8886,8 @@ mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info) mDNS *const m = (mDNS *const)info; (void)store; + KQueueLock(m); // serialize with KQueueLoop() + LogInfo("DynamicStoreReconnected: Reconnected"); // State:/Network/MulticastDNS @@ -8254,15 +8906,14 @@ mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info) 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 + KQueueUnlock(m, "DynamicStoreReconnected"); } mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) @@ -8285,7 +8936,7 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) CFArrayAppendValue(keys, NetworkChangedKey_DNS); CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS); CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); - CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of + CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity); CFArrayAppendValue(patterns, pattern1); CFArrayAppendValue(patterns, pattern2); @@ -8299,7 +8950,7 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) #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); + CFRunLoopAddSource(CFRunLoopGetMain(), m->p->StoreRLS, kCFRunLoopDefaultMode); #endif SCDynamicStoreSetDisconnectCallBack(store, DynamicStoreReconnected); m->p->Store = store; @@ -8318,33 +8969,6 @@ exit: return(err); } -#if 0 // -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) @@ -8373,7 +8997,7 @@ mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname, const Reso continue; } - LogInfo("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr)); + LogMsg("mDNSSetPacketFilterRules: found %s", ARDisplayString(m, rr)); portArray[count] = rr->resrec.rdata->u.srv.port.NotAnInteger; @@ -8381,13 +9005,21 @@ mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname, const Reso p = rr->resrec.name->c; // Skip to App Protocol - if (p[0]) p += 1 + p[0]; + if (p[0]) + p += 1 + p[0]; // Skip to Transport Protocol - if (p[0]) p += 1 + p[0]; + 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; + 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"); @@ -8450,12 +9082,9 @@ mDNSlocal void newMasterElected(mDNS *const m, struct net_event_data * ptr) 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"); + LogInfo("newMasterElected: interface %s not yet active", ifname); return; } InterfaceID = infoOSX->ifinfo.InterfaceID; @@ -8506,23 +9135,24 @@ mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op) 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) +mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr *ap, bool purgeNow) { mDNSu32 slot; CacheGroup *cg; CacheRecord *cr; - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(m, ifindex); + NetworkInterfaceInfoOSX *infoOSX; + mDNSInterfaceID InterfaceID; - if (!InterfaceID) + // Using mDNSPlatformInterfaceIDfromInterfaceIndex() would lead to recursive + // locking issues, see: + infoOSX = IfindexToInterfaceInfoOSX(m, (mDNSInterfaceID)(uintptr_t)ifindex); + if (!infoOSX) { - LogInfo("removeCachedPeerRecords: Invalid ifindex: %d", ifindex); + LogInfo("removeCachedPeerRecords: interface %d not yet active", ifindex); return; } + InterfaceID = infoOSX->ifinfo.InterfaceID; FORALL_CACHERECORDS(slot, cg, cr) { @@ -8530,7 +9160,11 @@ mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr { LogInfo("removeCachedPeerRecords: %s %##s marking for deletion", DNSTypeName(cr->resrec.rrtype), cr->resrec.name->c); - mDNS_Reconfirm_internal(m, cr, kAWDLReconfirmTime); + + if (purgeNow) + mDNS_PurgeCacheResourceRecord(m, cr); + else + mDNS_Reconfirm_internal(m, cr, 0); // use default minimum reconfirm time } } } @@ -8555,8 +9189,8 @@ mDNSlocal void nodePresence(mDNS *const m, struct kev_dl_node_presence * p) 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); + LogInfo("nodePresence: ssth is all zeroes, reconfirm cached records for this peer"); + removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr, false); } } @@ -8574,8 +9208,8 @@ mDNSlocal void nodeAbsence(mDNS *const m, struct kev_dl_node_absence * p) 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); + LogInfo("nodeAbsence: immediately purge cached records from this peer"); + removeCachedPeerRecords(m, p->sdl_node_address.sdl_index, & peerAddr, true); } mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context) @@ -8771,20 +9405,21 @@ mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCa 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) + 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 @@ -8793,6 +9428,12 @@ mDNSlocal void PowerOn(mDNS *const m) m->p->RequestReSleep = mDNS_TimeNow(m) + 20 * mDNSPlatformOneSecond; } } + + // Hold on to a sleep assertion to allow mDNSResponder to perform its maintenance activities. + // This allows for the network link to come up, DHCP to get an address, mDNS to issue queries etc. + // We will clear this assertion as soon as we think the mainenance activities are done. + mDNSPlatformPreventSleep(m, DARK_WAKE_TIME, "mDNSResponder:maintenance"); + } mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messageType, void *messageArgument) @@ -8813,7 +9454,7 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag 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 kIOMessageCanSystemSleep: LogSPS("PowerChanged kIOMessageCanSystemSleep"); break; // E0000270 case kIOMessageSystemWillSleep: LogSPS("PowerChanged kIOMessageSystemWillSleep"); // E0000280 mDNSCoreMachineSleep(m, true); break; @@ -8848,8 +9489,10 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag default: LogSPS("PowerChanged unknown message %X", messageType); break; } - if (messageType == kIOMessageSystemWillSleep) m->p->SleepCookie = (long)messageArgument; - else IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); + if (messageType == kIOMessageSystemWillSleep) + m->p->SleepCookie = (long)messageArgument; + else if (messageType == kIOMessageCanSystemSleep) + IOAllowPowerChange(m->p->PowerConnection, (long)messageArgument); KQueueUnlock(m, "PowerChanged Sleep/Wake"); } @@ -9173,93 +9816,77 @@ mDNSlocal void mDNSMacOSXParseEtcHostsLine(mDNS *const m, char *buffer, ssize_t // 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; } + mDNSMacOSXCreateEtcHostsEntry(m, &first, gairesults->ai_addr, mDNSNULL, ifname, auth); + + // /etc/hosts alias discussion: + // // If the /etc/hosts has an entry like this // - // 1.2.3.4 sun star bright + // ip_address cname [aliases...] + // 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 + // star CNAME sun + // bright CNAME sun // - // 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. + // We store the first name we parsed in "first" and add the address (A/AAAA) record. + // Then we parse additional names adding CNAME records till we reach the end. + aliasIndex = 0; - while (i <= length) + 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. + // Continue to parse additional aliases until we reach end of the line and + // for each "alias" parsed, add a CNAME record where "alias" points to the first "name". + // See also /etc/hosts alias discussion above + i = EtcHostsParseOneName(i + 1, length, buffer, &name2); + if (name2) { + if ((aliasIndex) && (*buffer == *name2)) + break; // break out of the loop if we wrap around + 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 (!SameDomainName(&first, &name2d)) { - if (!mDNSMacOSXCreateEtcHostsEntry(m, &name1d, mDNSNULL, &name2d, ifname, auth)) + if (!mDNSMacOSXCreateEtcHostsEntry(m, &name2d, mDNSNULL, &first, ifname, auth)) { freeaddrinfo(gairesults); return; } } else - LogMsg("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names name1 %##s, name2 %##s", name1d.c, name2d.c); + { + LogInfo("mDNSMacOSXParseEtcHostsLine: Ignoring entry with same names first %##s, name2 %##s", first.c, name2d.c); + } + 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; } - - // 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); } @@ -9526,7 +10153,7 @@ mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, m { AuthGroup *ag; mDNSu32 slot; - AuthRecord *rr, *primary, *rrnext; + AuthRecord *rr, *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) @@ -9539,7 +10166,7 @@ mDNSlocal mDNSBool EtcHostsDeleteOldEntries(mDNS *const m, AuthHash *newhosts, m ag1 = AuthGroupForRecord(newhosts, slot, &rr->resrec); if (ag1) { - primary = rr1 = ag1->members; + rr1 = ag1->members; while (rr1) { if (IdenticalResourceRecord(&rr1->resrec, &rr->resrec)) @@ -9657,9 +10284,9 @@ mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m) if (!EtcHostsDeleteOldEntries(m, &newhosts, mDNStrue)) { LogInfo("mDNSMacOSXUpdateEtcHosts: No work"); + FreeNewHosts(&newhosts); mDNS_Unlock(m); KQueueUnlock(m, "/etc/hosts changed"); - FreeNewHosts(&newhosts); return; } } @@ -9682,10 +10309,9 @@ mDNSlocal void mDNSMacOSXUpdateEtcHosts(mDNS *const m) // 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); + FreeNewHosts(&newhosts); mDNS_Unlock(m); - KQueueUnlock(m, "/etc/hosts changed"); - FreeNewHosts(&newhosts); } #if COMPILER_LIKES_PRAGMA_MARK @@ -9832,7 +10458,6 @@ mDNSlocal void SetupLocalHostRecords(mDNS *const m) mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) { mStatus err; - m->p->CFRunLoop = CFRunLoopGetCurrent(); char HINFO_SWstring[256] = ""; mDNSMacOSXSystemBuildNumber(HINFO_SWstring); @@ -9841,13 +10466,6 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) 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) { @@ -9912,14 +10530,16 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const 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.kqsv4.KQtask = "IPv4 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"; + m->p->permanentsockets.kqsv6.KQtask = "IPv6 UDP packet reception"; err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL); + if (err) LogMsg("mDNSPlatformInit_setup: SetupSocket(AF_INET) failed error %d errno %d (%s)", err, errno, strerror(errno)); err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL); + if (err) LogMsg("mDNSPlatformInit_setup: SetupSocket(AF_INET6) failed error %d errno %d (%s)", err, errno, strerror(errno)); struct sockaddr_in s4; socklen_t n4 = sizeof(s4); @@ -9972,16 +10592,12 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) err = WatchForNetworkChanges(m); if (err) { LogMsg("mDNSPlatformInit_setup: WatchForNetworkChanges failed %d", err); return(err); } -#if 0 // - 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(); + myGetIfAddrs(1); UpdateInterfaceList(m, utc); SetupActiveInterfaces(m, utc); @@ -10016,7 +10632,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) IOPMConnectionSetDispatchQueue(c, dispatch_get_main_queue()); LogInfo("IOPMConnectionSetDispatchQueue is now running"); #else - iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + iopmerr = IOPMConnectionScheduleWithRunLoop(c, CFRunLoopGetMain(), kCFRunLoopDefaultMode); if (iopmerr) LogMsg("IOPMConnectionScheduleWithRunLoop failed %d", iopmerr); LogInfo("IOPMConnectionScheduleWithRunLoop is now running"); #endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */ @@ -10033,7 +10649,7 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) #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); + CFRunLoopAddSource(CFRunLoopGetMain(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); #endif /* MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM */ } } @@ -10069,7 +10685,6 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) mDNSMacOSXUpdateEtcHosts(m); SetupLocalHostRecords(m); - CUPInit(m); return(mStatus_NoError); } @@ -10099,7 +10714,7 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) // We only initialize if mDNSCore successfully initialized. if (D2DInitialize) { - D2DStatus ds = D2DInitialize(m->p->CFRunLoop, xD2DServiceCallback, m) ; + D2DStatus ds = D2DInitialize(CFRunLoopGetMain(), xD2DServiceCallback, m) ; if (ds != kD2DSuccess) LogMsg("D2DInitialiize failed: %d", ds); else @@ -10119,7 +10734,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM IONotificationPortSetDispatchQueue(m->p->PowerPortRef, NULL); #else - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); + CFRunLoopRemoveSource(CFRunLoopGetMain(), IONotificationPortGetRunLoopSource(m->p->PowerPortRef), kCFRunLoopDefaultMode); #endif // According to , a single call // to IORegisterForSystemPower creates *three* objects that need to be disposed individually: @@ -10135,7 +10750,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) if (!SCDynamicStoreSetDispatchQueue(m->p->Store, NULL)) LogMsg("mDNSPlatformClose: SCDynamicStoreSetDispatchQueue failed"); #else - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->StoreRLS, kCFRunLoopDefaultMode); + CFRunLoopRemoveSource(CFRunLoopGetMain(), m->p->StoreRLS, kCFRunLoopDefaultMode); CFRunLoopSourceInvalidate(m->p->StoreRLS); CFRelease(m->p->StoreRLS); m->p->StoreRLS = NULL; @@ -10146,7 +10761,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) if (m->p->PMRLS) { - CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m->p->PMRLS, kCFRunLoopDefaultMode); + CFRunLoopRemoveSource(CFRunLoopGetMain(), m->p->PMRLS, kCFRunLoopDefaultMode); CFRunLoopSourceInvalidate(m->p->PMRLS); CFRelease(m->p->PMRLS); m->p->PMRLS = NULL; @@ -10185,8 +10800,7 @@ mDNSexport void mDNSPlatformClose(mDNS *const m) if (AnonymousRacoonConfig) { AnonymousRacoonConfig = mDNSNULL; - LogInfo("mDNSPlatformClose: Deconfiguring autotunnel"); - (void)mDNSConfigureServer(kmDNSDown, mDNSNULL, mDNSNULL); + LogInfo("mDNSPlatformClose: Deconfiguring autotunnel need not be done in mDNSResponder"); } #endif // APPLE_OSX_mDNSResponder } @@ -10263,8 +10877,9 @@ mDNSexport mDNSs32 mDNSPlatformUTC(void) // 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 mDNSPlatformStrCopy( void *dst, const void *src) { strcpy((char *)dst, (const char *)src); } +mDNSexport mDNSu32 mDNSPlatformStrLCopy( void *dst, const void *src, mDNSu32 dstlen) { return (strlcpy((char *)dst, (const char *)src, dstlen)); } +mDNSexport mDNSu32 mDNSPlatformStrLen ( const void *src) { return(strlen((const 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)); } @@ -10303,6 +10918,42 @@ mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, co } } +mDNSexport void mDNSPlatformPreventSleep(mDNS *const m, mDNSu32 timeout, const char *reason) +{ + if (m->p->IOPMAssertion) + { + LogSPS("Sleep Assertion is already being held. Will not attempt to get it again for %d seconds for %s", timeout, reason); + return; + } +#ifdef kIOPMAssertionTypeNoIdleSleep + +#if TARGET_OS_EMBEDDED + if (!IsAppleTV()) + return; // No need for maintenance wakes on non-AppleTV embedded devices. +#endif + + double timeoutVal = (double)timeout; + CFStringRef str = CFStringCreateWithCString(NULL, reason, kCFStringEncodingUTF8); + CFNumberRef Timeout_num = CFNumberCreate(NULL, kCFNumberDoubleType, &timeoutVal); + CFMutableDictionaryRef assertionProperties = CFDictionaryCreateMutable(NULL, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (IsAppleTV()) + CFDictionarySetValue(assertionProperties, kIOPMAssertionTypeKey, kIOPMAssertPreventUserIdleSystemSleep); + else + CFDictionarySetValue(assertionProperties, kIOPMAssertionTypeKey, kIOPMAssertMaintenanceActivity); + + CFDictionarySetValue(assertionProperties, kIOPMAssertionTimeoutKey, Timeout_num); + CFDictionarySetValue(assertionProperties, kIOPMAssertionNameKey, str); + + IOPMAssertionCreateWithProperties(assertionProperties, (IOPMAssertionID *)&m->p->IOPMAssertion); + CFRelease(str); + CFRelease(Timeout_num); + CFRelease(assertionProperties); + LogSPS("Got an idle sleep assertion for %d seconds for %s", timeout, reason); +#endif +} + mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration) { mDNSu32 ifindex; @@ -10324,12 +10975,21 @@ mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) if (InterfaceID == mDNSInterface_P2P) return mDNStrue; + // mDNSInterface_BLE not considered a D2D interface for the purpose of this + // routine, since it's not implemented via a D2D plugin. + if (InterfaceID == mDNSInterface_BLE) + return mDNSfalse; + if ( (InterfaceID == mDNSInterface_Any) || (InterfaceID == mDNSInterfaceMark) || (InterfaceID == mDNSInterface_LocalOnly) || (InterfaceID == mDNSInterface_Unicast)) return mDNSfalse; + // Compare to cached AWDL interface ID. + if (AWDLInterfaceID && (InterfaceID == AWDLInterfaceID)) + return mDNStrue; + info = IfindexToInterfaceInfoOSX(&mDNSStorage, InterfaceID); if (info == NULL) { @@ -10343,18 +11003,18 @@ mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) // 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) +mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(const AuthRecord *rr, mDNSInterfaceID InterfaceID) { // For an explicit match to a valid interface ID, return true. - if (rr->resrec.InterfaceID == intf->InterfaceID) + if (rr->resrec.InterfaceID == InterfaceID) return mDNStrue; // Only filtering records for D2D type interfaces, return true for all other interface types. - if (!mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + if (!mDNSPlatformInterfaceIsD2D(InterfaceID)) return mDNStrue; // If it's an AWDL interface the record must be explicitly marked to include AWDL. - if (intf->InterfaceID == AWDLInterfaceID) + if (InterfaceID == AWDLInterfaceID) { if (rr->ARType == AuthRecordAnyIncludeAWDL || rr->ARType == AuthRecordAnyIncludeAWDLandP2P) return mDNStrue; @@ -10407,11 +11067,7 @@ mDNSexport mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *c 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; } @@ -10458,25 +11114,24 @@ mDNSexport void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDis // definitions for device-info record construction #define DEVINFO_MODEL "model=" -#define DEVINFO_MODEL_LEN strlen(DEVINFO_MODEL) +#define DEVINFO_MODEL_LEN sizeof_string(DEVINFO_MODEL) #define OSX_VER "osxvers=" -#define OSX_VER_LEN strlen(OSX_VER) +#define OSX_VER_LEN sizeof_string(OSX_VER) #define VER_NUM_LEN 2 // 2 digits of version number added to base string #define MODEL_COLOR "ecolor=" -#define MODEL_COLOR_LEN strlen(MODEL_COLOR) -#define MODEL_RGB_VALUE_LEN strlen("255,255,255") // 'r,g,b' +#define MODEL_COLOR_LEN sizeof_string(MODEL_COLOR) +#define MODEL_RGB_VALUE_LEN sizeof_string("255,255,255") // 'r,g,b' -// Bytes available in TXT record for model name after subtracting space for other +// 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) - (MODEL_COLOR_LEN + MODEL_RGB_VALUE_LEN + 1)) -mDNSlocal mDNSBool getModelIconColors(char *color) +mDNSlocal mDNSu8 getModelIconColors(char *color) { - mDNSBool hasColor = mDNSfalse; mDNSPlatformMemZero(color, MODEL_RGB_VALUE_LEN + 1); - + #if !TARGET_OS_EMBEDDED && defined(kIOPlatformDeviceEnclosureColorKey) mDNSu8 red = 0; mDNSu8 green = 0; @@ -10487,12 +11142,11 @@ mDNSlocal mDNSBool getModelIconColors(char *color) if (kIOReturnSuccess == rGetDeviceColor) { // IOKit was able to get enclosure color for the current device. - hasColor = true; - snprintf(color, MODEL_RGB_VALUE_LEN + 1, "%d,%d,%d", red, green, blue); + return snprintf(color, MODEL_RGB_VALUE_LEN + 1, "%d,%d,%d", red, green, blue); } #endif // !TARGET_OS_EMBEDDED && defined(kIOPlatformDeviceEnclosureColorKey) - - return hasColor; + + return 0; } @@ -10518,20 +11172,21 @@ mDNSexport mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *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() + // WARNING: This code assumes that OSXVers is always exactly two digits snprintf(ver_num, VER_NUM_LEN + 1, "%d", OSXVers); mDNSPlatformMemCopy(ptr, ver_num, VER_NUM_LEN); ptr += VER_NUM_LEN; - + char rgb[MODEL_RGB_VALUE_LEN + 1]; // RGB value + null written by snprintf - if (getModelIconColors(rgb)) + len = getModelIconColors(rgb); + if (len) { - len = strlen(rgb); *ptr = MODEL_COLOR_LEN + len; // length byte ptr++; - + mDNSPlatformMemCopy(ptr, MODEL_COLOR, MODEL_COLOR_LEN); ptr += MODEL_COLOR_LEN; - + mDNSPlatformMemCopy(ptr, rgb, len); ptr += len; } diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index eb1e128..20f7a32 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -31,6 +31,7 @@ extern "C" { #include "mDNSEmbeddedAPI.h" // for domain name structure #include +#include //#define MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM @@ -41,9 +42,7 @@ extern "C" { #if TARGET_OS_EMBEDDED #define NO_SECURITYFRAMEWORK 1 #define NO_CFUSERNOTIFICATION 1 -#include // for IsAppleTV() -#include // for _scprefs_observer_watch() -extern mDNSBool GetmDNSManagedPref(CFStringRef key); +#include // for IsAppleTV() #endif #ifndef NO_SECURITYFRAMEWORK @@ -51,12 +50,6 @@ extern mDNSBool GetmDNSManagedPref(CFStringRef key); #include #endif /* NO_SECURITYFRAMEWORK */ -#if TARGET_OS_IPHONE -#include "cellular_usage_policy.h" -#endif - -#define kmDNSResponderServName "com.apple.mDNSResponder" - enum mDNSDynamicStoreSetConfigKey { kmDNSMulticastConfig = 1, @@ -141,8 +134,6 @@ struct NetworkInterfaceInfoOSX_struct 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 @@ -169,7 +160,7 @@ struct mDNS_PlatformSupport_struct { NetworkInterfaceInfoOSX *InterfaceList; KQSocketSet permanentsockets; - int num_mcasts; // Number of multicasts received during this CPU scheduling period (used for CPU limiting) + int num_mcasts; // Number of multicasts received during this CPU scheduling period (used for CPU limiting) 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 @@ -181,10 +172,8 @@ struct mDNS_PlatformSupport_struct 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; @@ -216,9 +205,6 @@ struct mDNS_PlatformSupport_struct TCPSocket TCPProxy; ProxyCallback *UDPProxyCallback; ProxyCallback *TCPProxyCallback; -#if TARGET_OS_IPHONE - cellular_usage_policy_client_t handle; -#endif }; extern int OfferSleepProxyService; diff --git a/mDNSMacOSX/mDNSResponder-entitlements.plist b/mDNSMacOSX/mDNSResponder-entitlements.plist index 21a343f..6d0bd67 100644 --- a/mDNSMacOSX/mDNSResponder-entitlements.plist +++ b/mDNSMacOSX/mDNSResponder-entitlements.plist @@ -8,25 +8,39 @@ com.apple.private.network.socket-delegate - com.apple.networkd.cellular_blocked.notify - com.apple.private.SCNetworkConnection-proxy-user + com.apple.mDNSResponder_Helper + com.apple.private.network.reserved-port com.apple.SystemConfiguration.SCDynamicStore-write-access com.apple.private.snhelper - com.apple.telephony.cupolicy-monitor-access - com.apple.private.necp.match + com.apple.private.necp.policies + com.apple.security.network.server com.apple.security.network.client com.apple.private.network.awdl.restricted + com.apple.BTServer.allowRestrictedServices + + com.apple.BTServer.appleMfgDataAdvertising + + com.apple.BTServer.appleMfgDataScanner + + com.apple.developer.networking.networkextension + + app-proxy-provider-system + + com.apple.private.neagent + + com.apple.private.nehelper.privileged + diff --git a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj deleted file mode 100644 index 3e7df11..0000000 --- a/mDNSMacOSX/mDNSResponder.pbproj/project.pbxproj +++ /dev/null @@ -1,2338 +0,0 @@ -// !$*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 ."; - 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 = " - - - - CFBundleDevelopmentRegion - English - CFBundleExecutable - Bonjour - CFBundleGetInfoString - - CFBundleIconFile - BonjourPref - CFBundleIdentifier - com.apple.preference.bonjour - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - - CFBundlePackageType - BNDL - CFBundleShortVersionString - - CFBundleSignature - ???? - CFBundleVersion - 1.0 - NSMainNibFile - DNSServiceDiscoveryPref - NSPrefPaneIconFile - BonjourPref.tiff - NSPrefPaneIconLabel - Bonjour - NSPrincipalClass - DNSServiceDiscoveryPref - - -"; - }; - 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/mDNSMacOSX/mDNSResponder.sb b/mDNSMacOSX/mDNSResponder.sb index f267f32..807217a 100644 --- a/mDNSMacOSX/mDNSResponder.sb +++ b/mDNSMacOSX/mDNSResponder.sb @@ -1,6 +1,6 @@ ; -*- Mode: Scheme; tab-width: 4 -*- ; -; Copyright (c) 2012 Apple Inc. All rights reserved. +; Copyright (c) 2012-2015 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: @@ -10,7 +10,7 @@ ; 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 +; 3. Neither the name of Apple 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. ; @@ -45,12 +45,17 @@ ; Mach communications ; These are needed for things like getpwnam, hostname changes, & keychain (allow mach-lookup + (global-name "com.apple.awdd") (global-name "com.apple.bsd.dirhelper") (global-name "com.apple.CoreServices.coreservicesd") + (global-name "com.apple.coreservices.quarantine-resolver") (global-name "com.apple.distributed_notifications.2") + (global-name "com.apple.distributed_notifications@1v3") + (global-name "com.apple.lsd.mapdb") (global-name "com.apple.ocspd") (global-name "com.apple.PowerManagement.control") (global-name "com.apple.mDNSResponderHelper") + (global-name "com.apple.mDNSResponder_Helper") (global-name "com.apple.SecurityServer") (global-name "com.apple.SystemConfiguration.configd") (global-name "com.apple.SystemConfiguration.SCNetworkReachability") @@ -58,16 +63,18 @@ (global-name "com.apple.SystemConfiguration.NetworkInformation") (global-name "com.apple.system.notification_center") (global-name "com.apple.system.logger") + (global-name "com.apple.usymptomsd") (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")) + (global-name "com.apple.snhelper") + (global-name "com.apple.nehelper") + (global-name "com.apple.networkserviceproxy")) (allow mach-register (global-name "com.apple.d2d.ipc")) @@ -99,6 +106,7 @@ (literal "/usr/sbin") (literal "/usr/sbin/mDNSResponder") + (literal "/Library/Preferences/com.apple.mDNSResponder.plist") (literal "/Library/Preferences/SystemConfiguration/preferences.plist") (literal "/Library/Preferences/SystemConfiguration/com.apple.nat.plist") (regex #"^/Library/Preferences/(ByHost/)?\.GlobalPreferences\.") @@ -106,7 +114,11 @@ (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")) + (literal "/private/var/preferences/SystemConfiguration/preferences.plist") + (subpath "/System/Library/Preferences/Logging") + (subpath "/AppleInternal/Library/Preferences/Logging") + (subpath "/Library/Preferences/Logging")) + ; For MAC Address (allow system-info (info-type "net.link.addr")) @@ -118,12 +130,6 @@ (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 @@ -136,10 +142,7 @@ (regex #"^/private/var/folders/[^/]+/[^/]+/C/mds(/|$)") ; Required on 10.5 and 10.6 - (regex #"^/private/var/folders/[^/]+/[^/]+/-Caches-/mds(/|$)") - - ; Required on 10.10.4 - (regex #"^/private/var/folders/[^/]+/[^/]+/[0-9]+(/|$)")) + (regex #"^/private/var/folders/[^/]+/[^/]+/-Caches-/mds(/|$)")) ; CRL Cache for SSL/TLS connections (allow file-read-data (literal "/private/var/db/crls/crlcache.db")) diff --git a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj index 39d8a45..3e6c3fd 100644 --- a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 45; + objectVersion = 46; objects = { /* Begin PBXAggregateTarget section */ @@ -25,16 +25,14 @@ 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 */, + 03067D6C0C83A3920022BE1F /* PBXTargetDependency */, BD7833F01ABA5E3500EC51ED /* PBXTargetDependency */, + 84C5B3411665544B00C324A8 /* PBXTargetDependency */, + 217A4C49138EE14C000A5BA8 /* PBXTargetDependency */, ); name = "Build Some"; productName = "Build Some"; @@ -57,9 +55,9 @@ buildPhases = ( ); dependencies = ( + 215FFB19124002C100470DE1 /* PBXTargetDependency */, 215FFB1D124002CC00470DE1 /* PBXTargetDependency */, 215FFB1B124002C700470DE1 /* PBXTargetDependency */, - 215FFB19124002C100470DE1 /* PBXTargetDependency */, ); name = SystemLibrariesStatic; productName = SystemLibrariesStatic; @@ -93,78 +91,55 @@ /* 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 */; }; + 216D9ACE1720C9F5008066E1 /* uDNSPathEvalulation.c in Sources */ = {isa = PBXBuildFile; fileRef = 216D9ACD1720C9F5008066E1 /* uDNSPathEvalulation.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 */; }; + 219D5542149ED645004464AE /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.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 */; }; + 21F51DC11B3541940070B05C /* com.apple.mDNSResponder.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 21F51DBF1B35412D0070B05C /* com.apple.mDNSResponder.plist */; }; + 21F51DC31B3541F50070B05C /* com.apple.mDNSResponderHelper.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 21F51DBE1B3541030070B05C /* com.apple.mDNSResponderHelper.plist */; }; + 21F51DC51B3542210070B05C /* com.apple.dnsextd.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 21F51DBD1B3540DB0070B05C /* com.apple.dnsextd.plist */; }; + 222A3C5B1C1B75F2003A6FFD /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 222A3C581C1B743B003A6FFD /* DNSServiceDiscoveryDefines.h */; }; + 222A3C6A1C1B7777003A6FFD /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */; }; + 222A3C6B1C1B7778003A6FFD /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */; }; + 222A3C6C1C1B7779003A6FFD /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */; }; + 222A3C6D1C1B777A003A6FFD /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */; }; + 222A3C6E1C1B777B003A6FFD /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */; }; + 222A3C6F1C1B777C003A6FFD /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */; }; + 2243AE391C90AECF0079023E /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2243AE381C90AECF0079023E /* CoreBluetooth.framework */; }; + 22448EA31C90A7BE004F25CC /* BLE.c in Sources */ = {isa = PBXBuildFile; fileRef = 22448EA11C90A7B5004F25CC /* BLE.c */; }; + 22448EA41C90A7CB004F25CC /* BLE.h in Headers */ = {isa = PBXBuildFile; fileRef = 22448EA21C90A7B5004F25CC /* BLE.h */; }; + 22448EA71C90A837004F25CC /* coreBLE.h in Headers */ = {isa = PBXBuildFile; fileRef = 22448EA51C90A82D004F25CC /* coreBLE.h */; }; + 227687F31C90AD580019382D /* coreBLE.m in Sources */ = {isa = PBXBuildFile; fileRef = 22448EA61C90A82D004F25CC /* coreBLE.m */; }; 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 */; }; @@ -174,38 +149,40 @@ 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 */; }; - 3F347CF6185D57CD00367B40 /* base.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 3F347CF5185D57CD00367B40 /* base.xcconfig */; }; + 371D0FBC1BF545FA00E5DB26 /* InterfaceTest.c in Sources */ = {isa = PBXBuildFile; fileRef = 371D0FBA1BF545FA00E5DB26 /* InterfaceTest.c */; }; + 371D0FBF1BF666EB00E5DB26 /* ResourceRecordTest.c in Sources */ = {isa = PBXBuildFile; fileRef = 371D0FBD1BF666EB00E5DB26 /* ResourceRecordTest.c */; }; + 373202101BAB4444007DE806 /* DNSMessageTest.c in Sources */ = {isa = PBXBuildFile; fileRef = 3732020F1BAB4349007DE806 /* DNSMessageTest.c */; }; + 3771F67D1BA387DD0072355E /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 37DDE9351BA386E70092AC61 /* main.c */; }; + 37DDE9331BA383D30092AC61 /* unittest.c in Sources */ = {isa = PBXBuildFile; fileRef = 37DDE9271BA3825C0092AC61 /* unittest.c */; }; + 37FEBD581BC789AA00638EA4 /* DNSCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = 7F18A9F60587CEF6001880B3 /* DNSCommon.c */; }; 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 */; }; + 729DF4601CD40630005ECF70 /* com.apple.mDNSResponder.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = 729DF45F1CD40630005ECF70 /* com.apple.mDNSResponder.plist */; }; 72FB5467166D5FCA0090B2D9 /* dnsctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FB545A166D5F960090B2D9 /* dnsctl.c */; }; 8415A6571897109000BDBA26 /* libdns_services.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8415A6561897109000BDBA26 /* libdns_services.dylib */; }; - 8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */; }; + 8417375C1B967D37000CD5C2 /* dnsctl_server.c in Sources */ = {isa = PBXBuildFile; fileRef = 8417375A1B967CBE000CD5C2 /* dnsctl_server.c */; }; 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 */; }; 84F4C090188F050200D1E1DE /* dns_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F4C08F188F04CF00D1E1DE /* dns_services.h */; settings = {ATTRIBUTES = (Private, ); }; }; - D284BE530ADD80740027CCDF /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */; }; + BD03E88D1AD31278005E8A81 /* SymptomReporter.c in Sources */ = {isa = PBXBuildFile; fileRef = BD03E88C1AD31278005E8A81 /* SymptomReporter.c */; }; + BDA3F08A1C48DB920054FB4B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDA3F0891C48DB910054FB4B /* Foundation.framework */; }; + BDA3F08D1C48DBEA0054FB4B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDA3F0891C48DB910054FB4B /* Foundation.framework */; }; + BDA3F08E1C48DCA50054FB4B /* Metrics.h in Headers */ = {isa = PBXBuildFile; fileRef = BDA3F0871C48DB6D0054FB4B /* Metrics.h */; }; + BDA3F08F1C48DCA50054FB4B /* Metrics.m in Sources */ = {isa = PBXBuildFile; fileRef = BDA3F0881C48DB6D0054FB4B /* Metrics.m */; }; + BDA9A7881B3A924C00523835 /* dns_sd_private.h in Headers */ = {isa = PBXBuildFile; fileRef = BDA9A7871B3A923600523835 /* dns_sd_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + BDA9A7891B3A92A500523835 /* dns_sd_private.h in Headers */ = {isa = PBXBuildFile; fileRef = BDA9A7871B3A923600523835 /* dns_sd_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; 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 */; }; @@ -222,28 +199,6 @@ 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 */; }; @@ -263,7 +218,6 @@ 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 */; }; @@ -288,23 +242,16 @@ 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 */; }; + FF3C72AA1CE3E62200CDF81E /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FF3C72A91CE3E62200CDF81E /* libicucore.dylib */; }; 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, ); }; }; + FFD52A9E1AF858DD00CAD3EC /* CryptoAlg.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; 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 */; }; @@ -345,13 +292,6 @@ 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 */; @@ -373,6 +313,13 @@ remoteGlobalIDString = 03067D640C83A3700022BE1F; remoteInfo = "Build Some"; }; + 0C2AAB311B6929F300113637 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 84C5B3341665529800C324A8; + remoteInfo = dns_services; + }; 2130257012400E9300AC839F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -502,6 +449,36 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 21F51DC01B35418C0070B05C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /System/Library/LaunchDaemons; + dstSubfolderSpec = 0; + files = ( + 21F51DC11B3541940070B05C /* com.apple.mDNSResponder.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 21F51DC21B3541F30070B05C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /System/Library/LaunchDaemons; + dstSubfolderSpec = 0; + files = ( + 21F51DC31B3541F50070B05C /* com.apple.mDNSResponderHelper.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + 21F51DC41B35421A0070B05C /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /System/Library/LaunchDaemons; + dstSubfolderSpec = 0; + files = ( + 21F51DC51B3542210070B05C /* com.apple.dnsextd.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -544,10 +521,10 @@ 8418673D15AB8BFF00BB7F70 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; - dstPath = /private/etc/asl/; + dstPath = /System/Library/Preferences/Logging/Subsystems; dstSubfolderSpec = 0; files = ( - 8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */, + 729DF4601CD40630005ECF70 /* com.apple.mDNSResponder.plist in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 1; }; @@ -599,7 +576,6 @@ 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; 21070E5D16486B9000A69507 /* DNSSECSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSSECSupport.c; sourceTree = ""; }; 21070E5E16486B9000A69507 /* DNSSECSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSSECSupport.h; sourceTree = ""; }; - 2120ABD416B71614007089B6 /* CUPolicy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CUPolicy.c; sourceTree = ""; }; 2124FA2B1471E98C0021D7BB /* nsec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec.h; path = ../mDNSCore/nsec.h; sourceTree = ""; }; 2124FA2F1471E9B50021D7BB /* dnssec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssec.h; path = ../mDNSCore/dnssec.h; sourceTree = ""; }; 2124FA321471E9DE0021D7BB /* nsec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec.c; path = ../mDNSCore/nsec.c; sourceTree = ""; }; @@ -612,10 +588,10 @@ 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 = ""; }; + 216D9ACD1720C9F5008066E1 /* uDNSPathEvalulation.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = uDNSPathEvalulation.c; sourceTree = ""; }; 218E8E4F156D8C0300720DA0 /* dnsproxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnsproxy.c; path = ../mDNSCore/dnsproxy.c; sourceTree = ""; }; 218E8E50156D8C0300720DA0 /* dnsproxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnsproxy.h; path = ../mDNSCore/dnsproxy.h; sourceTree = ""; }; - 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; }; + 219D5541149ED645004464AE /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = /usr/lib/libxml2.dylib; sourceTree = ""; }; 21A57F4A145B2AE100939099 /* CryptoAlg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = CryptoAlg.c; path = ../mDNSCore/CryptoAlg.c; sourceTree = ""; }; 21A57F4B145B2AE100939099 /* CryptoAlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptoAlg.h; path = ../mDNSCore/CryptoAlg.h; sourceTree = ""; }; 21A57F51145B2B1400939099 /* CryptoSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CryptoSupport.c; sourceTree = ""; }; @@ -624,18 +600,38 @@ 21DD8FBE161E9A250033C8F8 /* anonymous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = anonymous.h; path = ../mDNSCore/anonymous.h; sourceTree = ""; }; 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSProxySupport.c; sourceTree = ""; }; 21F432971134AA6800581B69 /* WebFilterDNS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebFilterDNS.framework; path = /System/Library/PrivateFrameworks/WebFilterDNS.framework; sourceTree = ""; }; - 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = helpermsg.defs; sourceTree = ""; }; + 21F51DBD1B3540DB0070B05C /* com.apple.dnsextd.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.dnsextd.plist; sourceTree = ""; }; + 21F51DBE1B3541030070B05C /* com.apple.mDNSResponderHelper.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.mDNSResponderHelper.plist; sourceTree = ""; }; + 21F51DBF1B35412D0070B05C /* com.apple.mDNSResponder.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.mDNSResponder.plist; sourceTree = ""; }; + 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSServiceDiscovery.c; sourceTree = ""; }; + 222A3C571C1B743B003A6FFD /* DNSServiceDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscovery.h; sourceTree = ""; }; + 222A3C581C1B743B003A6FFD /* DNSServiceDiscoveryDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscoveryDefines.h; sourceTree = ""; }; + 2243AE381C90AECF0079023E /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; }; + 22448EA11C90A7B5004F25CC /* BLE.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BLE.c; sourceTree = ""; }; + 22448EA21C90A7B5004F25CC /* BLE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BLE.h; sourceTree = ""; }; + 22448EA51C90A82D004F25CC /* coreBLE.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = coreBLE.h; sourceTree = ""; }; + 22448EA61C90A82D004F25CC /* coreBLE.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = coreBLE.m; sourceTree = ""; }; 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 = ""; }; 2E0406140C3197CB00F13B59 /* libbsm.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbsm.dylib; path = /usr/lib/libbsm.dylib; sourceTree = ""; }; 2E0406CA0C31E9AD00F13B59 /* helper-main.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = "helper-main.c"; sourceTree = ""; }; - 2E35528F0C3A95C100CA1CB7 /* helper-error.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "helper-error.h"; sourceTree = ""; }; 2E8165F60C59835F00485EB2 /* libipsec.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libipsec.dylib; path = /usr/lib/libipsec.dylib; sourceTree = ""; }; 2E96A5250C39BE480087C4D2 /* helper.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = helper.h; sourceTree = ""; }; 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "helper-stubs.c"; sourceTree = ""; }; 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "helpermsg-types.h"; sourceTree = ""; }; 2EDC5E720C39EA640092701B /* helper-server.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = "helper-server.h"; sourceTree = ""; }; - 3F347CF5185D57CD00367B40 /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = ""; }; + 371D0FBA1BF545FA00E5DB26 /* InterfaceTest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = InterfaceTest.c; path = ../unittests/InterfaceTest.c; sourceTree = ""; }; + 371D0FBB1BF545FA00E5DB26 /* InterfaceTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InterfaceTest.h; path = ../unittests/InterfaceTest.h; sourceTree = ""; }; + 371D0FBD1BF666EB00E5DB26 /* ResourceRecordTest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ResourceRecordTest.c; path = ../unittests/ResourceRecordTest.c; sourceTree = ""; }; + 371D0FBE1BF666EB00E5DB26 /* ResourceRecordTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ResourceRecordTest.h; path = ../unittests/ResourceRecordTest.h; sourceTree = ""; }; + 3732020F1BAB4349007DE806 /* DNSMessageTest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSMessageTest.c; path = ../unittests/DNSMessageTest.c; sourceTree = ""; }; + 373202111BAB63E8007DE806 /* DNSMessageTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DNSMessageTest.h; path = ../unittests/DNSMessageTest.h; sourceTree = ""; }; + 37AF802A1BF699AF00D657F6 /* DomainNameTest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DomainNameTest.c; path = ../unittests/DomainNameTest.c; sourceTree = ""; }; + 37AF802B1BF699AF00D657F6 /* DomainNameTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DomainNameTest.h; path = ../unittests/DomainNameTest.h; sourceTree = ""; }; + 37DDE9271BA3825C0092AC61 /* unittest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = unittest.c; path = ../unittests/unittest.c; sourceTree = ""; }; + 37DDE9281BA382670092AC61 /* unittest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = unittest.h; path = ../unittests/unittest.h; sourceTree = ""; }; + 37DDE92D1BA383610092AC61 /* unittests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unittests; sourceTree = BUILT_PRODUCTS_DIR; }; + 37DDE9351BA386E70092AC61 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../unittests/main.c; sourceTree = ""; }; 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 = ""; }; 4A7B9E7C14FDA19F00B84CC1 /* mDNSResponder.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = mDNSResponder.txt; sourceTree = ""; }; @@ -650,13 +646,10 @@ 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSEmbeddedAPI.h; path = ../mDNSCore/mDNSEmbeddedAPI.h; sourceTree = ""; }; 654BE65002B63B93000001D1 /* mDNSDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mDNSDebug.h; path = ../mDNSCore/mDNSDebug.h; sourceTree = ""; }; 65713D46025A293200000109 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; - 6575FBE9022EAF5A00000109 /* mDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; name = mDNS.c; path = ../mDNSCore/mDNS.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = mDNSMacOSX.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - 6575FBEC022EAF7200000109 /* daemon.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = daemon.c; sourceTree = ""; tabWidth = 4; usesTabs = 1; }; - 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscoveryDefines.h; sourceTree = ""; }; - 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryReply.defs; sourceTree = ""; }; - 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryRequest.defs; sourceTree = ""; }; - 6575FC20022EB7AA00000109 /* SamplemDNSClient.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = SamplemDNSClient.c; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 0; }; + 6575FBE9022EAF5A00000109 /* mDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; name = mDNS.c; path = ../mDNSCore/mDNS.c; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; + 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = mDNSMacOSX.c; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; + 6575FBEC022EAF7200000109 /* daemon.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = daemon.c; sourceTree = ""; tabWidth = 4; usesTabs = 0; }; + 729DF45F1CD40630005ECF70 /* com.apple.mDNSResponder.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = com.apple.mDNSResponder.plist; path = Private/com.apple.mDNSResponder.plist; sourceTree = ""; }; 72FB545A166D5F960090B2D9 /* dnsctl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dnsctl.c; path = ../Clients/dnsctl.c; sourceTree = ""; }; 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; }; @@ -664,23 +657,25 @@ 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 = ""; }; 7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LegacyNATTraversal.c; sourceTree = SOURCE_ROOT; }; - 8415A6561897109000BDBA26 /* libdns_services.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libdns_services.dylib; path = ../../../../../../../usr/lib/libdns_services.dylib; sourceTree = ""; }; - 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */ = {isa = PBXFileReference; lastKnownFileType = text; path = com.apple.networking.mDNSResponder; sourceTree = ""; }; - 8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = mDNSResponderLogging.mobileconfig; sourceTree = ""; }; + 8415A6561897109000BDBA26 /* libdns_services.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libdns_services.dylib; path = /usr/lib/libdns_services.dylib; sourceTree = ""; }; + 8417375A1B967CBE000CD5C2 /* dnsctl_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnsctl_server.c; path = Private/dnsctl_server.c; sourceTree = ""; }; 848DA5C6165477E000D2E8B4 /* xpc_services.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xpc_services.c; path = Private/xpc_services.c; sourceTree = ""; }; 848DA5C9165477EB00D2E8B4 /* xpc_services.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xpc_services.h; path = Private/xpc_services.h; sourceTree = ""; }; 848DA5D516547F7200D2E8B4 /* dns_xpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_xpc.h; path = Private/dns_xpc.h; sourceTree = ""; }; 84C5B3351665529800C324A8 /* libdns_services.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdns_services.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; 84C5B339166553AF00C324A8 /* dns_services.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dns_services.c; path = Private/dns_services.c; sourceTree = ""; }; 84F4C08F188F04CF00D1E1DE /* dns_services.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_services.h; path = Private/dns_services.h; sourceTree = ""; }; + BD03E88C1AD31278005E8A81 /* SymptomReporter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SymptomReporter.c; sourceTree = ""; }; + BDA3F0871C48DB6D0054FB4B /* Metrics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Metrics.h; sourceTree = ""; }; + BDA3F0881C48DB6D0054FB4B /* Metrics.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Metrics.m; sourceTree = ""; }; + BDA3F0891C48DB910054FB4B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + BDA9A7871B3A923600523835 /* dns_sd_private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dns_sd_private.h; path = Private/dns_sd_private.h; sourceTree = ""; }; 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 = ""; }; 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; }; @@ -697,14 +692,14 @@ DB2CC4680662DFF500335AB3 /* JavaVM.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaVM.framework; path = /System/Library/Frameworks/JavaVM.framework; sourceTree = ""; }; 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; }; + F525E72804AA167501F1CF4D /* uds_daemon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uds_daemon.c; path = ../mDNSShared/uds_daemon.c; sourceTree = SOURCE_ROOT; usesTabs = 0; }; 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; }; + FF1C919F07021E3F001048AB /* dns-sd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "dns-sd.c"; path = "../Clients/dns-sd.c"; sourceTree = SOURCE_ROOT; usesTabs = 0; }; 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 = ""; }; FF260A1F07B4436900CE10E5 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = ""; }; @@ -722,14 +717,13 @@ 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; }; + FF3C72A91CE3E62200CDF81E /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.0.Internal.sdk/usr/lib/libicucore.dylib; sourceTree = DEVELOPER_DIR; }; 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 = ""; }; - FFA572600AF1908D0055A0F1 /* DNSServiceDiscovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSServiceDiscovery.h; sourceTree = ""; }; - FFA572630AF190C20055A0F1 /* dns_sd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_sd.h; path = ../mDNSShared/dns_sd.h; sourceTree = SOURCE_ROOT; }; + FFA572630AF190C20055A0F1 /* dns_sd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_sd.h; path = ../mDNSShared/dns_sd.h; sourceTree = SOURCE_ROOT; usesTabs = 0; }; 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; }; @@ -742,7 +736,6 @@ FFFB0DAD07B43CBA00B88D48 /* PrivilegedOperations.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = PrivilegedOperations.c; sourceTree = ""; }; FFFB0DAE07B43CBA00B88D48 /* ConfigurationAuthority.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ConfigurationAuthority.c; sourceTree = ""; }; FFFB0DAF07B43CBA00B88D48 /* ddnswriteconfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ddnswriteconfig.m; sourceTree = ""; }; - FFFB0DB407B43D2700B88D48 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; FFFF8F800C3307AC00722979 /* dnsextd.conf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dnsextd.conf; path = ../mDNSShared/dnsextd.conf; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ @@ -788,6 +781,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 37DDE92A1BA383610092AC61 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 72FB545C166D5FB00090B2D9 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -807,23 +807,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 2243AE391C90AECF0079023E /* CoreBluetooth.framework in Frameworks */, D284BE650ADD80740027CCDF /* CoreFoundation.framework in Frameworks */, - D284BE660ADD80740027CCDF /* SystemConfiguration.framework in Frameworks */, + FF3C72AA1CE3E62200CDF81E /* libicucore.dylib in Frameworks */, + BDA3F08A1C48DB920054FB4B /* Foundation.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 */, + D284BE660ADD80740027CCDF /* SystemConfiguration.framework in Frameworks */, + 219D5542149ED645004464AE /* libxml2.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -858,7 +849,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - D284BEE00ADD80A70027CCDF /* Foundation.framework in Frameworks */, + BDA3F08D1C48DBEA0054FB4B /* Foundation.framework in Frameworks */, D284BEE10ADD80A70027CCDF /* Security.framework in Frameworks */, D284BEE20ADD80A70027CCDF /* SystemConfiguration.framework in Frameworks */, D284BEE30ADD80A70027CCDF /* CoreFoundation.framework in Frameworks */, @@ -904,16 +895,16 @@ 08FB7794FE84155DC02AAC07 /* mDNSResponder */ = { isa = PBXGroup; children = ( - 8415A6561897109000BDBA26 /* libdns_services.dylib */, - 3F347CF5185D57CD00367B40 /* base.xcconfig */, + 2243AE381C90AECF0079023E /* CoreBluetooth.framework */, + 729DF45F1CD40630005ECF70 /* com.apple.mDNSResponder.plist */, 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 */, + 37DDE9241BA382280092AC61 /* Unit Tests */, ); name = mDNSResponder; sourceTree = ""; @@ -921,79 +912,86 @@ 08FB7795FE84155DC02AAC07 /* mDNS Server Sources */ = { isa = PBXGroup; children = ( - 84F4C08F188F04CF00D1E1DE /* dns_services.h */, - 216D9ACD1720C9F5008066E1 /* VPNService.c */, - 2120ABD416B71614007089B6 /* CUPolicy.c */, - 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 */, + 22448EA51C90A82D004F25CC /* coreBLE.h */, + 22448EA61C90A82D004F25CC /* coreBLE.m */, + 22448EA11C90A7B5004F25CC /* BLE.c */, + 22448EA21C90A7B5004F25CC /* BLE.h */, + 222A3C571C1B743B003A6FFD /* DNSServiceDiscovery.h */, + 222A3C581C1B743B003A6FFD /* DNSServiceDiscoveryDefines.h */, + 222A3C561C1B7415003A6FFD /* DNSServiceDiscovery.c */, 21DD8FBD161E9A250033C8F8 /* anonymous.c */, 21DD8FBE161E9A250033C8F8 /* anonymous.h */, - 2127A47515C3C7B900A857FC /* nsec3.c */, - 2127A47615C3C7B900A857FC /* nsec3.h */, - 8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */, - 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */, - 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */, + 21F51DBD1B3540DB0070B05C /* com.apple.dnsextd.plist */, + 21F51DBF1B35412D0070B05C /* com.apple.mDNSResponder.plist */, + 21F51DBE1B3541030070B05C /* com.apple.mDNSResponderHelper.plist */, + 21A57F4A145B2AE100939099 /* CryptoAlg.c */, + 21A57F4B145B2AE100939099 /* CryptoAlg.h */, + 21A57F51145B2B1400939099 /* CryptoSupport.c */, + 21A57F52145B2B1400939099 /* CryptoSupport.h */, + 6575FBEC022EAF7200000109 /* daemon.c */, + FFA572630AF190C20055A0F1 /* dns_sd.h */, + BDA9A7871B3A923600523835 /* dns_sd_private.h */, + 84C5B339166553AF00C324A8 /* dns_services.c */, + 84F4C08F188F04CF00D1E1DE /* dns_services.h */, + 848DA5D516547F7200D2E8B4 /* dns_xpc.h */, + 7F18A9F60587CEF6001880B3 /* DNSCommon.c */, + 7F461DB5062DBF2900672BF3 /* DNSDigest.c */, + FF13FFEA0A5DA44A00897C81 /* dnsextd_lexer.l */, + FF13FFEC0A5DA45500897C81 /* dnsextd_parser.y */, + FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */, + FF25794606C9A8BF00376F7B /* dnsextd.c */, + FFFF8F800C3307AC00722979 /* dnsextd.conf */, 218E8E4F156D8C0300720DA0 /* dnsproxy.c */, 218E8E50156D8C0300720DA0 /* dnsproxy.h */, + 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */, + FFFA38620AEEDB090065B80A /* dnssd_clientlib.c */, + FFFA38640AEEDB130065B80A /* dnssd_clientstub.c */, + F5E11B5A04A28126019798ED /* dnssd_ipc.c */, + F5E11B5B04A28126019798ED /* dnssd_ipc.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 */, + 21070E5D16486B9000A69507 /* DNSSECSupport.c */, + 21070E5E16486B9000A69507 /* DNSSECSupport.h */, + DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */, 4ADB5F230F6AB9F400B95BF3 /* helper-entitlements.plist */, - 4A2E69DD0F5475A3004A87B0 /* uds_daemon.h */, - 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */, - 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */, - 2E35528F0C3A95C100CA1CB7 /* helper-error.h */, - 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */, + 2E0406CA0C31E9AD00F13B59 /* helper-main.c */, 2EDC5E720C39EA640092701B /* helper-server.h */, - 2E96A5250C39BE480087C4D2 /* helper.h */, + 2E96A52D0C39C1A50087C4D2 /* helper-stubs.c */, 2E0405F40C3195F700F13B59 /* helper.c */, - 2E0406CA0C31E9AD00F13B59 /* helper-main.c */, + 2E96A5250C39BE480087C4D2 /* helper.h */, + 2ECC11A50C4FEC3800CB1885 /* helpermsg-types.h */, 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 */, + 4A8202520C56C36500DDFD48 /* libpfkey.h */, 6575FBE9022EAF5A00000109 /* mDNS.c */, - 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */, - 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */, - 654BE65002B63B93000001D1 /* mDNSDebug.h */, DBAAFE29057E8F4D0085CAD0 /* mDNSDebug.c */, + 654BE65002B63B93000001D1 /* mDNSDebug.h */, + 654BE64F02B63B93000001D1 /* mDNSEmbeddedAPI.h */, + 6575FBEB022EAF7200000109 /* mDNSMacOSX.c */, 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */, - DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */, - FFCB6D73075D539900B8AF62 /* PlatformCommon.c */, - FF1C919D07021D77001048AB /* dns-sd.1 */, FF485D5105632E0000130380 /* mDNSResponder.8 */, - FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */, - FFFF8F800C3307AC00722979 /* dnsextd.conf */, + 4A7B9E7E14FDA1BB00B84CC1 /* mDNSResponder.plist */, 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 */, + 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */, + BDA3F0871C48DB6D0054FB4B /* Metrics.h */, + BDA3F0881C48DB6D0054FB4B /* Metrics.m */, + 2124FA321471E9DE0021D7BB /* nsec.c */, + 2124FA2B1471E98C0021D7BB /* nsec.h */, + 2127A47515C3C7B900A857FC /* nsec3.c */, + 2127A47615C3C7B900A857FC /* nsec3.h */, 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */, 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */, + 4A8202530C56C36600DDFD48 /* pfkey.c */, + FFCB6D73075D539900B8AF62 /* PlatformCommon.c */, + BD03E88C1AD31278005E8A81 /* SymptomReporter.c */, + 7F18A9F70587CEF6001880B3 /* uDNS.c */, + 216D9ACD1720C9F5008066E1 /* uDNSPathEvalulation.c */, + F525E72804AA167501F1CF4D /* uds_daemon.c */, + 4A2E69DD0F5475A3004A87B0 /* uds_daemon.h */, + 8417375A1B967CBE000CD5C2 /* dnsctl_server.c */, + 848DA5C6165477E000D2E8B4 /* xpc_services.c */, + 848DA5C9165477EB00D2E8B4 /* xpc_services.h */, ); name = "mDNS Server Sources"; sourceTree = ""; @@ -1001,18 +999,20 @@ 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 */, + FF2609FA07B4433800CE10E5 /* Cocoa.framework */, 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */, + 4A3600DF0F34F8CD00453EFB /* DeviceToDeviceManager.framework */, + BDA3F0891C48DB910054FB4B /* Foundation.framework */, 00CA213D02786FC30CCA2C71 /* IOKit.framework */, DB2CC4680662DFF500335AB3 /* JavaVM.framework */, - FF2609FA07B4433800CE10E5 /* Cocoa.framework */, + 2E0406140C3197CB00F13B59 /* libbsm.dylib */, + 8415A6561897109000BDBA26 /* libdns_services.dylib */, + 2E8165F60C59835F00485EB2 /* libipsec.dylib */, + 219D5541149ED645004464AE /* libxml2.dylib */, + FF3C72A91CE3E62200CDF81E /* libicucore.dylib */, FF260A1F07B4436900CE10E5 /* PreferencePanes.framework */, + 7F869685066EE02400D2A2DC /* Security.framework */, + 65713D46025A293200000109 /* SystemConfiguration.framework */, 21F432971134AA6800581B69 /* WebFilterDNS.framework */, ); name = "External Frameworks and Libraries"; @@ -1021,24 +1021,23 @@ 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 */, + D284BEE80ADD80A70027CCDF /* ddnswriteconfig */, + D284BEB00ADD80920027CCDF /* dns-sd */, + 72FB545F166D5FB00090B2D9 /* dnsctl */, + D284BED90ADD80A20027CCDF /* dnsextd */, 2141DD1D123FFCDB0086D23E /* libdns_sd.a */, 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */, 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */, 84C5B3351665529800C324A8 /* libdns_services.dylib */, - 72FB545F166D5FB00090B2D9 /* dnsctl */, + D284BEBE0ADD809A0027CCDF /* libjdns_sd.jnilib */, + FFB765840AEED9C700583A2C /* libsystem_dnssd.dylib */, + FFA572390AF18F1C0055A0F1 /* libsystem_dnssd_debug.dylib */, + FFA572450AF18F450055A0F1 /* libsystem_dnssd_profile.dylib */, + D284BE730ADD80740027CCDF /* mDNSResponder */, + 2E0405F00C31955500F13B59 /* mDNSResponderHelper */, + 37DDE92D1BA383610092AC61 /* unittests */, ); name = Products; sourceTree = ""; @@ -1052,22 +1051,30 @@ name = "Bonjour Events Plugin"; sourceTree = ""; }; - 6575FBFE022EAFA800000109 /* MIG files */ = { + 37DDE9241BA382280092AC61 /* Unit Tests */ = { isa = PBXGroup; children = ( - 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */, - 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */, - 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */, - 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */, - ); - name = "MIG files"; + 37AF802A1BF699AF00D657F6 /* DomainNameTest.c */, + 37AF802B1BF699AF00D657F6 /* DomainNameTest.h */, + 371D0FBD1BF666EB00E5DB26 /* ResourceRecordTest.c */, + 371D0FBE1BF666EB00E5DB26 /* ResourceRecordTest.h */, + 373202111BAB63E8007DE806 /* DNSMessageTest.h */, + 3732020F1BAB4349007DE806 /* DNSMessageTest.c */, + 37DDE9351BA386E70092AC61 /* main.c */, + 37DDE9271BA3825C0092AC61 /* unittest.c */, + 37DDE9281BA382670092AC61 /* unittest.h */, + 371D0FBA1BF545FA00E5DB26 /* InterfaceTest.c */, + 371D0FBB1BF545FA00E5DB26 /* InterfaceTest.h */, + ); + name = "Unit Tests"; sourceTree = ""; }; 6575FC1F022EB78C00000109 /* Command-Line Clients */ = { isa = PBXGroup; children = ( - 6575FC20022EB7AA00000109 /* SamplemDNSClient.c */, + FF1C919D07021D77001048AB /* dns-sd.1 */, FF1C919F07021E3F001048AB /* dns-sd.c */, + 72FB545A166D5F960090B2D9 /* dnsctl.c */, FF5852100DD27BD300862BDF /* ClientCommon.c */, ); name = "Command-Line Clients"; @@ -1137,6 +1144,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + BDA9A7891B3A92A500523835 /* dns_sd_private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1160,7 +1168,6 @@ 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 */, @@ -1180,11 +1187,9 @@ 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 */, @@ -1192,39 +1197,17 @@ 2124FA301471E9B50021D7BB /* dnssec.h in Headers */, 218E8E53156D8C0300720DA0 /* dnsproxy.h in Headers */, 2127A47915C3C7B900A857FC /* nsec3.h in Headers */, + 222A3C5B1C1B75F2003A6FFD /* DNSServiceDiscoveryDefines.h in Headers */, 21DD8FC1161E9A250033C8F8 /* anonymous.h in Headers */, 21070E6116486B9000A69507 /* DNSSECSupport.h in Headers */, 848DA5CA165477EB00D2E8B4 /* xpc_services.h in Headers */, + BDA3F08E1C48DCA50054FB4B /* Metrics.h in Headers */, + 22448EA71C90A837004F25CC /* coreBLE.h in Headers */, + 22448EA41C90A7CB004F25CC /* BLE.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; @@ -1243,8 +1226,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 2E35529D0C3A9E7600CA1CB7 /* helper-error.h in Headers */, 2E35529F0C3A9E7600CA1CB7 /* helper.h in Headers */, + FFD52A9E1AF858DD00CAD3EC /* CryptoAlg.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1280,6 +1263,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + BDA9A7881B3A924C00523835 /* dns_sd_private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1382,6 +1366,7 @@ 2E0405ED0C31955500F13B59 /* Sources */, 2E0405EE0C31955500F13B59 /* Frameworks */, 4AAE0C5A0C68E6EC003882A5 /* CopyFiles */, + 21F51DC21B3541F30070B05C /* CopyFiles */, ); buildRules = ( ); @@ -1392,6 +1377,23 @@ productReference = 2E0405F00C31955500F13B59 /* mDNSResponderHelper */; productType = "com.apple.product-type.tool"; }; + 37DDE92C1BA383610092AC61 /* unittests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 37DDE9311BA383610092AC61 /* Build configuration list for PBXNativeTarget "unittests" */; + buildPhases = ( + 37DDE9291BA383610092AC61 /* Sources */, + 37DDE92A1BA383610092AC61 /* Frameworks */, + 37DDE9341BA384000092AC61 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = unittests; + productName = unittests; + productReference = 37DDE92D1BA383610092AC61 /* unittests */; + productType = "com.apple.product-type.tool"; + }; 72FB545E166D5FB00090B2D9 /* dnsctl */ = { isa = PBXNativeTarget; buildConfigurationList = 72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */; @@ -1403,6 +1405,7 @@ buildRules = ( ); dependencies = ( + 0C2AAB321B6929F300113637 /* PBXTargetDependency */, ); name = dnsctl; productName = dnsctl; @@ -1434,12 +1437,12 @@ D284BE520ADD80740027CCDF /* Headers */, D284BE550ADD80740027CCDF /* Sources */, D284BE640ADD80740027CCDF /* Frameworks */, - D284BE690ADD80740027CCDF /* Rez */, D284BE6A0ADD80740027CCDF /* CopyFiles */, 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */, 4A7B9E8114FDA25500B84CC1 /* CopyFiles */, - D284BE6C0ADD80740027CCDF /* ShellScript */, + D284BE6C0ADD80740027CCDF /* Run Script */, 8418673D15AB8BFF00BB7F70 /* CopyFiles */, + 21F51DC01B35418C0070B05C /* CopyFiles */, ); buildRules = ( ); @@ -1451,25 +1454,6 @@ 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" */; @@ -1477,7 +1461,6 @@ D284BEA60ADD80920027CCDF /* Headers */, D284BEA70ADD80920027CCDF /* Sources */, D284BEA90ADD80920027CCDF /* Frameworks */, - D284BEAA0ADD80920027CCDF /* Rez */, D284BEAB0ADD80920027CCDF /* CopyFiles */, ); buildRules = ( @@ -1497,7 +1480,6 @@ D284BEB50ADD809A0027CCDF /* Headers */, D284BEB60ADD809A0027CCDF /* Sources */, D284BEB80ADD809A0027CCDF /* Frameworks */, - D284BEBA0ADD809A0027CCDF /* Rez */, ); buildRules = ( ); @@ -1521,7 +1503,7 @@ D284BECE0ADD80A20027CCDF /* Frameworks */, D284BED40ADD80A20027CCDF /* CopyFiles */, FFFF8F770C32F0FD00722979 /* CopyFiles */, - FF37FAAD0BC581780044A5CF /* ShellScript */, + 21F51DC41B35421A0070B05C /* CopyFiles */, ); buildRules = ( D284BFB80ADD8E510027CCDF /* PBXBuildRule */, @@ -1542,7 +1524,6 @@ D284BEDC0ADD80A70027CCDF /* Headers */, D284BEDD0ADD80A70027CCDF /* Sources */, D284BEDF0ADD80A70027CCDF /* Frameworks */, - D284BEE40ADD80A70027CCDF /* Rez */, ); buildRules = ( ); @@ -1562,7 +1543,6 @@ D284BEEE0ADD80B00027CCDF /* Resources */, D284BEFD0ADD80B00027CCDF /* Sources */, D284BF010ADD80B00027CCDF /* Frameworks */, - D284BF070ADD80B00027CCDF /* Rez */, ); buildRules = ( ); @@ -1633,9 +1613,15 @@ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { + LastUpgradeCheck = 0700; + TargetAttributes = { + 37DDE92C1BA383610092AC61 = { + CreatedOnToolsVersion = 7.0; + }; + }; }; buildConfigurationList = D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */; - compatibilityVersion = "Xcode 3.1"; + compatibilityVersion = "Xcode 3.2"; developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( @@ -1652,26 +1638,26 @@ 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 */, + 72FB545E166D5FB00090B2D9 /* dnsctl */, 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 */, + FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */, 2141DD1C123FFCDB0086D23E /* libdns_sd_static */, 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */, 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */, + FFB765830AEED9C700583A2C /* libdns_sd_dynamic */, + FFA572300AF18F1C0055A0F1 /* libdns_sd_debug_dynamic */, + FFA5723C0AF18F450055A0F1 /* libdns_sd_profile_dynamic */, 84C5B3341665529800C324A8 /* dns_services */, - 72FB545E166D5FB00090B2D9 /* dnsctl */, + D284BEB20ADD809A0027CCDF /* libjdns_sd.jnilib */, + 4AE471670EAFF81900A6C5AD /* dns_sd.jar */, + 37DDE92C1BA383610092AC61 /* unittests */, ); }; /* End PBXProject section */ @@ -1688,7 +1674,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3F347CF6185D57CD00367B40 /* base.xcconfig in Resources */, D284BEEF0ADD80B00027CCDF /* remove_idle.tiff in Resources */, D284BEF00ADD80B00027CCDF /* add_pressed.tiff in Resources */, D284BEF10ADD80B00027CCDF /* remove_disabled.tiff in Resources */, @@ -1708,51 +1693,6 @@ }; /* 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; @@ -1793,71 +1733,56 @@ shellPath = "/bin/bash -e -x"; shellScript = "DSTROOT=${DSTROOT}\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\"\n\nif [[ \"${ACTION}\" == \"installhdrs\" ]]; then\n exit 0\nfi\n\nif [[ \"${PLATFORM_NAME}\" =~ \"simulator\" ]]; then\n ln -s libsystem_dnssd.dylib ${DSTROOT}${INSTALL_PATH}/libsystem_sim_dnssd.dylib\nfi\n"; }; - 4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */ = { + 37DDE9341BA384000092AC61 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( - "$(SRCROOT)/../mDNSShared/dnsextd_parser.y", + "$(TARGET_BUILD_DIR)/$(TARGET_NAME)", ); - name = "Build yacc file into derived source files"; outputPaths = ( - "$(DERIVED_FILE_DIR)/dnsextd_parser.c", - "$(DERIVED_FILE_DIR)/dnsextd_parser.h", + "$(TARGET_BUILD_DIR)/unittest_success", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/usr/bin/bison -o ${SCRIPT_OUTPUT_FILE_0} -d ${SCRIPT_INPUT_FILE_0}"; + shellScript = "cd \"$TARGET_BUILD_DIR\"\n./\"$TARGET_NAME\"\n# touch unittest_success\nif [ ! -f unittest_success ] ; then exit -1; fi"; }; - D284BE510ADD80740027CCDF /* ShellScript */ = { + 4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */ = { 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/local/include/vproc.h\" -o -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 = ( + inputPaths = ( + "$(SRCROOT)/../mDNSShared/dnsextd_parser.y", ); - 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}\" -a -z \"${TVOS_DEPLOYMENT_TARGET}\" ] ; then\n SANDBOXDST=\"${DSTROOT}/usr/share/sandbox\"\nelse\n SANDBOXDST=\"${DSTROOT}/usr/local/share/sandbox/profiles/embedded/builtin\"\nfi\n(umask 022; mkdir -p -m 0755 \"$SANDBOXDST\")\ncp \"${SRCROOT}/mDNSResponder.sb\" \"${SANDBOXDST}/mDNSResponder.sb\"\n"; - }; - D284BE760ADD80800027CCDF /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( + 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 = "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/local/include/vproc.h\" -o -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"; + shellScript = "/usr/bin/bison -o ${SCRIPT_OUTPUT_FILE_0} -d ${SCRIPT_INPUT_FILE_0}"; }; - FF045B6A0C7E4AA600448140 /* ShellScript */ = { + D284BE510ADD80740027CCDF /* ShellScript */ = { isa = PBXShellScriptBuildPhase; - buildActionMask = 8; + buildActionMask = 2147483647; files = ( ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; + runOnlyForDeploymentPostprocessing = 0; 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"; + 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/local/include/vproc.h\" -o -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"; }; - FF37FAAD0BC581780044A5CF /* ShellScript */ = { + D284BE6C0ADD80740027CCDF /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; files = ( ); + name = "Run Script"; 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"; + 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}\" -a -z \"${TVOS_DEPLOYMENT_TARGET}\" -a -z \"${WATCHOS_DEPLOYMENT_TARGET}\" ] ; then\n SANDBOXDST=\"${DSTROOT}/usr/share/sandbox\"\nelse\n SANDBOXDST=\"${DSTROOT}/usr/local/share/sandbox/profiles/embedded/builtin\"\nfi\n(umask 022; mkdir -p -m 0755 \"$SANDBOXDST\")\ncp \"${SRCROOT}/mDNSResponder.sb\" \"${SANDBOXDST}/mDNSResponder.sb\"\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -1876,10 +1801,8 @@ files = ( 215FFAEE124000F900470DE1 /* dnssd_ipc.c in Sources */, 215FFAEF124000F900470DE1 /* dnssd_clientlib.c in Sources */, + 222A3C6A1C1B7777003A6FFD /* DNSServiceDiscovery.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; }; @@ -1889,10 +1812,8 @@ files = ( 215FFAF41240011800470DE1 /* dnssd_ipc.c in Sources */, 215FFAF51240011800470DE1 /* dnssd_clientlib.c in Sources */, + 222A3C6B1C1B7778003A6FFD /* DNSServiceDiscovery.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; }; @@ -1902,10 +1823,8 @@ files = ( 215FFAFA1240013400470DE1 /* dnssd_ipc.c in Sources */, 215FFAFB1240013400470DE1 /* dnssd_clientlib.c in Sources */, + 222A3C6C1C1B7779003A6FFD /* DNSServiceDiscovery.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; }; @@ -1914,13 +1833,25 @@ 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; }; + 37DDE9291BA383610092AC61 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 371D0FBF1BF666EB00E5DB26 /* ResourceRecordTest.c in Sources */, + 37FEBD581BC789AA00638EA4 /* DNSCommon.c in Sources */, + 371D0FBC1BF545FA00E5DB26 /* InterfaceTest.c in Sources */, + 373202101BAB4444007DE806 /* DNSMessageTest.c in Sources */, + 3771F67D1BA387DD0072355E /* main.c in Sources */, + 37DDE9331BA383D30092AC61 /* unittest.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 72FB545B166D5FB00090B2D9 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1941,20 +1872,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D284BE560ADD80740027CCDF /* DNSServiceDiscoveryReply.defs in Sources */, - D284BE570ADD80740027CCDF /* DNSServiceDiscoveryRequest.defs in Sources */, + 227687F31C90AD580019382D /* coreBLE.m 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 */, + 22448EA31C90A7BE004F25CC /* BLE.c in Sources */, D284BE5F0ADD80740027CCDF /* dnssd_ipc.c in Sources */, D284BE600ADD80740027CCDF /* PlatformCommon.c in Sources */, D284BE610ADD80740027CCDF /* mDNSMacOSX.c in Sources */, + BDA3F08F1C48DCA50054FB4B /* Metrics.m 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 */, @@ -1962,46 +1893,13 @@ 213BDC6D147319F400000896 /* dnssec.c in Sources */, 218E8E51156D8C0300720DA0 /* dnsproxy.c in Sources */, 21DED43515702C0F0060B6B9 /* DNSProxySupport.c in Sources */, - 216D9ACE1720C9F5008066E1 /* VPNService.c in Sources */, + 216D9ACE1720C9F5008066E1 /* uDNSPathEvalulation.c in Sources */, + BD03E88D1AD31278005E8A81 /* SymptomReporter.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 */, + 8417375C1B967D37000CD5C2 /* dnsctl_server.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2027,7 +1925,6 @@ 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 */, @@ -2037,7 +1934,6 @@ 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; @@ -2067,10 +1963,8 @@ files = ( FFA572330AF18F1C0055A0F1 /* dnssd_ipc.c in Sources */, FFA572340AF18F1C0055A0F1 /* dnssd_clientlib.c in Sources */, + 222A3C6E1C1B777B003A6FFD /* DNSServiceDiscovery.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; }; @@ -2080,10 +1974,8 @@ files = ( FFA5723F0AF18F450055A0F1 /* dnssd_ipc.c in Sources */, FFA572400AF18F450055A0F1 /* dnssd_clientlib.c in Sources */, + 222A3C6F1C1B777C003A6FFD /* DNSServiceDiscovery.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; }; @@ -2093,10 +1985,8 @@ files = ( FFFA38660AEEDB2B0065B80A /* dnssd_ipc.c in Sources */, FFFA38630AEEDB090065B80A /* dnssd_clientlib.c in Sources */, + 222A3C6D1C1B777A003A6FFD /* DNSServiceDiscovery.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; }; @@ -2108,11 +1998,6 @@ 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 */; @@ -2128,6 +2013,11 @@ target = 03067D640C83A3700022BE1F /* Build Some */; targetProxy = 03067D850C849CC30022BE1F /* PBXContainerItemProxy */; }; + 0C2AAB321B6929F300113637 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 84C5B3341665529800C324A8 /* dns_services */; + targetProxy = 0C2AAB311B6929F300113637 /* PBXContainerItemProxy */; + }; 2130257112400E9300AC839F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = FFA572650AF190F10055A0F1 /* SystemLibrariesDynamic */; @@ -2218,87 +2108,531 @@ 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; +/* 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 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "Build Some"; + }; + name = Release; + }; + 0C419F121BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/Debug"; + CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)/Debug"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + GCC_OPTIMIZATION_LEVEL = 0; + 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", + "DEBUG=1", + "BONJOUR_ON_DEMAND=1", + "USE_LIBIDN=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", + ); + SDKROOT = macosx.internal; + STRIP_INSTALLED_PRODUCT = NO; + STRIP_STYLE = debugging; + SUPPORTED_PLATFORMS = "macosx iphoneos"; + WARNING_CFLAGS = ( + "-W", + "-Wall", + "-Wmissing-prototypes", + "-Wno-four-char-constants", + "-Wno-unknown-pragmas", + "-Wshadow", + "-Wno-format", + "-Wformat-security", + ); + YACC_GENERATED_FILE_STEM = Standard; + }; + name = Debug; + }; + 0C419F131BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "Build All"; + }; + name = Debug; + }; + 0C419F141BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "Build Some"; + }; + name = Debug; + }; + 0C419F151BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "Build All"; + }; + name = Debug; + }; + 0C419F161BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "APPLY_RULES_IN_COPY_FILES[sdk=appletvos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=iphoneos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=watchos*]" = YES; + CODE_SIGN_ENTITLEMENTS = "mDNSResponder-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + 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", + "$(SDKROOT)/usr/local/include/", + ); + INSTALL_PATH = /usr/sbin; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.10; + ORDER_FILE = "${SRCROOT}/mDNSResponder.order"; + OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( + "-weak_framework", + DeviceToDeviceManager, + "-lMobileGestalt", + ); + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( + "-lAWACS", + "-weak_framework", + WebFilterDNS, + "-weak_framework", + DeviceToDeviceManager, + ); + "PLIST_FILE_OUTPUT_FORMAT[sdk=appletvos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=iphoneos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=watchos*]" = binary; + PRODUCT_NAME = mDNSResponder; + PROVISIONING_PROFILE = ""; + }; + name = Debug; + }; + 0C419F171BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + "APPLY_RULES_IN_COPY_FILES[sdk=appletvos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=iphoneos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=watchos*]" = YES; + CODE_SIGN_ENTITLEMENTS = "helper-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + HEADER_SEARCH_PATHS = ( + "${CONFIGURATION_TEMP_DIR}", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + "$(SDKROOT)/usr/local/include", + ); + INSTALL_PATH = /usr/sbin; + LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; + MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_LDFLAGS = "-lipsec"; + "PLIST_FILE_OUTPUT_FORMAT[sdk=appletvos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=iphoneos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=watchos*]" = binary; + PRODUCT_NAME = mDNSResponderHelper; + PROVISIONING_PROFILE = ""; + }; + name = Debug; + }; + 0C419F181BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../mDNSShared, + "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", + ); + INSTALL_PATH = /usr/bin; + PRODUCT_NAME = "dns-sd"; + }; + name = Debug; + }; + 0C419F191BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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 = "dnsctl-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + 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/local/bin; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SDKROOT = macosx; + }; + name = Debug; + }; + 0C419F1A1BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + 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.10; + OTHER_CFLAGS = "-UAPPLE_OSX_mDNSResponder"; + PRODUCT_NAME = dnsextd; + }; + name = Debug; + }; + 0C419F1B1BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = "/Library/Application Support/Bonjour"; + MACOSX_DEPLOYMENT_TARGET = 10.6; + OTHER_LDFLAGS = "-Wl,-pie"; + PRODUCT_NAME = ddnswriteconfig; + }; + name = Debug; + }; + 0C419F1C1BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_ENABLE_OBJC_GC = supported; + INFOPLIST_FILE = "PreferencePane/Info-PreferencePane.plist"; + INSTALL_PATH = /AppleInternal/Library/PreferencePanes; + MACOSX_DEPLOYMENT_TARGET = 10.6; + PRODUCT_NAME = Bonjour; + WRAPPER_EXTENSION = prefPane; + }; + name = Debug; + }; + 0C419F1D1BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + BUNDLE_LOADER = /usr/libexec/UserEventAgent; + CODE_SIGN_IDENTITY = "-"; + INFOPLIST_FILE = "BonjourEvents-Info.plist"; + INSTALL_PATH = /System/Library/UserEventPlugins/; + PRODUCT_NAME = BonjourEvents; + PROVISIONING_PROFILE = ""; + WRAPPER_EXTENSION = plugin; + }; + name = Debug; + }; + 0C419F1E1BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = SystemLibraries; + }; + name = Debug; + }; + 0C419F1F1BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = SystemLibrariesStatic; + }; + name = Debug; + }; + 0C419F201BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = SystemLibrariesDynamic; + }; + name = Debug; + }; + 0C419F211BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + 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; + PRODUCT_NAME = dns_sd; + "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; + }; + name = Debug; + }; + 0C419F221BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALL_PATH = /usr/local/lib/system; + PRODUCT_NAME = dns_sd_debug; + "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; + }; + name = Debug; + }; + 0C419F231BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + 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/local/lib/system; + PRODUCT_NAME = dns_sd_profile; + "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; + }; + name = Debug; + }; + 0C419F241BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_EXTENSION = dylib; + 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; + INTERPOSITION_SIM_SUFFIX = ""; + "INTERPOSITION_SIM_SUFFIX[sdk=iphonesimulator*]" = _sim; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_LDFLAGS = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_kernel", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_platform", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + PRODUCT_NAME = libsystem_dnssd; + }; + name = Debug; + }; + 0C419F251BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_EXTENSION = dylib; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "__DARWIN_NON_CANCELABLE=1", + ); + HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; + INSTALL_PATH = /usr/lib/system; + INTERPOSITION_SIM_SUFFIX = ""; + "INTERPOSITION_SIM_SUFFIX[sdk=iphonesimulator*]" = _sim; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_LDFLAGS = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_kernel", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_platform", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + PRODUCT_NAME = libsystem_dnssd_debug; + }; + name = Debug; + }; + 0C419F261BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_EXTENSION = dylib; + 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; + INTERPOSITION_SIM_SUFFIX = ""; + "INTERPOSITION_SIM_SUFFIX[sdk=iphonesimulator*]" = _sim; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_LDFLAGS = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_kernel", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_platform", + "-lsystem$(INTERPOSITION_SIM_SUFFIX)_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + PRODUCT_NAME = libsystem_dnssd_profile; + }; + name = Debug; + }; + 0C419F271BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + 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 = Debug; }; - FF260A4B07B4477F00CE10E5 /* InfoPlist.strings */ = { - isa = PBXVariantGroup; - children = ( - FF260A4C07B4477F00CE10E5 /* English */, - ); - name = InfoPlist.strings; - path = PreferencePane; - sourceTree = SOURCE_ROOT; + 0C419F281BA20DF600A70FF7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + EXECUTABLE_EXTENSION = jnilib; + 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; + PRODUCT_NAME = libjdns_sd; + }; + name = Debug; }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 03067D740C83A3CB0022BE1F /* Development */ = { + 0C419F291BA20DF600A70FF7 /* Debug */ = { 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"; + PRODUCT_NAME = dns_sd.jar; }; - name = Development; + name = Debug; }; - 213FB21A12028A7B002B3A08 /* Development */ = { + 213FB21A12028A7B002B3A08 /* Release */ = { 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; + name = Release; }; - 2141DCF9123FFB5D0086D23E /* Development */ = { + 2141DCF9123FFB5D0086D23E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = 0; PRODUCT_NAME = SystemLibraries; }; - name = Development; + name = Release; }; - 2141DD0C123FFC7F0086D23E /* Development */ = { + 2141DD0C123FFC7F0086D23E /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_OPTIMIZATION_LEVEL = s; PRODUCT_NAME = SystemLibrariesStatic; }; - name = Development; + name = Release; }; - 2141DD1E123FFCDB0086D23E /* Development */ = { + 2141DD1E123FFCDB0086D23E /* Release */ = { 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", @@ -2307,102 +2641,142 @@ INSTALLHDRS_COPY_PHASE = YES; INSTALLHDRS_SCRIPT_PHASE = YES; INSTALL_PATH = /usr/local/lib/system; - PREBINDING = NO; PRODUCT_NAME = dns_sd; "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; }; - name = Development; + name = Release; }; - 2141DD25123FFD100086D23E /* Development */ = { + 2141DD25123FFD100086D23E /* Release */ = { 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; "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; }; - name = Development; + name = Release; }; - 2141DD2B123FFD2C0086D23E /* Development */ = { + 2141DD2B123FFD2C0086D23E /* Release */ = { 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_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/local/lib/system; - PREBINDING = NO; PRODUCT_NAME = dns_sd_profile; "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; }; - name = Development; + name = Release; }; - 2E0405F20C31955500F13B59 /* Development */ = { + 2E0405F20C31955500F13B59 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "helper-entitlements.plist"; + "APPLY_RULES_IN_COPY_FILES[sdk=appletvos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=iphoneos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=watchos*]" = YES; + CODE_SIGN_ENTITLEMENTS = "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_TREAT_WARNINGS_AS_ERRORS = NO; - GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; HEADER_SEARCH_PATHS = ( "${CONFIGURATION_TEMP_DIR}", "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", "$(SDKROOT)/usr/local/include", ); 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=macosx*][arch=*]" = ( - "-lipsec", - "-Wl,-pie", - ); - PREBINDING = NO; + MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_LDFLAGS = "-lipsec"; + "PLIST_FILE_OUTPUT_FORMAT[sdk=appletvos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=iphoneos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=watchos*]" = binary; PRODUCT_NAME = mDNSResponderHelper; PROVISIONING_PROFILE = ""; }; - name = Development; + name = Release; + }; + 37AF80271BF6997A00D657F6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + PRODUCT_NAME = dns_sd.jar; + }; + name = Release; + }; + 37AF80281BF6997A00D657F6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + PRODUCT_NAME = unittests; + }; + name = Release; }; - 4AE471680EAFF81900A6C5AD /* Development */ = { + 37AF80291BF6997A00D657F6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; - PRODUCT_NAME = dns_sd.jar; + PRODUCT_NAME = unittests; + }; + name = Debug; + }; + 37DDE9321BA383610092AC61 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = "$(CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION)"; + CLANG_WARN_CONSTANT_CONVERSION = "$(CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION)"; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = NO; + CLANG_WARN_ENUM_CONVERSION = "$(CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION)"; + CLANG_WARN_INT_CONVERSION = "$(CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION)"; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = NO; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_RETURN_TYPE = NO; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = NO; + GCC_WARN_UNUSED_FUNCTION = NO; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + STRIP_INSTALLED_PRODUCT = NO; }; name = Development; }; - 72FB5466166D5FB00090B2D9 /* Development */ = { + 72FB5466166D5FB00090B2D9 /* Release */ = { 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; @@ -2411,54 +2785,44 @@ 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_ENTITLEMENTS = "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_TREAT_WARNINGS_AS_ERRORS = 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/local/bin; MACOSX_DEPLOYMENT_TARGET = 10.9; - ONLY_ACTIVE_ARCH = NO; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SDKROOT = macosx; }; - name = Development; + name = Release; }; - 84C5B3371665529800C324A8 /* Development */ = { + 84C5B3371665529800C324A8 /* Release */ = { 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_TREAT_WARNINGS_AS_ERRORS = NO; GCC_WARN_64_TO_32_BIT_CONVERSION = NO; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES; @@ -2469,28 +2833,21 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; }; - name = Development; + name = Release; }; - D284BE290ADD78180027CCDF /* Development */ = { + D284BE290ADD78180027CCDF /* Release */ = { 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; + name = Release; }; - D284BE2C0ADD78180027CCDF /* Development */ = { + D284BE2C0ADD78180027CCDF /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3F347CF5185D57CD00367B40 /* base.xcconfig */; buildSettings = { - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/Release"; + CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)/Release"; + COPY_PHASE_STRIP = NO; DEAD_CODE_STRIPPING = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "__APPLE_USE_RFC_3542=1", @@ -2500,6 +2857,8 @@ "mDNSResponderVersion=${MVERS}", _LEGACY_NAT_TRAVERSAL_, "_BUILDING_XCODE_PROJECT_=1", + "BONJOUR_ON_DEMAND=1", + "USE_LIBIDN=1", ); GCC_TREAT_WARNINGS_AS_ERRORS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = NO; @@ -2508,7 +2867,7 @@ "-DUSE_SYSTEMCONFIGURATION_PRIVATE_HEADERS", "-fwrapv", ); - PREBINDING = NO; + SDKROOT = macosx.internal; STRIP_STYLE = debugging; WARNING_CFLAGS = ( "-W", @@ -2517,24 +2876,26 @@ "-Wno-four-char-constants", "-Wno-unknown-pragmas", "-Wshadow", + "-Wno-format", + "-Wformat-security", ); YACC_GENERATED_FILE_STEM = Standard; }; - name = Development; + name = Release; }; - D284BE6E0ADD80740027CCDF /* Development */ = { + D284BE6E0ADD80740027CCDF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "mDNSResponder-entitlements.plist"; + "APPLY_RULES_IN_COPY_FILES[sdk=appletvos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=iphoneos*]" = YES; + "APPLY_RULES_IN_COPY_FILES[sdk=watchos*]" = YES; + CODE_SIGN_ENTITLEMENTS = "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", + "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", ); - GCC_DYNAMIC_NO_PIC = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_TREAT_WARNINGS_AS_ERRORS = NO; HEADER_SEARCH_PATHS = ( "$(SDKROOT)/$(SYSTEM_LIBRARY_DIR)/Frameworks/Security.framework/PrivateHeaders", @@ -2547,129 +2908,47 @@ ); INSTALL_PATH = /usr/sbin; LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; - MACOSX_DEPLOYMENT_TARGET = 10.5; + MACOSX_DEPLOYMENT_TARGET = 10.10; 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 = ""; + "PLIST_FILE_OUTPUT_FORMAT[sdk=appletvos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=iphoneos*]" = binary; + "PLIST_FILE_OUTPUT_FORMAT[sdk=watchos*]" = binary; 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", - "$(SDKROOT)/usr/local/include", - ); - 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; + name = Release; }; - D284BEAE0ADD80920027CCDF /* Development */ = { + D284BEAE0ADD80920027CCDF /* Release */ = { 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; + name = Release; }; - D284BEBC0ADD809A0027CCDF /* Development */ = { + D284BEBC0ADD809A0027CCDF /* Release */ = { 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, @@ -2680,23 +2959,13 @@ 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; + name = Release; }; - D284BED70ADD80A20027CCDF /* Development */ = { + D284BED70ADD80A20027CCDF /* Release */ = { 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", @@ -2705,71 +2974,39 @@ ); 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"; + MACOSX_DEPLOYMENT_TARGET = 10.10; + OTHER_CFLAGS = "-UAPPLE_OSX_mDNSResponder"; PRODUCT_NAME = dnsextd; - SECTORDER_FLAGS = ""; }; - name = Development; + name = Release; }; - D284BEE60ADD80A70027CCDF /* Development */ = { + D284BEE60ADD80A70027CCDF /* Release */ = { 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 = ""; + MACOSX_DEPLOYMENT_TARGET = 10.6; + OTHER_LDFLAGS = "-Wl,-pie"; PRODUCT_NAME = ddnswriteconfig; - REZ_EXECUTABLE = YES; - SECTORDER_FLAGS = ""; }; - name = Development; + name = Release; }; - D284BF090ADD80B00027CCDF /* Development */ = { + D284BF090ADD80B00027CCDF /* Release */ = { 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 = ""; + MACOSX_DEPLOYMENT_TARGET = 10.6; PRODUCT_NAME = Bonjour; - SECTORDER_FLAGS = ""; WRAPPER_EXTENSION = prefPane; }; - name = Development; + name = Release; }; - FFA572380AF18F1C0055A0F1 /* Development */ = { + FFA572380AF18F1C0055A0F1 /* Release */ = { 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", @@ -2796,17 +3033,13 @@ ); PRODUCT_NAME = libsystem_dnssd_debug; }; - name = Development; + name = Release; }; - FFA572440AF18F450055A0F1 /* Development */ = { + FFA572440AF18F450055A0F1 /* Release */ = { 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)", @@ -2835,45 +3068,31 @@ ); PRODUCT_NAME = libsystem_dnssd_profile; }; - name = Development; + name = Release; }; - FFA5726F0AF191200055A0F1 /* Development */ = { + FFA5726F0AF191200055A0F1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - COPY_PHASE_STRIP = NO; - GCC_DYNAMIC_NO_PIC = NO; - GCC_GENERATE_DEBUGGING_SYMBOLS = YES; PRODUCT_NAME = SystemLibrariesDynamic; }; - name = Development; + name = Release; }; - FFB7657F0AEED99D00583A2C /* Development */ = { + FFB7657F0AEED99D00583A2C /* Release */ = { 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; + name = Release; }; - FFB7658A0AEED9FB00583A2C /* Development */ = { + FFB7658A0AEED9FB00583A2C /* Release */ = { 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", ); - GCC_TREAT_WARNINGS_AS_ERRORS = NO; HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; INSTALLHDRS_COPY_PHASE = YES; INSTALLHDRS_SCRIPT_PHASE = YES; @@ -2898,7 +3117,7 @@ ); PRODUCT_NAME = libsystem_dnssd; }; - name = Development; + name = Release; }; /* End XCBuildConfiguration section */ @@ -2906,63 +3125,81 @@ 03067D730C83A3CB0022BE1F /* Build configuration list for PBXAggregateTarget "Build Some" */ = { isa = XCConfigurationList; buildConfigurations = ( - 03067D740C83A3CB0022BE1F /* Development */, + 03067D740C83A3CB0022BE1F /* Release */, + 0C419F141BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 213FB21B12028A7C002B3A08 /* Build configuration list for PBXNativeTarget "BonjourEvents" */ = { isa = XCConfigurationList; buildConfigurations = ( - 213FB21A12028A7B002B3A08 /* Development */, + 213FB21A12028A7B002B3A08 /* Release */, + 0C419F1D1BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 2141DD08123FFB830086D23E /* Build configuration list for PBXAggregateTarget "SystemLibraries" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2141DCF9123FFB5D0086D23E /* Development */, + 2141DCF9123FFB5D0086D23E /* Release */, + 0C419F1E1BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 2141DD18123FFC990086D23E /* Build configuration list for PBXAggregateTarget "SystemLibrariesStatic" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2141DD0C123FFC7F0086D23E /* Development */, + 2141DD0C123FFC7F0086D23E /* Release */, + 0C419F1F1BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 2141DD1F123FFCF90086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_static" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2141DD1E123FFCDB0086D23E /* Development */, + 2141DD1E123FFCDB0086D23E /* Release */, + 0C419F211BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 2141DD35123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_debug_static" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2141DD25123FFD100086D23E /* Development */, + 2141DD25123FFD100086D23E /* Release */, + 0C419F221BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 2141DD36123FFD3B0086D23E /* Build configuration list for PBXNativeTarget "libdns_sd_profile_static" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2141DD2B123FFD2C0086D23E /* Development */, + 2141DD2B123FFD2C0086D23E /* Release */, + 0C419F231BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 2E0405F30C31956600F13B59 /* Build configuration list for PBXNativeTarget "mDNSResponderHelper" */ = { isa = XCConfigurationList; buildConfigurations = ( - 2E0405F20C31955500F13B59 /* Development */, + 2E0405F20C31955500F13B59 /* Release */, + 0C419F171BA20DF600A70FF7 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 37DDE9311BA383610092AC61 /* Build configuration list for PBXNativeTarget "unittests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 37DDE9321BA383610092AC61 /* Development */, + 37AF80281BF6997A00D657F6 /* Release */, + 37AF80291BF6997A00D657F6 /* Debug */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Development; @@ -2970,138 +3207,146 @@ 4AE471770EAFF84000A6C5AD /* Build configuration list for PBXLegacyTarget "dns_sd.jar" */ = { isa = XCConfigurationList; buildConfigurations = ( - 4AE471680EAFF81900A6C5AD /* Development */, + 0C419F291BA20DF600A70FF7 /* Debug */, + 37AF80271BF6997A00D657F6 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */ = { isa = XCConfigurationList; buildConfigurations = ( - 72FB5466166D5FB00090B2D9 /* Development */, + 72FB5466166D5FB00090B2D9 /* Release */, + 0C419F191BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 84C5B3361665529800C324A8 /* Build configuration list for PBXNativeTarget "dns_services" */ = { isa = XCConfigurationList; buildConfigurations = ( - 84C5B3371665529800C324A8 /* Development */, + 84C5B3371665529800C324A8 /* Release */, + 0C419F271BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; D284BE280ADD78180027CCDF /* Build configuration list for PBXAggregateTarget "Build More" */ = { isa = XCConfigurationList; buildConfigurations = ( - D284BE290ADD78180027CCDF /* Development */, + D284BE290ADD78180027CCDF /* Release */, + 0C419F131BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */ = { isa = XCConfigurationList; buildConfigurations = ( - D284BE2C0ADD78180027CCDF /* Development */, + D284BE2C0ADD78180027CCDF /* Release */, + 0C419F121BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; 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 */, + D284BE6E0ADD80740027CCDF /* Release */, + 0C419F161BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; D284BEAD0ADD80920027CCDF /* Build configuration list for PBXNativeTarget "dns-sd tool" */ = { isa = XCConfigurationList; buildConfigurations = ( - D284BEAE0ADD80920027CCDF /* Development */, + D284BEAE0ADD80920027CCDF /* Release */, + 0C419F181BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; D284BEBB0ADD809A0027CCDF /* Build configuration list for PBXNativeTarget "libjdns_sd.jnilib" */ = { isa = XCConfigurationList; buildConfigurations = ( - D284BEBC0ADD809A0027CCDF /* Development */, + D284BEBC0ADD809A0027CCDF /* Release */, + 0C419F281BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; D284BED60ADD80A20027CCDF /* Build configuration list for PBXNativeTarget "dnsextd" */ = { isa = XCConfigurationList; buildConfigurations = ( - D284BED70ADD80A20027CCDF /* Development */, + D284BED70ADD80A20027CCDF /* Release */, + 0C419F1A1BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; D284BEE50ADD80A70027CCDF /* Build configuration list for PBXNativeTarget "ddnswriteconfig" */ = { isa = XCConfigurationList; buildConfigurations = ( - D284BEE60ADD80A70027CCDF /* Development */, + D284BEE60ADD80A70027CCDF /* Release */, + 0C419F1B1BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; D284BF080ADD80B00027CCDF /* Build configuration list for PBXNativeTarget "PreferencePane" */ = { isa = XCConfigurationList; buildConfigurations = ( - D284BF090ADD80B00027CCDF /* Development */, + D284BF090ADD80B00027CCDF /* Release */, + 0C419F1C1BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; FFA572370AF18F1C0055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_debug_dynamic" */ = { isa = XCConfigurationList; buildConfigurations = ( - FFA572380AF18F1C0055A0F1 /* Development */, + FFA572380AF18F1C0055A0F1 /* Release */, + 0C419F251BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; FFA572430AF18F450055A0F1 /* Build configuration list for PBXNativeTarget "libdns_sd_profile_dynamic" */ = { isa = XCConfigurationList; buildConfigurations = ( - FFA572440AF18F450055A0F1 /* Development */, + FFA572440AF18F450055A0F1 /* Release */, + 0C419F261BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; FFA5726E0AF191200055A0F1 /* Build configuration list for PBXAggregateTarget "SystemLibrariesDynamic" */ = { isa = XCConfigurationList; buildConfigurations = ( - FFA5726F0AF191200055A0F1 /* Development */, + FFA5726F0AF191200055A0F1 /* Release */, + 0C419F201BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; FFB7657E0AEED99D00583A2C /* Build configuration list for PBXAggregateTarget "Build All" */ = { isa = XCConfigurationList; buildConfigurations = ( - FFB7657F0AEED99D00583A2C /* Development */, + FFB7657F0AEED99D00583A2C /* Release */, + 0C419F151BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; FFB765890AEED9FB00583A2C /* Build configuration list for PBXNativeTarget "libdns_sd_dynamic" */ = { isa = XCConfigurationList; buildConfigurations = ( - FFB7658A0AEED9FB00583A2C /* Development */, + FFB7658A0AEED9FB00583A2C /* Release */, + 0C419F241BA20DF600A70FF7 /* Debug */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Development; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme b/mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme new file mode 100644 index 0000000..6bc511e --- /dev/null +++ b/mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/Build All.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/mDNSResponder.xcscheme b/mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/mDNSResponder.xcscheme new file mode 100644 index 0000000..7ba2fe0 --- /dev/null +++ b/mDNSMacOSX/mDNSResponder.xcodeproj/xcshareddata/xcschemes/mDNSResponder.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mDNSMacOSX/mDNSResponderHelper.8 b/mDNSMacOSX/mDNSResponderHelper.8 index 6e959db..7f5836c 100644 --- a/mDNSMacOSX/mDNSResponderHelper.8 +++ b/mDNSMacOSX/mDNSResponderHelper.8 @@ -1,6 +1,6 @@ .\" -*- tab-width: 4 -*- .\" -.\" Copyright (c) 2007 Apple Computer, Inc. All Rights Reserved. +.\" Copyright (c) 2007-2009 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. diff --git a/mDNSMacOSX/mDNSResponderLogging.mobileconfig b/mDNSMacOSX/mDNSResponderLogging.mobileconfig deleted file mode 100644 index 34ec0d9..0000000 --- a/mDNSMacOSX/mDNSResponderLogging.mobileconfig +++ /dev/null @@ -1,57 +0,0 @@ - - - - - PayloadIdentifier - com.apple.mDNSResponder - PayloadUUID - 6D0962E1-558A-44FB-8FE0-1F4F0BD157F4 - PayloadDescription - Turns on mDNSResponder Debug Logging - PayloadDisplayName - mDNSResponder Debug Logging - PayloadOrganization - Apple, Inc - PayloadType - Configuration - PayloadVersion - 2 - - ConsentText - - en - English consent text - jp - Japanese consent text - default - Default consent text - used if none of the other languages match - - - PayloadContent - - - PayloadUUID - 6D0962E1-558A-44FB-8FE0-1F4F0BD157F4 - PayloadIdentifier - com.apple.defaults.1 - PayloadType - com.apple.defaults.managed - PayloadVersion - 1 - PayloadContent - - - DefaultsDomainName - com.apple.mDNSResponder - DefaultsData - - EnableLogging - - - - - - - - - diff --git a/mDNSMacOSX/pfkey.c b/mDNSMacOSX/pfkey.c index 839c8d7..6e95a6b 100644 --- a/mDNSMacOSX/pfkey.c +++ b/mDNSMacOSX/pfkey.c @@ -1,5 +1,6 @@ -/* - * Copyright (c) 2003-2007 Apple Computer, Inc. All rights reserved. +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2003-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. @@ -13,6 +14,7 @@ * 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 $ */ diff --git a/mDNSMacOSX/uDNSPathEvalulation.c b/mDNSMacOSX/uDNSPathEvalulation.c new file mode 100644 index 0000000..cc0d0ba --- /dev/null +++ b/mDNSMacOSX/uDNSPathEvalulation.c @@ -0,0 +1,155 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2013, 2015 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 "mDNSMacOSX.h" +#include +#include + +//Gets the DNSPolicy from NW PATH EVALUATOR +mDNSexport void mDNSPlatformGetDNSRoutePolicy(mDNS *const m, DNSQuestion *q, mDNSBool *isBlocked) +{ + (void) m; + q->ServiceID = -1; // initialize the ServiceID to default value of -1 + + // Return for non-unicast DNS queries, invalid pid, if NWPathEvaluation is already done by the client, or NWPathEvaluation not available on this OS + if (mDNSOpaque16IsZero(q->TargetQID) || (q->pid < 0) || (q->flags & kDNSServiceFlagsPathEvaluationDone) || !nw_endpoint_create_host) + { + *isBlocked = mDNSfalse; + return; + } + + mDNSs32 service_id; + mDNSu32 client_ifindex, dnspol_ifindex; + int retval; + struct proc_uniqidentifierinfo info; + mDNSBool isUUIDSet; + + char unenc_name[MAX_ESCAPED_DOMAIN_NAME]; + ConvertDomainNameToCString(&q->qname, unenc_name); + + nw_endpoint_t host = nw_endpoint_create_host(unenc_name, "0"); + if (host == NULL) + LogMsg("mDNSPlatformGetDNSRoutePolicy: DNS Route Policy: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] nw_endpoint_t host is NULL", q->qname.c, + DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID); + + nw_parameters_t parameters = nw_parameters_create(); + if (parameters == NULL) + LogMsg("mDNSPlatformGetDNSRoutePolicy: DNS Route Policy: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] nw_endpoint_t parameters is NULL", q->qname.c, + DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID); + + // Check for all the special (negative) internal value interface indices before initializing client_ifindex + if ( (q->InterfaceID == mDNSInterface_Any) + || (q->InterfaceID == mDNSInterface_Unicast) + || (q->InterfaceID == mDNSInterface_LocalOnly) + || (q->InterfaceID == mDNSInterfaceMark) + || (q->InterfaceID == mDNSInterface_P2P) + || (q->InterfaceID == uDNSInterfaceMark)) + { + client_ifindex = 0; + } + else + { + client_ifindex = (mDNSu32)(uintptr_t)q->InterfaceID; + } + + + if (client_ifindex > 0) + { + nw_interface_t client_intf = nw_interface_create_with_index(client_ifindex); + nw_parameters_require_interface(parameters, client_intf); + if (client_intf != NULL) + network_release(client_intf); + else + LogInfo("mDNSPlatformGetDNSRoutePolicy: DNS Route Policy: client_intf returned by nw_interface_create_with_index() is NULL"); + } + + nw_parameters_set_uid(parameters,(uid_t)q->euid); + + if (q->pid != 0) + { + nw_parameters_set_pid(parameters, q->pid); + retval = proc_pidinfo(q->pid, PROC_PIDUNIQIDENTIFIERINFO, 1, &info, sizeof(info)); + if (retval == (int)sizeof(info)) + { + nw_parameters_set_e_proc_uuid(parameters, info.p_uuid); + isUUIDSet = mDNStrue; + } + else + { + debugf("mDNSPlatformGetDNSRoutePolicy: proc_pidinfo returned %d", retval); + isUUIDSet = mDNSfalse; + } + } + else + { + nw_parameters_set_e_proc_uuid(parameters, q->uuid); + isUUIDSet = mDNStrue; + } + + nw_path_evaluator_t evaluator = nw_path_create_evaluator_for_endpoint(host, parameters); + if (evaluator == NULL) + LogMsg("mDNSPlatformGetDNSRoutePolicy: DNS Route Policy: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] nw_path_evaluator_t evaluator is NULL", q->qname.c, + DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID); + + if (host != NULL) + network_release(host); + if (parameters != NULL) + network_release(parameters); + + nw_path_t path = nw_path_evaluator_copy_path(evaluator); + if (path == NULL) + LogMsg("mDNSPlatformGetDNSRoutePolicy: DNS Route Policy: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] nw_path_t path is NULL", q->qname.c, + DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID); + + service_id = nw_path_get_flow_divert_unit(path); + if (service_id != 0) + { + q->ServiceID = service_id; + LogInfo("mDNSPlatformGetDNSRoutePolicy: DNS Route Policy: Query for %##s service ID is set ->service_ID:[%d] ", q->qname.c, service_id); + } + else + { + nw_interface_t nwpath_intf = nw_path_copy_scoped_interface(path); + if (nwpath_intf != NULL) + { + // Use the new scoped interface given by NW PATH EVALUATOR + dnspol_ifindex = nw_interface_get_index(nwpath_intf); + q->InterfaceID = (mDNSInterfaceID)(uintptr_t)dnspol_ifindex; + + network_release(nwpath_intf); + + if (dnspol_ifindex != client_ifindex) + LogInfo("mDNSPlatformGetDNSRoutePolicy: DNS Route Policy has changed the scoped ifindex from [%d] to [%d]", + client_ifindex, dnspol_ifindex); + } + else + { + debugf("mDNSPlatformGetDNSRoutePolicy: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] nw_interface_t nwpath_intf is NULL ", q->qname.c, DNSTypeName(q->qtype), q->pid, q->euid, q->ServiceID); + } + } + + if (isUUIDSet && (nw_path_get_status(path) == nw_path_status_unsatisfied) && (nw_path_get_reason(path) == nw_path_reason_policy_drop)) + *isBlocked = mDNStrue; + else + *isBlocked = mDNSfalse; + + if (path != NULL) + network_release(path); + if (evaluator != NULL) + network_release(evaluator); + +} diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c index 003ac63..6a5d362 100644 --- a/mDNSPosix/Identify.c +++ b/mDNSPosix/Identify.c @@ -330,8 +330,10 @@ mDNSexport int main(int argc, char **argv) if (StopNow == 2) break; } #endif - else { - if (strlen(arg) >= sizeof(hostname)) { + else + { + if (strlen(arg) >= sizeof(hostname)) + { fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname)); goto usage; } diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index 817a377..4f98e90 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -146,6 +146,7 @@ ifeq ($(os),x) # we get build failures: ‘daemon’ is deprecated (declared at /usr/include/stdlib.h:283) CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement \ -D__MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 \ + -DHAVE_STRLCPY=1 \ -D__APPLE_USE_RFC_2292 #-Wunreachable-code CC = gcc LD = $(CC) -dynamiclib @@ -153,6 +154,7 @@ LINKOPTS = -lSystem LDSUFFIX = dylib JDK = /System/Library/Frameworks/JavaVM.framework/Home JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM +OPTIONALTARG = dnsextd else $(error ERROR: Must specify target OS on command-line, e.g. "make os=x [target]".\ @@ -215,9 +217,9 @@ CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG) ############################################################################# -all: setup Daemon libdns_sd Clients SAClient SAResponder SAProxyResponder Identify NetMonitor dnsextd $(OPTIONALTARG) +all: setup Daemon libdns_sd Clients SAClient SAResponder SAProxyResponder Identify NetMonitor $(OPTIONALTARG) -install: setup InstalledDaemon InstalledStartup InstalledLib InstalledManPages InstalledClients $(OPTINSTALL) +install: setup InstalledStartup InstalledDaemon InstalledLib InstalledManPages InstalledClients $(OPTINSTALL) # 'setup' sets up the build directory structure the way we want setup: @@ -296,9 +298,10 @@ InstalledNSS: $(NSSINSTPATH)/$(NSSLINKNAME) /etc/nss_mdns.conf $(MANPATH)/man5/n @echo $+ " installed" # Note: If daemon already installed, we make sure it's stopped before overwriting it -$(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd +$(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) @if test -x $@; then $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) stop; fi $(CP) $< $@ + @$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) start $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libdns_sd.$(LDSUFFIX) $(CP) $< $@ @@ -311,12 +314,9 @@ endif $(INSTBASE)/include/dns_sd.h: $(SHAREDDIR)/dns_sd.h $(CP) $< $@ -# We make this target dependent on $(INSTBASE)/sbin/mdnsd because we need to ensure -# that the daemon is installed *before* we try to execute the command to start it. -$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) $(INSTBASE)/sbin/mdnsd +$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) $(CP) $< $@ chmod ugo+x $@ - $@ start ifdef RUNLEVELSCRIPTSDIR ifeq ($(wildcard $(RUNLEVELSCRIPTSDIR)/runlevels/default), $(RUNLEVELSCRIPTSDIR)/runlevels/default) $(LN) $@ $(RUNLEVELSCRIPTSDIR)/runlevels/default/mdns diff --git a/mDNSPosix/Responder.c b/mDNSPosix/Responder.c index 3996b7b..7a77bcd 100755 --- a/mDNSPosix/Responder.c +++ b/mDNSPosix/Responder.c @@ -503,6 +503,7 @@ static mStatus RegisterServicesInFile(const char *filePath) { mStatus status = mStatus_NoError; FILE * fp = fopen(filePath, "r"); + int rv; if (fp == NULL) { return mStatus_UnknownErr; @@ -603,7 +604,8 @@ static mStatus RegisterServicesInFile(const char *filePath) status = mStatus_UnknownErr; } - assert(0 == fclose(fp)); + rv = fclose(fp); + assert(rv == 0); return status; } diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c index 39cd66c..203cbac 100755 --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -138,7 +138,7 @@ mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipA // mDNS core calls this routine when it needs to send a packet. mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort, mDNSBool useBackgroundTrafficClass) { int err = 0; @@ -311,13 +311,6 @@ mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int s &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); } -mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src) -{ - (void)m; // unused - (void)src; // unused - return mDNSfalse; -} - mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass) { (void)m; // Unused @@ -516,6 +509,7 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) numOfServers++; } } + fclose(fp); return (numOfServers > 0) ? 0 : -1; } @@ -581,11 +575,20 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNS // interface must have already been deregistered with the mDNS core. mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf) { + int rv; assert(intf != NULL); if (intf->intfName != NULL) free((void *)intf->intfName); - if (intf->multicastSocket4 != -1) assert(close(intf->multicastSocket4) == 0); + if (intf->multicastSocket4 != -1) + { + rv = close(intf->multicastSocket4); + assert(rv == 0); + } #if HAVE_IPV6 - if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 0); + if (intf->multicastSocket6 != -1) + { + rv = close(intf->multicastSocket6); + assert(rv == 0); + } #endif // Move interface to the RecentInterfaces list for a minute @@ -639,10 +642,22 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf // ... with a shared UDP port, if it's for multicast receiving if (err == 0 && port.NotAnInteger) { - #if defined(SO_REUSEPORT) - err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); - #elif defined(SO_REUSEADDR) + // + // We test for SO_REUSEADDR first, as suggested by Jonny Törnbom from Axis Communications + // Linux kernel versions 3.9 introduces support for socket option + // SO_REUSEPORT, however this is not implemented the same as on *BSD + // systems. Linux version implements a "port hijacking" prevention + // mechanism, limiting processes wanting to bind to an already existing + // addr:port to have the same effective UID as the first who bound it. What + // this meant for us was that the daemon ran as one user and when for + // instance mDNSClientPosix was executed by another user, it wasn't allowed + // to bind to the socket. Our suggestion was to switch the order in which + // SO_REUSEPORT and SO_REUSEADDR was tested so that SO_REUSEADDR stays on + // top and SO_REUSEPORT to be used only if SO_REUSEADDR doesn't exist. + #if defined(SO_REUSEADDR) && !defined(__MAC_OS_X_VERSION_MIN_REQUIRED) err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn)); + #elif defined(SO_REUSEPORT) + err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn)); #else #error This platform has no way to avoid address busy errors on multicast. #endif @@ -836,7 +851,13 @@ mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interf } // Clean up - if (err != 0 && *sktPtr != -1) { assert(close(*sktPtr) == 0); *sktPtr = -1; } + if (err != 0 && *sktPtr != -1) + { + int rv; + rv = close(*sktPtr); + assert(rv == 0); + *sktPtr = -1; + } assert((err == 0) == (*sktPtr != -1)); return err; } @@ -855,7 +876,7 @@ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct assert(intfMask != NULL); // Allocate the interface structure itself. - intf = (PosixNetworkInterface*)malloc(sizeof(*intf)); + intf = (PosixNetworkInterface*)calloc(1, sizeof(*intf)); if (intf == NULL) { assert(0); err = ENOMEM; } // And make a copy of the intfName. @@ -907,9 +928,10 @@ mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct // and skip the probe phase of the probe/announce packet sequence. intf->coreIntf.DirectLink = mDNSfalse; #ifdef DIRECTLINK_INTERFACE_NAME - if (strcmp(intfName, STRINGIFY(DIRECTLINK_INTERFACE_NAME)) == 0) - intf->coreIntf.DirectLink = mDNStrue; + if (strcmp(intfName, STRINGIFY(DIRECTLINK_INTERFACE_NAME)) == 0) + intf->coreIntf.DirectLink = mDNStrue; #endif + intf->coreIntf.SupportsUnicastMDNSResponse = mDNStrue; // The interface is all ready to go, let's register it with the mDNS core. if (err == 0) @@ -1315,11 +1337,20 @@ mDNSexport mStatus mDNSPlatformInit(mDNS *const m) // In our case all we need to do is to tear down every network interface. mDNSexport void mDNSPlatformClose(mDNS *const m) { + int rv; assert(m != NULL); ClearInterfaceList(m); - if (m->p->unicastSocket4 != -1) assert(close(m->p->unicastSocket4) == 0); + if (m->p->unicastSocket4 != -1) + { + rv = close(m->p->unicastSocket4); + assert(rv == 0); + } #if HAVE_IPV6 - if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 0); + if (m->p->unicastSocket6 != -1) + { + rv = close(m->p->unicastSocket6); + assert(rv == 0); + } #endif } @@ -1366,14 +1397,36 @@ mDNSexport void mDNSPlatformUnlock (const mDNS *const m) // On the Posix platform this maps directly to the ANSI C strcpy. mDNSexport void mDNSPlatformStrCopy(void *dst, const void *src) { - strcpy((char *)dst, (char *)src); + strcpy((char *)dst, (const char *)src); +} + +mDNSexport mDNSu32 mDNSPlatformStrLCopy(void *dst, const void *src, mDNSu32 len) +{ +#if HAVE_STRLCPY + return ((mDNSu32)strlcpy((char *)dst, (const char *)src, len)); +#else + size_t srcLen; + + srcLen = strlen((const char *)src); + if (srcLen < len) + { + memcpy(dst, src, srcLen + 1); + } + else if (len > 0) + { + memcpy(dst, src, len - 1); + ((char *)dst)[len - 1] = '\0'; + } + + return ((mDNSu32)srcLen); +#endif } // mDNS core calls this routine to get the length of a C string. // On the Posix platform this maps directly to the ANSI C strlen. mDNSexport mDNSu32 mDNSPlatformStrLen (const void *src) { - return strlen((char*)src); + return strlen((const char*)src); } // mDNS core calls this routine to copy memory. @@ -1505,10 +1558,10 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte (void) iteration; } -mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) +mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(const AuthRecord *rr, mDNSInterfaceID InterfaceID) { (void) rr; - (void) intf; + (void) InterfaceID; return 1; } @@ -1567,43 +1620,38 @@ mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifnam return mStatus_NoError; } -mDNSexport mStatus mDNSPlatformClearSPSMACAddr(void) +mDNSexport mStatus mDNSPlatformClearSPSData(void) { return mStatus_NoError; } +mDNSexport mStatus mDNSPlatformStoreOwnerOptRecord(char *ifname, DNSMessage *msg, int length) +{ + (void) ifname; // Unused + (void) msg; // Unused + (void) length; // Unused + return mStatus_UnsupportedErr; +} + mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) { (void) sock; // unused - + return (mDNSu16)-1; } mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) { (void) InterfaceID; // unused - - return mDNSfalse; -} -mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) -{ - (void) m; - (void) q; - return mDNStrue; -} - -mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q) -{ - (void) m; - (void) q; - return -1; + return mDNSfalse; } -mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +mDNSexport void mDNSPlatformSetSocktOpt(void *sock, mDNSTransport_Type transType, mDNSAddr_Type addrType, const DNSQuestion *q) { - (void) src; - (void) dst; + (void) sock; + (void) transType; + (void) addrType; (void) q; } diff --git a/mDNSPosix/mDNSUNP.c b/mDNSPosix/mDNSUNP.c index b392fc7..17a37c6 100755 --- a/mDNSPosix/mDNSUNP.c +++ b/mDNSPosix/mDNSUNP.c @@ -83,14 +83,12 @@ void plen_to_mask(int plen, char *addr) { struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) { struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr; - FILE *fp; + FILE *fp = NULL; char addr[8][5]; int flags, myflags, index, plen, scope; char ifname[9], lastname[IFNAMSIZ]; char addr6[32+7+1]; /* don't forget the seven ':' */ struct addrinfo hints, *res0; - struct sockaddr_in6 *sin6; - struct in6_addr *addrptr; int err; int sockfd = -1; struct ifreq ifr; @@ -150,18 +148,13 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) char ipv6addr[INET6_ADDRSTRLEN]; plen_to_mask(plen, ipv6addr); ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6)); - if (ifi->ifi_addr == NULL) { + if (ifi->ifi_netmask == NULL) { goto gotError; } - sin6=calloc(1, sizeof(struct sockaddr_in6)); - addrptr=calloc(1, sizeof(struct in6_addr)); - inet_pton(family, ipv6addr, addrptr); - sin6->sin6_family=family; - sin6->sin6_addr=*addrptr; - sin6->sin6_scope_id=scope; - memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6)); - free(sin6); + ((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_family=family; + ((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_scope_id=scope; + inet_pton(family, ipv6addr, &((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_addr); /* Add interface name */ memcpy(ifi->ifi_name, ifname, IFI_NAME); @@ -179,6 +172,7 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) * EADDRNOTAVAIL for the main interface */ free(ifi->ifi_addr); + free(ifi->ifi_netmask); free(ifi); ifipnext = ifiptr; *ifipnext = ifipold; @@ -205,7 +199,12 @@ gotError: } done: if (sockfd != -1) { - assert(close(sockfd) == 0); + int rv; + rv = close(sockfd); + assert(rv == 0); + } + if (fp != NULL) { + fclose(fp); } return(ifihead); /* pointer to first structure in linked list */ } diff --git a/mDNSPosix/mdnsd.sh b/mDNSPosix/mdnsd.sh index 14fef9b..6e65612 100644 --- a/mDNSPosix/mdnsd.sh +++ b/mDNSPosix/mdnsd.sh @@ -36,10 +36,11 @@ if [ -r /sbin/start-stop-daemon ]; then # Suse Linux doesn't work with symbolic signal names, but we really don't need # to specify "-s TERM" since SIGTERM (15) is the default stop signal anway # STOP="start-stop-daemon --stop -s TERM --quiet --oknodo --exec" - STOP="start-stop-daemon --stop --quiet --oknodo --exec" + STOP="start-stop-daemon --stop --quiet --oknodo --retry 2 --exec" else killmdnsd() { kill -TERM `cat /var/run/mdnsd.pid` + sleep 1 } START= STOP=killmdnsd @@ -60,7 +61,6 @@ case "$1" in reload|restart|force-reload) echo -n "Restarting Apple Darwin Multicast DNS / DNS Service Discovery daemon:" $STOP $DAEMON - sleep 1 $START $DAEMON echo -n " mdnsd" ;; diff --git a/mDNSResponder.sln b/mDNSResponder.sln index 8cd5bf7..c93f95a 100755 --- a/mDNSResponder.sln +++ b/mDNSResponder.sln @@ -9,18 +9,33 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mdnsNSP", "mDNSWindows\mdnsNSP\mdnsNSP.vcxproj", "{F4F15529-F0EB-402F-8662-73C5797EE557}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPlugin", "Clients\ExplorerPlugin\ExplorerPlugin.vcxproj", "{BB8AC1B5-6587-4163-BDC6-788B157705CA}" + ProjectSection(ProjectDependencies) = postProject + {1643427B-F226-4AD6-B413-97DA64D5C6B4} = {1643427B-F226-4AD6-B413-97DA64D5C6B4} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizard", "Clients\PrinterSetupWizard\PrinterSetupWizard.vcxproj", "{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}" + ProjectSection(ProjectDependencies) = postProject + {967F5375-0176-43D3-ADA3-22EE25551C37} = {967F5375-0176-43D3-ADA3-22EE25551C37} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizardLocRes", "Clients\PrinterSetupWizard\PrinterSetupWizardLocRes.vcxproj", "{967F5375-0176-43D3-ADA3-22EE25551C37}" + ProjectSection(ProjectDependencies) = postProject + {CFCCB176-6CAA-472B-B0A2-90511C8E2E52} = {CFCCB176-6CAA-472B-B0A2-90511C8E2E52} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizardRes", "Clients\PrinterSetupWizard\PrinterSetupWizardRes.vcxproj", "{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPluginLocRes", "Clients\ExplorerPlugin\ExplorerPluginLocRes.vcxproj", "{1643427B-F226-4AD6-B413-97DA64D5C6B4}" + ProjectSection(ProjectDependencies) = postProject + {871B1492-B4A4-4B57-9237-FA798484D7D7} = {871B1492-B4A4-4B57-9237-FA798484D7D7} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPluginRes", "Clients\ExplorerPlugin\ExplorerPluginRes.vcxproj", "{871B1492-B4A4-4B57-9237-FA798484D7D7}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dns-sd", "Clients\DNS-SD.VisualStudio\dns-sd.vcxproj", "{AA230639-E115-4A44-AA5A-44A61235BA50}" + ProjectSection(ProjectDependencies) = postProject + {AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Java", "mDNSWindows\Java\Java.vcxproj", "{9CE2568A-3170-41C6-9F20-A0188A9EC114}" EndProject @@ -37,10 +52,16 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mDNSNetMonitor", "Clients\mDNSNetMonitor.VisualStudio\mDNSNetMonitor.vcxproj", "{AF35C285-528D-46A1-8A0E-47B0733DC718}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelLocRes", "mDNSWindows\ControlPanel\ControlPanelLocRes.vcxproj", "{4490229E-025A-478F-A2CF-51154DA83E39}" + ProjectSection(ProjectDependencies) = postProject + {5254AA9C-3D2E-4539-86D9-5EB0F4151215} = {5254AA9C-3D2E-4539-86D9-5EB0F4151215} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelRes", "mDNSWindows\ControlPanel\ControlPanelRes.vcxproj", "{5254AA9C-3D2E-4539-86D9-5EB0F4151215}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcxproj", "{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}" + ProjectSection(ProjectDependencies) = postProject + {4490229E-025A-478F-A2CF-51154DA83E39} = {4490229E-025A-478F-A2CF-51154DA83E39} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FirefoxExtension", "Clients\FirefoxExtension\FirefoxExtension.vcxproj", "{7826EA27-D4CC-4FAA-AD23-DF813823227B}" EndProject diff --git a/mDNSShared/CommonServices.h b/mDNSShared/CommonServices.h index 342479b..7fceac0 100644 --- a/mDNSShared/CommonServices.h +++ b/mDNSShared/CommonServices.h @@ -834,6 +834,12 @@ typedef unsigned long int uintptr_t; #endif #endif +// Limits + +#if( !defined( UINT32_MAX ) ) + #define UINT32_MAX UINT32_C( 4294967295 ) +#endif + #if 0 #pragma mark == bool == #endif diff --git a/mDNSShared/DebugServices.c b/mDNSShared/DebugServices.c index 98f876a..44305a5 100644 --- a/mDNSShared/DebugServices.c +++ b/mDNSShared/DebugServices.c @@ -1746,8 +1746,8 @@ DEBUG_EXPORT const char * DebugGetErrorString( int_least32_t inErrorCode, char CaseErrorStringifyHardCode( -65548, mStatus_NameConflict ); CaseErrorStringifyHardCode( -65549, mStatus_Invalid ); CaseErrorStringifyHardCode( -65550, mStatus_GrowCache ); - CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr ); - CaseErrorStringifyHardCode( -65552, mStatus_Incompatible ); + CaseErrorStringifyHardCode( -65551, mStatus_Incompatible ); + CaseErrorStringifyHardCode( -65552, mStatus_BadInterfaceErr ); CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged ); CaseErrorStringifyHardCode( -65792, mStatus_MemFree ); diff --git a/mDNSShared/GenLinkedList.c b/mDNSShared/GenLinkedList.c index 45dbb7c..1c6cb5b 100644 --- a/mDNSShared/GenLinkedList.c +++ b/mDNSShared/GenLinkedList.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-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. @@ -13,13 +13,6 @@ * 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. - - File: GenLinkedList.c - - Contains: implementation of generic linked lists. - - Version: 1.0 - Tabs: 4 spaces */ #include "GenLinkedList.h" diff --git a/mDNSShared/PlatformCommon.c b/mDNSShared/PlatformCommon.c index d86a755..49c8fd1 100644 --- a/mDNSShared/PlatformCommon.c +++ b/mDNSShared/PlatformCommon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2015 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. @@ -23,6 +23,10 @@ #include // Needed for sockaddr_in #include +#if APPLE_OSX_mDNSResponder +#include +#endif + #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" #include "PlatformCommon.h" @@ -174,6 +178,14 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m int syslog_level = LOG_ERR; switch (loglevel) { +#if APPLE_OSX_mDNSResponder + case MDNS_LOG_MSG: syslog_level = OS_LOG_TYPE_DEFAULT; break; + case MDNS_LOG_OPERATION: syslog_level = OS_LOG_TYPE_INFO; break; + case MDNS_LOG_SPS: syslog_level = OS_LOG_TYPE_INFO; break; + case MDNS_LOG_INFO: syslog_level = OS_LOG_TYPE_INFO; break; + case MDNS_LOG_DEBUG: syslog_level = OS_LOG_TYPE_DEBUG; break; + default: syslog_level = OS_LOG_TYPE_DEFAULT; break; +#else case MDNS_LOG_MSG: syslog_level = LOG_ERR; break; case MDNS_LOG_OPERATION: syslog_level = LOG_WARNING; break; case MDNS_LOG_SPS: syslog_level = LOG_NOTICE; break; @@ -182,6 +194,7 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m default: fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel); fflush(stderr); +#endif } if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; } @@ -190,10 +203,13 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m if (ident && ident[0] && mDNSPlatformClockDivisor) syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer); else -#elif APPLE_OSX_mDNSResponder - mDNSPlatformLogToFile(syslog_level, buffer); +#endif + { +#if APPLE_OSX_mDNSResponder + mDNSPlatformLogToFile(syslog_level, buffer); #else - syslog(syslog_level, "%s", buffer); + syslog(syslog_level, "%s", buffer); #endif + } } } diff --git a/mDNSShared/dns-sd.1 b/mDNSShared/dns-sd.1 index 9d8323b..d462ae2 100644 --- a/mDNSShared/dns-sd.1 +++ b/mDNSShared/dns-sd.1 @@ -1,6 +1,6 @@ .\" -*- tab-width: 4 -*- .\" -.\" Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. +.\" Copyright (c) 2004-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. diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index 3831527..660b370 100644 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2015 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: @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple 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. * @@ -66,7 +66,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 5763004 +#define _DNS_SD_H 7655009 #ifdef __cplusplus extern "C" { @@ -88,6 +88,13 @@ extern "C" { #define DNSSD_API #endif +#if defined(_WIN32) +#include +typedef SOCKET dnssd_sock_t; +#else +typedef int dnssd_sock_t; +#endif + /* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */ #if defined(__FreeBSD__) && (__FreeBSD__ < 5) #include @@ -180,6 +187,15 @@ enum * in the future they will be delivered as usual. */ + kDNSServiceFlagsAutoTrigger = 0x1, + /* Valid for browses using kDNSServiceInterfaceIndexAny. + * Will auto trigger the browse over AWDL as well once the service is discoveryed + * over BLE. + * This flag is an input value to DNSServiceBrowse(), which is why we can + * use the same value as kDNSServiceFlagsMoreComing, which is an output flag + * for various client callbacks. + */ + kDNSServiceFlagsAdd = 0x2, kDNSServiceFlagsDefault = 0x4, /* Flags for domain enumeration and browse/query reply callbacks. @@ -330,10 +346,21 @@ enum * * 5. Thread Safety * The dns_sd.h API does not presuppose any particular threading model, and consequently - * does no locking of its own (which would require linking some specific threading library). - * If client code calls API routines on the same DNSServiceRef concurrently - * from multiple threads, it is the client's responsibility to use a mutext - * lock or take similar appropriate precautions to serialize those calls. + * does no locking internally (which would require linking with a specific threading library). + * If the client concurrently, from multiple threads (or contexts), calls API routines using + * the same DNSServiceRef, it is the client's responsibility to provide mutual exclusion for + * that DNSServiceRef. + + * For example, use of DNSServiceRefDeallocate requires caution. A common mistake is as follows: + * Thread B calls DNSServiceRefDeallocate to deallocate sdRef while Thread A is processing events + * using sdRef. Doing this will lead to intermittent crashes on thread A if the sdRef is used after + * it was deallocated. + + * A telltale sign of this crash type is to see DNSServiceProcessResult on the stack preceding the + * actual crash location. + + * To state this more explicitly, mDNSResponder does not queue DNSServiceRefDeallocate so + * that it occurs discretely before or after an event is handled. */ kDNSServiceFlagsSuppressUnusable = 0x8000, @@ -405,7 +432,7 @@ enum kDNSServiceFlagsSecure = 0x200010, /* - * The response has been validated by verifying all the signaures in the response and was able to + * The response has been validated by verifying all the signatures in the response and was able to * build a successful authentication chain starting from a known trust anchor. */ @@ -497,12 +524,18 @@ enum * as the "serviceIndex". */ - kDNSServiceFlagsDenyExpensive = 0x20000000 + kDNSServiceFlagsDenyExpensive = 0x20000000, /* * This flag is meaningful only for Unicast DNS queries. When set, the kernel will restrict * DNS resolutions on interfaces defined as expensive for that request. */ + kDNSServiceFlagsPathEvaluationDone = 0x40000000 + /* + * This flag is meaningful for only Unicast DNS queries. + * When set, it indicates that Network PathEvaluation has already been performed. + */ + }; #define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault) @@ -667,24 +700,49 @@ enum * -- or -- * "Why is kDNSServiceMaxDomainName 1009, when the maximum legal domain name is 256 bytes?" * - * All strings used in the DNS-SD APIs are UTF-8 strings. Apart from the exceptions noted below, - * the APIs expect the strings to be properly escaped, using the conventional DNS escaping rules: + * All strings used in the DNS-SD APIs are UTF-8 strings. + * Apart from the exceptions noted below, the APIs expect the strings to be properly escaped, using the + * conventional DNS escaping rules, as used by the traditional DNS res_query() API, as described below: * - * '\\' represents a single literal '\' in the name - * '\.' represents a single literal '.' in the name + * Generally all UTF-8 characters (which includes all US ASCII characters) represent themselves, + * with two exceptions, the dot ('.') character, which is the label separator, + * and the backslash ('\') character, which is the escape character. + * The escape character ('\') is interpreted as described below: + * * '\ddd', where ddd is a three-digit decimal value from 000 to 255, - * represents a single literal byte with that value. - * A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain. + * represents a single literal byte with that value. Any byte value may be + * represented in '\ddd' format, even characters that don't strictly need to be escaped. + * For example, the ASCII code for 'w' is 119, and therefore '\119' is equivalent to 'w'. + * Thus the command "ping '\119\119\119.apple.com'" is the equivalent to the command "ping 'www.apple.com'". + * Nonprinting ASCII characters in the range 0-31 are often represented this way. + * In particular, the ASCII NUL character (0) cannot appear in a C string because C uses it as the + * string terminator character, so ASCII NUL in a domain name has to be represented in a C string as '\000'. + * Other characters like space (ASCII code 32) are sometimes represented as '\032' + * in contexts where having an actual space character in a C string would be inconvenient. + * + * Otherwise, for all cases where a '\' is followed by anything other than a three-digit decimal value + * from 000 to 255, the character sequence '\x' represents a single literal occurrence of character 'x'. + * This is legal for any character, so, for example, '\w' is equivalent to 'w'. + * Thus the command "ping '\w\w\w.apple.com'" is the equivalent to the command "ping 'www.apple.com'". + * However, this encoding is most useful when representing the characters '.' and '\', + * which otherwise would have special meaning in DNS name strings. + * This means that the following encodings are particularly common: + * '\\' represents a single literal '\' in the name + * '\.' represents a single literal '.' in the name + * + * A lone escape character ('\') appearing at the end of a string is not allowed, since it is + * followed by neither a three-digit decimal value from 000 to 255 nor a single character. + * If a lone escape character ('\') does appear as the last character of a string, it is silently ignored. * * The exceptions, that do not use escaping, are the routines where the full * DNS name of a resource is broken, for convenience, into servicename/regtype/domain. * In these routines, the "servicename" is NOT escaped. It does not need to be, since * it is, by definition, just a single literal string. Any characters in that string * represent exactly what they are. The "regtype" portion is, technically speaking, - * escaped, but since legal regtypes are only allowed to contain letters, digits, - * and hyphens, there is nothing to escape, so the issue is moot. The "domain" - * portion is also escaped, though most domains in use on the public Internet - * today, like regtypes, don't contain any characters that need to be escaped. + * escaped, but since legal regtypes are only allowed to contain US ASCII letters, + * digits, and hyphens, there is nothing to escape, so the issue is moot. + * The "domain" portion is also escaped, though most domains in use on the public + * Internet today, like regtypes, don't contain any characters that need to be escaped. * As DNS-SD becomes more popular, rich-text domains for service discovery will * become common, so software should be written to cope with domains with escaping. * @@ -724,9 +782,8 @@ enum * DNS server." Normally, most clients will use 0 for interface index to * automatically get the default sensible behaviour. * - * If the client passes a positive interface index, then for multicast names that - * indicates to do the operation only on that one interface. For unicast names the - * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set. + * If the client passes a positive interface index, then that indicates to do the + * operation only on that one specified interface. * * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering * a service, then that service will be found *only* by other local clients @@ -737,12 +794,23 @@ enum * in a way such that it does not inadvertently appear in service lists on * all the other machines on the network. * - * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing - * then it will find *all* records registered on that same local machine. - * Clients explicitly wishing to discover *only* LocalOnly services can - * accomplish this by inspecting the interfaceIndex of each service reported - * to their DNSServiceBrowseReply() callback function, and discarding those - * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. + * If the client passes kDNSServiceInterfaceIndexLocalOnly when querying or + * browsing, then the LocalOnly authoritative records and /etc/hosts caches + * are searched and will find *all* records registered or configured on that + * same local machine. + * + * If interested in getting negative answers to local questions while querying + * or browsing, then set both the kDNSServiceInterfaceIndexLocalOnly and the + * kDNSServiceFlagsReturnIntermediates flags. If no local answers exist at this + * moment in time, then the reply will return an immediate negative answer. If + * local records are subsequently created that answer the question, then those + * answers will be delivered, for as long as the question is still active. + * + * Clients explicitly wishing to discover *only* LocalOnly services during a + * browse may do this, without flags, by inspecting the interfaceIndex of each + * service reported to a DNSServiceBrowseReply() callback function, and + * discarding those answers where the interface index is not set to + * kDNSServiceInterfaceIndexLocalOnly. * * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, Register, * and Resolve operations. It should not be used in other DNSService APIs. @@ -771,6 +839,7 @@ enum #define kDNSServiceInterfaceIndexLocalOnly ((uint32_t)-1) #define kDNSServiceInterfaceIndexUnicast ((uint32_t)-2) #define kDNSServiceInterfaceIndexP2P ((uint32_t)-3) +#define kDNSServiceInterfaceIndexBLE ((uint32_t)-4) typedef uint32_t DNSServiceFlags; typedef uint32_t DNSServiceProtocol; @@ -828,29 +897,6 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty #define kDNSServiceProperty_DaemonVersion "DaemonVersion" - -// Map the source port of the local UDP socket that was opened for sending the DNS query -// to the process ID of the application that triggered the DNS resolution. -// -/* DNSServiceGetPID() Parameters: - * - * srcport: Source port (in network byte order) of the UDP socket that was created by - * the daemon to send the DNS query on the wire. - * - * pid: Process ID of the application that started the name resolution which triggered - * the daemon to send the query on the wire. The value can be -1 if the srcport - * cannot be mapped. - * - * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning - * if the daemon is not running. The value of the pid is undefined if the return - * value has error. - */ -DNSServiceErrorType DNSSD_API DNSServiceGetPID -( - uint16_t srcport, - int32_t *pid -); - /********************************************************************************************* * * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions @@ -871,8 +917,8 @@ DNSServiceErrorType DNSSD_API DNSServiceGetPID * a client can choose to fork a thread and have it loop calling "DNSServiceProcessResult(ref);" * If DNSServiceProcessResult() is called when no data is available for reading on the socket, it * will block until data does become available, and then process the data and return to the caller. - * The application is reponsible for checking the return value of DNSServiceProcessResult() to determine - * if the socket is valid and if it should continue to process data on the socket. + * The application is responsible for checking the return value of DNSServiceProcessResult() + * to determine if the socket is valid and if it should continue to process data on the socket. * When data arrives on the socket, the client is responsible for calling DNSServiceProcessResult(ref) * in a timely fashion -- if the client allows a large backlog of data to build up the daemon * may terminate the connection. @@ -883,7 +929,7 @@ DNSServiceErrorType DNSSD_API DNSServiceGetPID * error. */ -int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); +dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); /* DNSServiceProcessResult() @@ -1232,7 +1278,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe * with respect to a single DNSServiceRef. If you plan to have multiple threads * in your program simultaneously add, update, or remove records from the same - * DNSServiceRef, then it's the caller's responsibility to use a mutext lock + * DNSServiceRef, then it's the caller's responsibility to use a mutex lock * or take similar appropriate precautions to serialize those calls. * * Parameters; @@ -2242,8 +2288,8 @@ typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignmen * For most applications, DNS-SD TXT records are generally * less than 100 bytes, so in most cases a simple fixed-sized * 256-byte buffer will be more than sufficient. - * Recommended size limits for DNS-SD TXT Records are discussed in - * + * Recommended size limits for DNS-SD TXT Records are discussed in RFC 6763 + * * * Note: When passing parameters to and from these TXT record APIs, * the key name does not include the '=' character. The '=' character @@ -2293,8 +2339,8 @@ void DNSSD_API TXTRecordDeallocate * - Present with no value ("key" appears alone) * - Present with empty value ("key=" appears in TXT record) * - Present with non-empty value ("key=value" appears in TXT record) - * For more details refer to "Data Syntax for DNS-SD TXT Records" in - * + * For more details refer to "Data Syntax for DNS-SD TXT Records" in RFC 6763 + * * * txtRecord: A TXTRecordRef initialized by calling TXTRecordCreate(). * @@ -2606,41 +2652,6 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive ); #endif -#ifdef APPLE_OSX_mDNSResponder -/* DNSServiceCreateDelegateConnection() - * - * Create a delegate connection to the daemon allowing efficient registration of - * multiple individual records. - * - * Parameters: - * - * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating - * the reference (via DNSServiceRefDeallocate()) severs the - * connection and deregisters all records registered on this connection. - * - * pid : Process ID of the delegate - * - * uuid: UUID of the delegate - * - * Note that only one of the two arguments (pid or uuid) can be specified. If pid - * is zero, uuid will be assumed to be a valid value; otherwise pid will be used. - * - * return value: Returns kDNSServiceErr_NoError on success, otherwise returns - * an error code indicating the specific failure that occurred (in which - * case the DNSServiceRef is not initialized). kDNSServiceErr_NotAuth is - * returned to indicate that the calling process does not have entitlements - * to use this API. - */ -DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid); -#endif - -#ifdef __APPLE_API_PRIVATE - -#define kDNSServiceCompPrivateDNS "PrivateDNS" -#define kDNSServiceCompMulticastDNS "MulticastDNS" - -#endif //__APPLE_API_PRIVATE - /* Some C compiler cleverness. We can make the compiler check certain things for us, * and report errors at compile-time if anything is wrong. The usual way to do this would * be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but diff --git a/mDNSShared/dnsextd.8 b/mDNSShared/dnsextd.8 index 796caab..6611c82 100644 --- a/mDNSShared/dnsextd.8 +++ b/mDNSShared/dnsextd.8 @@ -1,6 +1,6 @@ .\" -*- tab-width: 4 -*- .\" -.\" Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. +.\" Copyright (c) 2004, 2006, 2009 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. diff --git a/mDNSShared/dnsextd.c b/mDNSShared/dnsextd.c index aa06650..b55d3f4 100644 --- a/mDNSShared/dnsextd.c +++ b/mDNSShared/dnsextd.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2015 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. @@ -560,6 +560,7 @@ mDNSlocal mDNSBool IsLLQRequest(PktMsg *pkt) ptr = LocateAdditionals(&pkt->msg, end); if (!ptr) goto end; + bzero(&lcr, sizeof(lcr)); // find last Additional info. for (i = 0; i < pkt->msg.h.numAdditionals; i++) { @@ -628,7 +629,6 @@ SetZone ) { domainname zname; - mDNSu8 QR_OP; const mDNSu8 * ptr = pkt->msg.data; mDNSBool exception = mDNSfalse; @@ -640,15 +640,13 @@ SetZone // Figure out what type of packet this is - QR_OP = ( mDNSu8 ) ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ); - if ( IsQuery( pkt ) ) { DNSQuestion question; // It's a query - ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); + getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); AppendDomainName( &zname, &question.qname ); @@ -661,7 +659,7 @@ SetZone // It's an update. The format of the zone section is the same as the format for the question section // according to RFC 2136, so we'll just treat this as a question so we can get at the zone. - ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); + getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question ); AppendDomainName( &zname, &question.qname ); @@ -1373,8 +1371,7 @@ mDNSlocal void DeleteRecords(DaemonInfo *d, mDNSBool DeleteAll) // Add, delete, or refresh records in table based on contents of a successfully completed dynamic update mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease) { - RRTableElem **rptr, *tmp; - int i, allocsize, bucket; + int i, allocsize; LargeCacheRecord lcr; ResourceRecord *rr = &lcr.r.resrec; const mDNSu8 *ptr, *end; @@ -1397,8 +1394,8 @@ mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease) ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); if (!ptr || lcr.r.resrec.RecordType == kDNSRecordTypePacketNegative) { Log("UpdateLeaseTable: GetLargeResourceRecord failed"); goto cleanup; } - bucket = rr->namehash % d->nbuckets; - rptr = &d->table[bucket]; + int bucket = rr->namehash % d->nbuckets; + RRTableElem *tmp, **rptr = &d->table[bucket]; // handle deletions if (rr->rrtype == kDNSQType_ANY && !rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength) @@ -1444,7 +1441,6 @@ mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease) { RehashTable(d); bucket = rr->namehash % d->nbuckets; - rptr = &d->table[bucket]; } if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; } allocsize = sizeof(RRTableElem); @@ -1475,12 +1471,11 @@ cleanup: // Replies are currently not signed !!!KRS change this mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease) { - PktMsg *reply; - mDNSu8 *ptr, *end; + PktMsg *const reply = malloc(sizeof(*reply)); + mDNSu8 *ptr; mDNSOpaque16 flags; - (void)d; //unused - reply = malloc(sizeof(*reply)); + if (!reply) { LogErr("FormatLeaseReply", "malloc"); return NULL; } flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update; flags.b[1] = 0; @@ -1488,9 +1483,7 @@ mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease) InitializeDNSMessage(&reply->msg.h, orig->msg.h.id, flags); reply->src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // unused except for log messages reply->src.sin_family = AF_INET; - ptr = reply->msg.data; - end = (mDNSu8 *)&reply->msg + sizeof(DNSMessage); - ptr = putUpdateLease(&reply->msg, ptr, lease); + ptr = putUpdateLease(&reply->msg, reply->msg.data, lease); if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; } reply->len = ptr - (mDNSu8 *)&reply->msg; HdrHToN(reply); @@ -2362,7 +2355,7 @@ mDNSlocal int RecvLLQ( DaemonInfo *d, PktMsg *pkt, TCPSocket *sock ) { qptr = getQuestion(&pkt->msg, qptr, end, 0, &q); if (!qptr) { Log("Malformatted LLQ from %s: cannot read question %d", addr, i); goto end; } - llq = (LLQOptData *)&opt.r.resrec.rdata->u.opt[0].u.llq + i; // point into OptData at index i + llq = &opt.r.resrec.rdata->u.opt[i].u.llq; // point into OptData at index i if (llq->vers != kLLQ_Vers) { Log("LLQ from %s contains bad version %d (expected %d)", addr, llq->vers, kLLQ_Vers); goto end; } e = LookupLLQ(d, pkt->src, &q.qname, q.qtype, &llq->id); @@ -2401,6 +2394,7 @@ mDNSlocal mDNSBool IsAuthorized( DaemonInfo * d, PktMsg * pkt, DomainAuthInfo ** HdrNToH(pkt); *key = NULL; + bzero(&lcr, sizeof(lcr)); if ( pkt->msg.h.numAdditionals ) { @@ -3100,7 +3094,7 @@ int main(int argc, char *argv[]) void mDNSCoreInitComplete( mDNS * const m, mStatus result) { ( void ) m; ( void ) result; } void mDNS_ConfigChanged(mDNS *const m) { ( void ) m; } void mDNSCoreMachineSleep(mDNS * const m, mDNSBool wake) { ( void ) m; ( void ) wake; } -void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, +void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID iid) { ( void ) m; ( void ) msg; ( void ) end; ( void ) srcaddr; ( void ) srcport; ( void ) dstaddr; ( void ) dstport; ( void ) iid; } diff --git a/mDNSShared/dnsextd_lexer.l b/mDNSShared/dnsextd_lexer.l index 5cac106..75df969 100644 --- a/mDNSShared/dnsextd_lexer.l +++ b/mDNSShared/dnsextd_lexer.l @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2006-2011 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-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. @@ -23,11 +23,6 @@ extern YYSTYPE yylval; -/* Mac OS X 10.4 has flex version 2.5.4, which doesn't define yylineno for us */ -/* Mac OS X 10.5 has flex version 2.5.33, which does define yylineno */ -#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION <= 4 -int yylineno = 1; -#endif #define YY_NO_INPUT 1 int yylex(void); diff --git a/mDNSShared/dnsextd_parser.y b/mDNSShared/dnsextd_parser.y index 18c5990..24d6269 100644 --- a/mDNSShared/dnsextd_parser.y +++ b/mDNSShared/dnsextd_parser.y @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2006-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. @@ -76,6 +76,7 @@ typedef struct ZoneSpec static StringListElem * g_stringList = NULL; +static StringListElem * g_addrList = NULL; static KeySpec * g_keys; static ZoneSpec * g_zones; static ZoneSpec g_zoneSpec; @@ -173,6 +174,22 @@ optionsstatement: | LISTEN_ON PORT NUMBER addresscontent { + mDNSIPPort listen_port = mDNSOpaque16fromIntVal( $3 ); + DaemonInfo* d = ( DaemonInfo* ) context; + d->addr.sin_port = ( listen_port.NotAnInteger) ? listen_port.NotAnInteger : UnicastDNSPort.NotAnInteger; + StringListElem* addr = g_addrList; + while (addr != NULL) + { + StringListElem* next; + // The first ipv4 address in {,} is used; the rest are ignored. + if (inet_pton( AF_INET, addr->string, &d->addr.sin_addr ) == 0) { + inet_pton( AF_INET, "127.0.0.1", &d->ns_addr.sin_addr ); + LogMsg("LISTEN_ON: An invalid ipv4 address, %s, detected.", addr->string); + } + next = addr->next; + free(addr); + addr = next; + } } | NAMESERVER ADDRESS networkaddress @@ -308,6 +325,20 @@ addressstatements: addressstatement: DOTTED_DECIMAL_ADDRESS { + StringListElem * elem; + + elem = ( StringListElem* ) malloc( sizeof( StringListElem ) ); + + if ( !elem ) + { + LogMsg("ERROR: memory allocation failure"); + YYABORT; + } + + elem->string = $1; + + elem->next = g_addrList; + g_addrList = elem; } ; diff --git a/mDNSShared/dnssd_clientlib.c b/mDNSShared/dnssd_clientlib.c index cca5885..cfc1d42 100644 --- a/mDNSShared/dnssd_clientlib.c +++ b/mDNSShared/dnssd_clientlib.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2004, Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2011 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: @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple 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. * diff --git a/mDNSShared/dnssd_clientshim.c b/mDNSShared/dnssd_clientshim.c index cb14310..c0a309d 100644 --- a/mDNSShared/dnssd_clientshim.c +++ b/mDNSShared/dnssd_clientshim.c @@ -87,7 +87,7 @@ typedef struct DNSQuestion q; } mDNS_DirectOP_QueryRecord; -int DNSServiceRefSockFD(DNSServiceRef sdRef) +dnssd_sock_t DNSServiceRefSockFD(DNSServiceRef sdRef) { (void)sdRef; // Unused return(0); diff --git a/mDNSShared/dnssd_clientstub.c b/mDNSShared/dnssd_clientstub.c index 5c1d02c..5cf4ebe 100644 --- a/mDNSShared/dnssd_clientstub.c +++ b/mDNSShared/dnssd_clientstub.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2015 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: @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple 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. * @@ -29,14 +29,15 @@ #include #include +#include "dnssd_ipc.h" + #if APPLE_OSX_mDNSResponder #include #include #include +#include "dns_sd_private.h" #endif -#include "dnssd_ipc.h" - #if defined(_WIN32) #define _SSIZE_T @@ -74,7 +75,7 @@ static void syslog( int priority, const char * message, ...) va_start( args, message ); len = _vscprintf( message, args ) + 1; buffer = malloc( len * sizeof(char) ); - if ( buffer ) { vsprintf( buffer, message, args ); OutputDebugString( buffer ); free( buffer ); } + if ( buffer ) { vsnprintf( buffer, len, message, args ); OutputDebugString( buffer ); free( buffer ); } WSASetLastError( err ); } #else @@ -171,6 +172,19 @@ struct _DNSRecordRef_t DNSServiceOp *sdr; }; +#if !defined(USE_TCP_LOOPBACK) +static void SetUDSPath(struct sockaddr_un *saddr, const char *path) +{ + size_t pathLen; + + pathLen = strlen(path); + if (pathLen < sizeof(saddr->sun_path)) + memcpy(saddr->sun_path, path, pathLen + 1); + else + saddr->sun_path[0] = '\0'; +} +#endif + // Write len bytes. Return 0 on success, -1 on error static int write_all(dnssd_sock_t sd, char *buf, size_t len) { @@ -184,7 +198,7 @@ static int write_all(dnssd_sock_t sd, char *buf, size_t len) // Should never happen. If it does, it indicates some OS bug, // or that the mDNSResponder daemon crashed (which should never happen). #if !defined(__ppc__) && defined(SO_ISDEFUNCT) - int defunct; + int defunct = 0; socklen_t dlen = sizeof (defunct); if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0) syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); @@ -271,6 +285,12 @@ static int more_bytes(dnssd_sock_t sd) fd_set *fs; int ret; +#if defined(_WIN32) + fs = &readfds; + FD_ZERO(fs); + FD_SET(sd, fs); + ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); +#else if (sd < FD_SETSIZE) { fs = &readfds; @@ -284,7 +304,7 @@ static int more_bytes(dnssd_sock_t sd) // two ints and not just one. int nfdbits = sizeof (int) * 8; int nints = (sd/nfdbits) + 1; - fs = (fd_set *)calloc(nints, sizeof(int)); + fs = (fd_set *)calloc(nints, (size_t)sizeof(int)); if (fs == NULL) { syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); @@ -295,6 +315,7 @@ static int more_bytes(dnssd_sock_t sd) ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); if (fs != &readfds) free(fs); +#endif return (ret > 0); } @@ -355,7 +376,7 @@ static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int struct timeval tv; if (gettimeofday(&tv, NULL) < 0) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; } - sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), + snprintf(ctrl_path, sizeof(ctrl_path), "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(), (unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec)); *len += strlen(ctrl_path) + 1; #else @@ -405,7 +426,11 @@ static void FreeDNSServiceOp(DNSServiceOp *x) // We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed // then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket) if ((x->sockfd ^ x->validator) != ValidatorBits) + { + static DNSServiceOp *op_were_not_going_to_free_but_we_need_to_fool_the_analyzer; syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator); + op_were_not_going_to_free_but_we_need_to_fool_the_analyzer = x; + } else { x->next = NULL; @@ -530,6 +555,11 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f char* uds_serverpath = getenv(MDNS_UDS_SERVERPATH_ENVVAR); if (uds_serverpath == NULL) uds_serverpath = MDNS_UDS_SERVERPATH; + else if (strlen(uds_serverpath) >= MAX_CTLPATH) + { + uds_serverpath = MDNS_UDS_SERVERPATH; + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: using default path since env len is invalid"); + } #endif *ref = NULL; sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0); @@ -551,7 +581,7 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f saddr.sin_port = htons(MDNS_TCP_SERVERPORT); #else saddr.sun_family = AF_LOCAL; - strcpy(saddr.sun_path, uds_serverpath); + SetUDSPath(&saddr, uds_serverpath); #if !defined(__ppc__) && defined(SO_DEFUNCTOK) { int defunct = 1; @@ -578,8 +608,10 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f } else { + #if !defined(USE_TCP_LOOPBACK) syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed path:%s Socket:%d Err:%d Errno:%d %s", uds_serverpath, sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno)); + #endif dnssd_close(sdr->sockfd); FreeDNSServiceOp(sdr); return kDNSServiceErr_ServiceNotRunning; @@ -597,21 +629,25 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) { + if (!hdr) + { + syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); + return kDNSServiceErr_Unknown; + } + uint32_t datalen = hdr->datalen; // We take a copy here because we're going to convert hdr->datalen to network byte order #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) char *const data = (char *)hdr + sizeof(ipc_msg_hdr); #endif dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket; DNSServiceErrorType err = kDNSServiceErr_Unknown; // Default for the "goto cleanup" cases - int MakeSeparateReturnSocket = 0; // Note: need to check hdr->op, not sdr->op. // hdr->op contains the code for the specific operation we're currently doing, whereas sdr->op // contains the original parent DNSServiceOp (e.g. for an add_record_request, hdr->op will be // add_record_request but the parent sdr->op will be connection_request or reg_service_request) - if (sdr->primary || - hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request) - MakeSeparateReturnSocket = 1; + const int MakeSeparateReturnSocket = (sdr->primary || + hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request); if (!DNSServiceRefValid(sdr)) { @@ -621,12 +657,6 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) return kDNSServiceErr_BadReference; } - if (!hdr) - { - syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); - return kDNSServiceErr_Unknown; - } - if (MakeSeparateReturnSocket) { #if defined(USE_TCP_LOOPBACK) @@ -661,7 +691,7 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) #ifndef NOT_HAVE_SA_LEN caddr.sun_len = sizeof(struct sockaddr_un); #endif - strcpy(caddr.sun_path, data); + SetUDSPath(&caddr, data); mask = umask(0); bindresult = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr)); umask(mask); @@ -842,7 +872,7 @@ cleanup: return err; } -int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) +dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) { if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with NULL DNSServiceRef"); return dnssd_InvalidSocket; } @@ -859,7 +889,7 @@ int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) return dnssd_InvalidSocket; } - return (int) sdRef->sockfd; + return sdRef->sockfd; } #if _DNS_SD_LIBDISPATCH @@ -1144,13 +1174,18 @@ void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef) DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *result, uint32_t *size) { + DNSServiceErrorType err; char *ptr; - size_t len = strlen(property) + 1; + size_t len; ipc_msg_hdr *hdr; DNSServiceOp *tmp; uint32_t actualsize; - DNSServiceErrorType err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL); + if (!property || !result || !size) + return kDNSServiceErr_BadParam; + + len = strlen(property) + 1; + err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL); if (err) return err; hdr = create_hdr(getproperty_request, &len, &ptr, 0, tmp); @@ -1158,6 +1193,8 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void * put_string(property, &ptr); err = deliver_request(hdr, tmp); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(tmp); return err; } + if (read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)) < 0) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } @@ -1182,24 +1219,17 @@ DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t * size_t len = sizeof(int32_t); DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL); - if (err) - return err; + if (err) return err; hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp); - if (!hdr) - { - DNSServiceRefDeallocate(tmp); - return kDNSServiceErr_NoMemory; - } + if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; } put_uint16(srcport, &ptr); err = deliver_request(hdr, tmp); // Will free hdr for us + if (err) { DNSServiceRefDeallocate(tmp); return err; } if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0) - { - DNSServiceRefDeallocate(tmp); - return kDNSServiceErr_ServiceNotRunning; - } + { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; } DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoError; @@ -1275,7 +1305,7 @@ DNSServiceErrorType DNSSD_API DNSServiceResolve ipc_msg_hdr *hdr; DNSServiceErrorType err; - if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; + if (!sdRef || !name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam; // Need a real InterfaceID for WakeOnResolve if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 && @@ -1350,6 +1380,9 @@ DNSServiceErrorType DNSSD_API DNSServiceQueryRecord ipc_msg_hdr *hdr; DNSServiceErrorType err; + // NULL name handled below. + if (!sdRef || !callBack) return kDNSServiceErr_BadParam; + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) flags |= kDNSServiceFlagsIncludeP2P; @@ -1391,6 +1424,7 @@ static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHead rdlen = get_uint16(&data, end); rdata = get_rdata (&data, end, rdlen); ttl = get_uint32(&data, end); + (void)rrclass; // Unused // We only generate client callbacks for A and AAAA results (including NXDOMAIN results for // those types, if the client has requested those with the kDNSServiceFlagsReturnIntermediates). @@ -1458,7 +1492,7 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo ipc_msg_hdr *hdr; DNSServiceErrorType err; - if (!hostname) return kDNSServiceErr_BadParam; + if (!sdRef || !hostname || !callBack) return kDNSServiceErr_BadParam; err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, callBack, context); if (err) @@ -1512,6 +1546,9 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse ipc_msg_hdr *hdr; DNSServiceErrorType err; + // NULL domain handled below + if (!sdRef || !regtype || !callBack) return kDNSServiceErr_BadParam; + if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) flags |= kDNSServiceFlagsIncludeP2P; @@ -1540,11 +1577,16 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain); DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain) { + DNSServiceErrorType err; DNSServiceOp *tmp; char *ptr; - size_t len = sizeof(flags) + strlen(domain) + 1; + size_t len; ipc_msg_hdr *hdr; - DNSServiceErrorType err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL); + + if (!domain) return kDNSServiceErr_BadParam; + len = sizeof(flags) + strlen(domain) + 1; + + err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL); if (err) return err; hdr = create_hdr(setdomain_request, &len, &ptr, 0, tmp); @@ -1590,8 +1632,8 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister DNSServiceErrorType err; union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder }; + if (!sdRef || !regtype) return kDNSServiceErr_BadParam; if (!name) name = ""; - if (!regtype) return kDNSServiceErr_BadParam; if (!domain) domain = ""; if (!host) host = ""; if (!txtRecord) txtRecord = (void*)""; @@ -1613,13 +1655,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister hdr = create_hdr(reg_service_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef); if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; } - - // If it is going over a shared connection, then don't set the IPC_FLAGS_NOREPLY - // as it affects all the operations over the shared connection. This is not - // a normal case and hence receiving the response back from the daemon and - // discarding it in ConnectionResponse is okay. - - if (!(flags & kDNSServiceFlagsShareConnection) && !callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY; + if (!callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY; put_flags(flags, &ptr); put_uint32(interfaceIndex, &ptr); @@ -1659,9 +1695,13 @@ DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains size_t len; ipc_msg_hdr *hdr; DNSServiceErrorType err; + int f1; + int f2; - int f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0; - int f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0; + if (!sdRef || !callBack) return kDNSServiceErr_BadParam; + + f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0; + f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0; if (f1 + f2 != 1) return kDNSServiceErr_BadParam; err = ConnectToServer(sdRef, flags, enumeration_request, handle_enumeration_response, callBack, context); @@ -1736,10 +1776,13 @@ static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *co DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) { + DNSServiceErrorType err; char *ptr; size_t len = 0; ipc_msg_hdr *hdr; - DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL); + + if (!sdRef) return kDNSServiceErr_BadParam; + err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL); if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL hdr = create_hdr(connection_request, &len, &ptr, 0, *sdRef); @@ -1757,6 +1800,7 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef * size_t len = 0; ipc_msg_hdr *hdr; + if (!sdRef) return kDNSServiceErr_BadParam; DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL); if (err) { @@ -1846,7 +1890,11 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord if ((interfaceIndex == kDNSServiceInterfaceIndexAny) && includeP2PWithIndexAny()) flags |= kDNSServiceFlagsIncludeP2P; - if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + if (!sdRef || !RecordRef || !fullname || (!rdata && rdlen) || !callBack) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL parameter"); + return kDNSServiceErr_BadParam; + } if (!DNSServiceRefValid(sdRef)) { @@ -1927,8 +1975,11 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord DNSRecordRef rref; DNSRecord **p; - if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } - if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSRecordRef pointer"); return kDNSServiceErr_BadParam; } + if (!sdRef || !RecordRef || (!rdata && rdlen)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL parameter"); + return kDNSServiceErr_BadParam; + } if (sdRef->op != reg_service_request) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with non-DNSServiceRegister DNSServiceRef %p %d", sdRef, sdRef->op); @@ -1988,7 +2039,11 @@ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord size_t len = 0; char *ptr; - if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + if (!sdRef || (!rdata && rdlen)) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL parameter"); + return kDNSServiceErr_BadParam; + } if (!DNSServiceRefValid(sdRef)) { @@ -2064,12 +2119,15 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord const void *rdata ) { + DNSServiceErrorType err; char *ptr; size_t len; ipc_msg_hdr *hdr; DNSServiceOp *tmp; - DNSServiceErrorType err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL); + if (!fullname || (!rdata && rdlen)) return kDNSServiceErr_BadParam; + + err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL); if (err) return err; len = sizeof(DNSServiceFlags); diff --git a/mDNSShared/dnssd_ipc.c b/mDNSShared/dnssd_ipc.c index 6059eb3..0fd7582 100644 --- a/mDNSShared/dnssd_ipc.c +++ b/mDNSShared/dnssd_ipc.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2011 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: @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple 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. * diff --git a/mDNSShared/dnssd_ipc.h b/mDNSShared/dnssd_ipc.h index 609fa64..96466a9 100644 --- a/mDNSShared/dnssd_ipc.h +++ b/mDNSShared/dnssd_ipc.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2015 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: @@ -10,7 +10,7 @@ * 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 + * 3. Neither the name of Apple 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. * @@ -41,7 +41,6 @@ # define dnssd_EWOULDBLOCK WSAEWOULDBLOCK # define dnssd_EINTR WSAEINTR # define dnssd_ECONNRESET WSAECONNRESET -# define dnssd_sock_t SOCKET # define dnssd_socklen_t int # define dnssd_close(sock) closesocket(sock) # define dnssd_errno WSAGetLastError() @@ -67,7 +66,6 @@ extern char *win32_strerror(int inErrorCode); # define dnssd_EINTR EINTR # define dnssd_ECONNRESET ECONNRESET # define dnssd_EPIPE EPIPE -# define dnssd_sock_t int # define dnssd_socklen_t unsigned int # define dnssd_close(sock) close(sock) # define dnssd_errno errno @@ -88,7 +86,7 @@ extern char *win32_strerror(int inErrorCode); # define MDNS_UDS_SERVERPATH_ENVVAR "DNSSD_UDS_PATH" # define LISTENQ 100 // longest legal control path length -# define MAX_CTLPATH 256 +# define MAX_CTLPATH (sizeof(((struct sockaddr_un*)0)->sun_path)) # define dnssd_sockaddr_t struct sockaddr_un #endif diff --git a/mDNSShared/mDNSDebug.c b/mDNSShared/mDNSDebug.c index cb4da6e..1243ae7 100644 --- a/mDNSShared/mDNSDebug.c +++ b/mDNSShared/mDNSDebug.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2015 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. @@ -13,13 +13,6 @@ * 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. - - File: mDNSDebug.c - - Contains: Implementation of debugging utilities. Requires a POSIX environment. - - Version: 1.0 - */ #include "mDNSDebug.h" diff --git a/mDNSShared/mDNSResponder.8 b/mDNSShared/mDNSResponder.8 index 48c1cf6..e9bd8e9 100644 --- a/mDNSShared/mDNSResponder.8 +++ b/mDNSShared/mDNSResponder.8 @@ -1,6 +1,6 @@ .\" -*- tab-width: 4 -*- .\" -.\" Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved. +.\" Copyright (c) 2003-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. @@ -80,9 +80,28 @@ A SIGINFO signal will dump a snapshot summary of the internal state to .Pa /var/log/system.log Ns : .Pp .Dl % sudo killall -INFO mDNSResponder +.Sh OPTIONAL ARGUMENTS +.Nm +accepts the following optional arguments: +.Bl -tag -width "AlwaysAppendSearchDomains" +.It Fl AlwaysAppendSearchDomains +Append search domains for multi-labeled Partially Qualified Domain Name as well as single-labeled Partially Qualified Domain Name. +This argument is not recommended because of the extra DNS traffic it generates and its adverse effect on battery life. +.It Fl NoMulticastAdvertisements +Prevent the system from advertising Bonjour services via Multicast DNS. +.El +.Pp +To cause +.Nm +to run with these optional arguments when it launches on OS X 10.11 (El Capitan) and later, set the +.Sy AlwaysAppendSearchDomains +or +.Sy NoMulticastAdvertisements +boolean keys to true in /Library/Preferences/com.apple.mDNSResponder.plist and reboot. +.Pp .Sh FILES -.Pa /usr/sbin/mDNSResponder \" Pathname -.\" +.Pa /usr/sbin/mDNSResponder +.Pa /Library/Preferences/com.apple.mDNSResponder.plist .Pp .Sh INFO .Pp @@ -112,5 +131,5 @@ daemon first appeared in Mac OS X 10.2 (Jaguar). Also available from the Darwin open source repository (though not officially supported by Apple) are .Nm -daemons for other platforms, including Mac OS 9, Microsoft Windows, +daemons for other platforms, including Microsoft Windows, Linux, FreeBSD, NetBSD, Solaris, and other POSIX systems. diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index 310df52..2552c8a 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2015 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. @@ -48,17 +48,18 @@ mDNSBool AlwaysAppendSearchDomains = mDNSfalse; #endif #endif -#ifdef LOCAL_PEERPID -#include // for LOCAL_PEERPID +#ifdef LOCAL_PEEREPID +#include // for LOCAL_PEEREPID #include // for getsockopt #include // for struct proc_bsdshortinfo #include // for proc_pidinfo() -#endif //LOCAL_PEERPID +#endif //LOCAL_PEEREPID //upto 16 characters of process name (defined in but we do not want to include that file) #define MAXCOMLEN 16 #if APPLE_OSX_mDNSResponder #include +#include "BLE.h" #if !NO_WCF @@ -171,6 +172,7 @@ struct request_state struct reply_state *replies; // corresponding (active) reply list req_termination_fn terminate; DNSServiceFlags flags; + mDNSu32 interfaceIndex; union { @@ -224,7 +226,8 @@ struct request_state DNSServiceFlags flags; DNSQuestion q_all; DNSQuestion q_default; - } enumeration; + DNSQuestion q_autoall; + } enumeration; struct { DNSQuestion q; @@ -270,11 +273,19 @@ typedef struct reply_state mDNSexport mDNS mDNSStorage; mDNSexport const char ProgramName[] = "mDNSResponder"; +#if defined(USE_TCP_LOOPBACK) +static char* boundPath = NULL; +#else +static char* boundPath = MDNS_UDS_SERVERPATH; +#endif +#if DEBUG +#define MDNS_UDS_SERVERPATH_DEBUG "/var/tmp/mDNSResponder" +#endif static dnssd_sock_t listenfd = dnssd_InvalidSocket; static request_state *all_requests = NULL; -#ifdef LOCAL_PEERPID +#ifdef LOCAL_PEEREPID struct proc_bsdshortinfo proc; -#endif //LOCAL_PEERPID +#endif //LOCAL_PEEREPID mDNSlocal void set_peer_pid(request_state *request); mDNSlocal void LogMcastClientInfo(request_state *req); mDNSlocal void GetMcastClients(request_state *req); @@ -283,6 +294,13 @@ static mDNSu32 i_mcount; // sets mcount when McastLogging is enabled(PROF sign static mDNSu32 n_mrecords; // tracks the current active mcast records for McastLogging static mDNSu32 n_mquests; // tracks the current active mcast questions for McastLogging + +#if TARGET_OS_EMBEDDED +mDNSu32 curr_num_regservices = 0; +mDNSu32 max_num_regservices = 0; +#endif + + // Note asymmetry here between registration and browsing. // For service registrations we only automatically register in domains that explicitly appear in local configuration data // (so AutoRegistrationDomains could equally well be called SCPrefRegDomains) @@ -317,10 +335,8 @@ mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int a mDNSlocal void FatalError(char *errmsg) { - char* ptr = NULL; LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno)); - *ptr = 0; // On OS X abort() doesn't generate a crash log, but writing to zero does - abort(); // On platforms where writing to zero doesn't generate an exception, abort instead + abort(); } mDNSlocal mDNSu32 dnssd_htonl(mDNSu32 l) @@ -341,9 +357,9 @@ mDNSlocal void my_perror(char *errmsg) mDNSlocal void my_throttled_perror(char *err_msg) { static int uds_throttle_count = 0; - if ((uds_throttle_count++ % 250) == 0) + if ((uds_throttle_count++ % 250) == 0) my_perror(err_msg); -} +} // LogMcastQuestion/LogMcastQ should be called after the DNSQuestion struct is initialized(especially for q->TargetQID) // Hence all calls are made after mDNS_StartQuery()/mDNS_StopQuery()/mDNS_StopBrowse() is called. @@ -361,9 +377,11 @@ mDNSlocal void LogMcastQuestion(mDNS *const m, const DNSQuestion *const q, reque { mcount--; } - LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Question" : "-Question", q->qname.c, DNSTypeName(q->qtype), - q->InterfaceID == mDNSInterface_LocalOnly ? "lo" : q->InterfaceID == mDNSInterface_P2P ? "p2p" : - q->InterfaceID == mDNSInterface_Any ? "any" : InterfaceNameForID(m, q->InterfaceID), + LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Question" : "-Question", q->qname.c, DNSTypeName(q->qtype), + q->InterfaceID == mDNSInterface_LocalOnly ? "lo" : + q->InterfaceID == mDNSInterface_P2P ? "p2p" : + q->InterfaceID == mDNSInterface_BLE ? "BLE" : + q->InterfaceID == mDNSInterface_Any ? "any" : InterfaceNameForID(m, q->InterfaceID), req->process_id, req->pid_name); LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse); } @@ -387,8 +405,10 @@ mDNSlocal void LogMcastService(mDNS *const m, const AuthRecord *const ar, reques mcount--; } LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Service" : "-Service", ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), - ar->resrec.InterfaceID == mDNSInterface_LocalOnly ? "lo" : ar->resrec.InterfaceID == mDNSInterface_P2P ? "p2p" : - ar->resrec.InterfaceID == mDNSInterface_Any ? "all" : InterfaceNameForID(m, ar->resrec.InterfaceID), + ar->resrec.InterfaceID == mDNSInterface_LocalOnly ? "lo" : + ar->resrec.InterfaceID == mDNSInterface_P2P ? "p2p" : + ar->resrec.InterfaceID == mDNSInterface_BLE ? "BLE" : + ar->resrec.InterfaceID == mDNSInterface_Any ? "all" : InterfaceNameForID(m, ar->resrec.InterfaceID), req->process_id, req->pid_name); LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse); } @@ -408,13 +428,13 @@ mDNSexport void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, { request_state *req, *r; for (req = all_requests; req; req=req->next) - { + { if (req->primary) // If this is a subbordinate operation, check that the parent is in the list - { - for (r = all_requests; r && r != req; r=r->next) - if (r == req->primary) + { + for (r = all_requests; r && r != req; r=r->next) + if (r == req->primary) goto foundpar; - } + } // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info GetMcastClients(req); foundpar:; @@ -433,28 +453,28 @@ mDNSexport void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, // wrong value if MulticastLogging is disabled and then re-enabled LogMcastNoIdent("--- START MCAST STATE LOG ---"); if (!all_requests) - { + { mcount = 0; LogMcastNoIdent(""); - } - else - { + } + else + { request_state *req, *r; for (req = all_requests; req; req=req->next) - { + { if (req->primary) // If this is a subbordinate operation, check that the parent is in the list - { - for (r = all_requests; r && r != req; r=r->next) - if (r == req->primary) + { + for (r = all_requests; r && r != req; r=r->next) + if (r == req->primary) goto foundparent; LogMcastNoIdent("%3d: Orphan operation; parent not found in request list", req->sd); - } + } // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info LogMcastClientInfo(req); foundparent:; } if(!mcount) // To initially set mcount - mcount = i_mcount; + mcount = i_mcount; } if (mcount == 0) { @@ -462,7 +482,7 @@ mDNSexport void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, LogMcastNoIdent("--- MCOUNT[%d]: IMPKTNUM[%d] ---", mcount, i_mpktnum); } if (mflag) - LogMcastNoIdent("--- MCOUNT[%d]: CMPKTNUM[%d] - IMPKTNUM[%d] = [%d]PKTS ---", mcount, m->MPktNum, i_mpktnum, (m->MPktNum - i_mpktnum)); + LogMcastNoIdent("--- MCOUNT[%d]: CMPKTNUM[%d] - IMPKTNUM[%d] = [%d]PKTS ---", mcount, m->MPktNum, i_mpktnum, (m->MPktNum - i_mpktnum)); LogMcastNoIdent("--- END MCAST STATE LOG ---"); } } @@ -508,6 +528,24 @@ mDNSlocal void abort_request(request_state *req) req->terminate = (req_termination_fn) ~0; } +#if DEBUG +mDNSexport void SetDebugBoundPath(void) +{ +#if !defined(USE_TCP_LOOPBACK) + boundPath = MDNS_UDS_SERVERPATH_DEBUG; +#endif +} + +mDNSexport int IsDebugSocketInUse(void) +{ +#if !defined(USE_TCP_LOOPBACK) + return !strcmp(boundPath, MDNS_UDS_SERVERPATH_DEBUG); +#else + return mDNSfalse; +#endif +} +#endif + mDNSlocal void AbortUnlinkAndFree(request_state *req) { request_state **p = &all_requests; @@ -546,10 +584,20 @@ mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, r // Append a reply to the list in a request object // If our request is sharing a connection, then we append our reply_state onto the primary's list +// If the request does not want asynchronous replies, then the reply is freed instead of being appended to any list. mDNSlocal void append_reply(request_state *req, reply_state *rep) { - request_state *r = req->primary ? req->primary : req; - reply_state **ptr = &r->replies; + request_state *r; + reply_state **ptr; + + if (req->no_reply) + { + freeL("reply_state/append_reply", rep); + return; + } + + r = req->primary ? req->primary : req; + ptr = &r->replies; while (*ptr) ptr = &(*ptr)->next; *ptr = rep; rep->next = NULL; @@ -602,9 +650,7 @@ mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const } } -// Special support to enable the DNSServiceBrowse call made by Bonjour Browser -// Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse -mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicename, const mDNSInterfaceID id, +mDNSlocal void GenerateBrowseReply(const domainname *const servicename, const mDNSInterfaceID id, request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err) { char namestr[MAX_DOMAIN_LABEL+1]; @@ -656,12 +702,13 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i mDNSu16 rdlen = get_uint16(&request->msgptr, request->msgend); const char *rdata = get_rdata (&request->msgptr, request->msgend, rdlen); mDNSu32 ttl = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0; - int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + size_t storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); AuthRecord *rr; mDNSInterfaceID InterfaceID; AuthRecType artype; request->flags = flags; + request->interfaceIndex = interfaceIndex; if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; } @@ -681,7 +728,7 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); if (InterfaceID == mDNSInterface_LocalOnly) artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) + else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE) artype = AuthRecordP2P; else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P) && (flags & kDNSServiceFlagsIncludeAWDL)) @@ -772,12 +819,14 @@ mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const d #pragma mark - external helpers #endif -mDNSlocal mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags) +mDNSexport mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags) { #if APPLE_OSX_mDNSResponder - if ( ((InterfaceID == mDNSInterface_Any) && (flags & (kDNSServiceFlagsIncludeP2P | kDNSServiceFlagsIncludeAWDL)) && IsLocalDomain(domain)) - || mDNSPlatformInterfaceIsD2D(InterfaceID)) + // Only call D2D layer routines if request applies to a D2D interface and the domain is "local". + if ( (((InterfaceID == mDNSInterface_Any) && (flags & (kDNSServiceFlagsIncludeP2P | kDNSServiceFlagsIncludeAWDL | kDNSServiceFlagsAutoTrigger))) + || mDNSPlatformInterfaceIsD2D(InterfaceID) || (InterfaceID == mDNSInterface_BLE)) + && IsLocalDomain(domain)) { return mDNStrue; } @@ -812,6 +861,14 @@ mDNSlocal void external_start_advertising_helper(service_instance *const instanc external_start_advertising_service(&instance->srs.RR_PTR.resrec, instance->request->flags); external_start_advertising_service(&instance->srs.RR_SRV.resrec, instance->request->flags); + +#if APPLE_OSX_mDNSResponder + if (applyToBLE(instance->srs.RR_SRV.resrec.InterfaceID, instance->request->flags)) + { + start_BLE_advertise(& instance->srs, instance->srs.RR_SRV.resrec.name , instance->srs.RR_SRV.resrec.rrtype, instance->request->flags); + } +#endif // APPLE_OSX_mDNSResponder + external_start_advertising_service(&instance->srs.RR_TXT.resrec, instance->request->flags); for (e = instance->srs.Extras; e; e = e->next) @@ -1004,6 +1061,9 @@ mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, m } else if (result == mStatus_MemFree) { +#if TARGET_OS_EMBEDDED + curr_num_regservices--; +#endif if (instance->request && instance->renameonmemfree) { external_stop_advertising_helper(instance); @@ -1066,7 +1126,7 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result); // We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination. - // If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback + // If the record has been updated, we need to free the rdata. Every time we call mDNS_Update, it calls update_callback // with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need // to free the latest rdata for which the update_callback was never called with. if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata); @@ -1138,23 +1198,24 @@ mDNSlocal void set_peer_pid(request_state *request) socklen_t len = sizeof(p); request->pid_name[0] = '\0'; request->process_id = -1; -#ifdef LOCAL_PEERPID - if (request->sd < 0) +#ifdef LOCAL_PEEREPID + if (request->sd < 0) return; - // to extract the pid value - if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEERPID, &p, &len) != 0) + // to extract the effective pid value + if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREPID, &p, &len) != 0) return; // to extract the process name from the pid value if (proc_pidinfo(p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) return; - mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm); + mDNSPlatformStrLCopy(request->pid_name, proc.pbsi_comm, sizeof(request->pid_name)); request->process_id = p; -#else // !LOCAL_PEERPID + debugf("set_peer_pid: Client PEEREPID is %d %s", p, request->pid_name); +#else // !LOCAL_PEEREPID len = 0; + LogInfo("set_peer_pid: Not Supported on this version of OS"); if (request->sd < 0) return; - LogInfo("set_peer_pid: Not Supported on this version of OS"); -#endif // LOCAL_PEERPID +#endif // LOCAL_PEEREPID } mDNSlocal void connection_termination(request_state *request) @@ -1185,7 +1246,7 @@ mDNSlocal void connection_termination(request_state *request) { registered_record_entry *ptr = request->u.reg_recs; LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), request->process_id, request->pid_name); - request->u.reg_recs = request->u.reg_recs->next; + request->u.reg_recs = request->u.reg_recs->next; ptr->rr->RecordContext = NULL; if (ptr->external_advertise) { @@ -1222,6 +1283,10 @@ mDNSlocal void handle_cancel_request(request_state *request) mDNSlocal mStatus handle_regrecord_request(request_state *request) { mStatus err = mStatus_BadParamErr; + + if (request->terminate != connection_termination) + { LogMsg("%3d: DNSServiceRegisterRecord(not a shared connection ref)", request->sd); return(err); } + AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1); if (rr) { @@ -1237,7 +1302,7 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) } // allocate registration entry, link into list re = mallocL("registered_record_entry", sizeof(registered_record_entry)); - if (!re) + if (!re) FatalError("ERROR: malloc"); re->key = request->hdr.reg_index; re->rr = rr; @@ -1248,7 +1313,7 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) rr->RecordCallback = regrecord_callback; re->origInterfaceID = rr->resrec.InterfaceID; - if (rr->resrec.InterfaceID == mDNSInterface_P2P) + if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any; #if 0 if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); @@ -1256,7 +1321,7 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) if (rr->resrec.rroriginalttl == 0) rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), request->process_id, request->pid_name); err = mDNS_Register(&mDNSStorage, rr); @@ -1268,7 +1333,7 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) } else { - LogMcastS(&mDNSStorage, rr, request, reg_start); + LogMcastS(&mDNSStorage, rr, request, reg_start); re->next = request->u.reg_recs; request->u.reg_recs = re; } @@ -1280,17 +1345,17 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m); mDNSlocal void regservice_termination_callback(request_state *request) { - if (!request) - { - LogMsg("regservice_termination_callback context is NULL"); - return; + if (!request) + { + LogMsg("regservice_termination_callback context is NULL"); + return; } while (request->u.servicereg.instances) { service_instance *p = request->u.servicereg.instances; request->u.servicereg.instances = request->u.servicereg.instances->next; // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) - LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", request->sd, p->srs.RR_SRV.resrec.name->c, + LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), request->process_id, request->pid_name); external_stop_advertising_helper(p); @@ -1309,9 +1374,9 @@ mDNSlocal void regservice_termination_callback(request_state *request) } } if (request->u.servicereg.txtdata) - { - freeL("service_info txtdata", request->u.servicereg.txtdata); - request->u.servicereg.txtdata = NULL; + { + freeL("service_info txtdata", request->u.servicereg.txtdata); + request->u.servicereg.txtdata = NULL; } if (request->u.servicereg.autoname) { @@ -1336,7 +1401,7 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance ServiceRecordSet *srs = &instance->srs; mStatus result; mDNSu32 coreFlags = 0; // translate to corresponding mDNSCore flag definitions - int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + size_t size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size); if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } @@ -1352,15 +1417,15 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance coreFlags |= coreFlagIncludeP2P; if (request->flags & kDNSServiceFlagsIncludeAWDL) coreFlags |= coreFlagIncludeAWDL; - + result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, coreFlags); - if (result) - { - freeL("ExtraResourceRecord/add_record_to_service", extra); - return result; + if (result) + { + freeL("ExtraResourceRecord/add_record_to_service", extra); + return result; } LogMcastS(&mDNSStorage, &srs->RR_PTR, request, reg_start); - + extra->ClientID = request->hdr.reg_index; if ( instance->external_advertise && callExternalHelpers(request->u.servicereg.InterfaceID, &instance->domain, request->flags)) @@ -1426,14 +1491,7 @@ mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd if (external_advertise) { ResourceRecord ext = rr->resrec; - DNSServiceFlags flags = 0; - - // Since we don't have a copy of the flags value used when the record was registered, - // we'll have to derive it from the ARType field. - if (rr->ARType == AuthRecordAnyIncludeP2P) - flags |= kDNSServiceFlagsIncludeP2P; - else if (rr->ARType == AuthRecordAnyIncludeAWDL) - flags |= kDNSServiceFlagsIncludeAWDL; + DNSServiceFlags flags = deriveD2DFlagsFromAuthRecType(rr->ARType); if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit; SetNewRData(&ext, oldrd, oldrdlen); @@ -1448,7 +1506,7 @@ exit: mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise) { mStatus result; - const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); + const size_t rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody); RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize); if (!newrd) FatalError("ERROR: malloc"); newrd->MaxRDLength = (mDNSu16) rdsize; @@ -1698,7 +1756,7 @@ mDNSexport mDNSs32 ChopSubTypes(char *regtype, char **AnonData) mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData) { AuthRecord *st = mDNSNULL; - // + // // "p" is pointing at the regtype e.g., _http._tcp followed by ":" indicated // by AnonData being non-NULL which is in turn follwed by "," indicated by // NumSubTypes being non-zero. We need to skip the initial regtype to get to the actual @@ -1746,7 +1804,7 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **Ano if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p)) { freeL("ServiceSubTypes", st); - if (*AnonData) + if (AnonData && *AnonData) freeL("AnonymousData", *AnonData); return(mDNSNULL); } @@ -1760,7 +1818,7 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **Ano mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain) { service_instance **ptr, *instance; - const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0; + size_t extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0; const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain); mStatus result; mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID; @@ -1817,14 +1875,14 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain char *AnonData = mDNSNULL; instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, &AnonData); if (AnonData) - instance->srs.AnonData = (const mDNSu8 *)AnonData; + instance->srs.AnonData = (const mDNSu8 *)AnonData; } if (request->u.servicereg.num_subtypes && !instance->subtypes) - { - unlink_and_free_service_instance(instance); - instance = NULL; - FatalError("ERROR: malloc"); + { + unlink_and_free_service_instance(instance); + instance = NULL; + FatalError("ERROR: malloc"); } result = mDNS_RegisterService(&mDNSStorage, &instance->srs, @@ -1838,7 +1896,7 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain if (!result) { *ptr = instance; // Append this to the end of our request->u.servicereg.instances list - LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", instance->request->sd, + LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port)); LogMcastS(&mDNSStorage, &instance->srs.RR_SRV, request, reg_start); } @@ -1856,10 +1914,6 @@ mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d { request_state *request; -#if APPLE_OSX_mDNSResponder - machserver_automatic_registration_domain_changed(&d->name, add); -#endif // APPLE_OSX_mDNSResponder - LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c); for (request = all_requests; request; request = request->next) { @@ -1975,7 +2029,6 @@ mDNSlocal mDNSBool PreDefinedInterfaceIndex(mDNSu32 interfaceIndex) case kDNSServiceInterfaceIndexUnicast: case kDNSServiceInterfaceIndexP2P: return mDNStrue; - break; default: return mDNSfalse; } @@ -1989,6 +2042,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) domainname d, srv; mStatus err; char *AnonData = mDNSNULL; + const char *msgTXTData; DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); @@ -2012,7 +2066,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) // If it's one of the specially defined inteface index values, just return an error. if (PreDefinedInterfaceIndex(interfaceIndex)) { - LogMsg("ERROR: handle_regservice_request: bad interfaceIndex %d", interfaceIndex); + LogInfo("handle_regservice_request: bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } @@ -2029,11 +2083,12 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); } request->flags = flags; + request->interfaceIndex = interfaceIndex; request->u.servicereg.InterfaceID = InterfaceID; request->u.servicereg.instances = NULL; request->u.servicereg.txtlen = 0; request->u.servicereg.txtdata = NULL; - mDNSPlatformStrCopy(request->u.servicereg.type_as_string, type_as_string); + mDNSPlatformStrLCopy(request->u.servicereg.type_as_string, type_as_string, sizeof(request->u.servicereg.type_as_string)); if (request->msgptr + 2 > request->msgend) request->msgptr = NULL; else @@ -2043,21 +2098,23 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) } request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend); + msgTXTData = get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen); + + if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + if (request->u.servicereg.txtlen) { request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen); if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc"); - mDNSPlatformMemCopy(request->u.servicereg.txtdata, get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen), request->u.servicereg.txtlen); + mDNSPlatformMemCopy(request->u.servicereg.txtdata, msgTXTData, request->u.servicereg.txtlen); } - if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - // Check for sub-types after the service type request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string, &AnonData); // Note: Modifies regtype string to remove trailing subtypes if (request->u.servicereg.num_subtypes < 0) { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); - return(mStatus_BadParamErr); + goto bad_param; } if (AnonData) { @@ -2065,7 +2122,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) if (AnonDataLen > MAX_ANONYMOUS_DATA) { LogMsg("ERROR: handle_regservice_request: AnonDataLen %d", AnonDataLen); - return(mStatus_BadParamErr); + goto bad_param; } request->u.servicereg.AnonData = mDNStrue; } @@ -2076,7 +2133,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string)) - { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); } + { LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); goto bad_param; } if (!name[0]) { @@ -2092,7 +2149,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) name[newlen] = 0; } if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name)) - { LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); } + { LogMsg("ERROR: handle_regservice_request - name bad %s", name); goto bad_param; } request->u.servicereg.autoname = mDNSfalse; } @@ -2100,7 +2157,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) { request->u.servicereg.default_domain = mDNSfalse; if (!MakeDomainNameFromDNSNameString(&d, domain)) - { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); } + { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); goto bad_param; } } else { @@ -2109,19 +2166,16 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) } // We don't allow the anonymous and the regular ones to coexist - if (!CheckForMixedRegistrations(&request->u.servicereg.type, &d, request->u.servicereg.AnonData)) - { - return(mStatus_BadParamErr); - } + if (!CheckForMixedRegistrations(&request->u.servicereg.type, &d, request->u.servicereg.AnonData)) { goto bad_param; } if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d)) { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", - request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr); + request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); goto bad_param; } if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host)) - { LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); } + { LogMsg("ERROR: handle_regservice_request - host bad %s", host); goto bad_param; } request->u.servicereg.autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0; request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0; @@ -2137,7 +2191,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) } LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START PID[%d](%s)", - request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, + request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port), request->process_id, request->pid_name); // We need to unconditionally set request->terminate, because even if we didn't successfully @@ -2149,6 +2203,12 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) request->terminate = regservice_termination_callback; err = register_service_instance(request, &d); + +#if TARGET_OS_EMBEDDED + ++curr_num_regservices; + if (curr_num_regservices > max_num_regservices) + max_num_regservices = curr_num_regservices; +#endif #if 0 err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError; @@ -2168,6 +2228,11 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) } return(err); + +bad_param: + freeL("handle_regservice_request (txtdata)", request->u.servicereg.txtdata); + request->u.servicereg.txtdata = NULL; + return mStatus_BadParamErr; } // *************************************************************************** @@ -2191,14 +2256,21 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc flags |= kDNSServiceFlagsThresholdReached; } + // if returning a negative answer, then use question's name in reply + if (answer->RecordType == kDNSRecordTypePacketNegative) + { + GenerateBrowseReply(&question->qname, answer->InterfaceID, req, &rep, browse_reply_op, flags, kDNSServiceErr_NoSuchRecord); + goto validReply; + } + if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError) { if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) { // Special support to enable the DNSServiceBrowse call made by Bonjour Browser // Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse - GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError); - goto bonjourbrowserhack; + GenerateBrowseReply(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError); + goto validReply; } LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer", @@ -2206,7 +2278,7 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc return; } -bonjourbrowserhack: +validReply: LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s", req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", @@ -2215,6 +2287,36 @@ bonjourbrowserhack: append_reply(req, rep); } +mDNSlocal void SetQuestionPolicy(DNSQuestion *q, request_state *req) +{ + int i; + q->euid = req->uid; + // The policy is either based on pid or UUID. Pass a zero pid + // to the "core" if the UUID is valid. If we always pass the pid, + // then the "core" needs to determine whether the uuid is valid + // by examining all the 16 bytes at the time of the policy + // check and also when setting the delegate socket option. Also, it + // requires that we zero out the uuid wherever the question is + // initialized to make sure that it is not interpreted as valid. + // To prevent these intrusive changes, just pass a zero pid to indicate + // that pid is not valid when uuid is valid. In future if we need the + // pid in the question, we will reevaluate this strategy. + if (req->validUUID) + { + for (i = 0; i < UUID_SIZE; i++) + { + q->uuid[i] = req->uuid[i]; + } + q->pid = 0; + } + else + { + q->pid = req->process_id; + } + + //debugf("SetQuestionPolicy: q->euid[%d] q->pid[%d] uuid is valid : %s", q->euid, q->pid, req->validUUID ? "true" : "false"); +} + mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d) { browser_t *b, *p; @@ -2228,8 +2330,10 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d b = mallocL("browser_t", sizeof(*b)); if (!b) return mStatus_NoMemoryErr; + mDNSPlatformMemZero(b, sizeof(*b)); AssignDomainName(&b->domain, d); - err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.AnonData, info->u.browser.interface_id, info->flags, + SetQuestionPolicy(&b->q, info); + err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.AnonData, info->u.browser.interface_id, info->flags, info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info); if (err) { @@ -2240,7 +2344,7 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d { b->next = info->u.browser.browsers; info->u.browser.browsers = b; - LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, info->process_id, + LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, info->process_id, info->pid_name); LogMcastQ(&mDNSStorage, &b->q, info, q_start); if (callExternalHelpers(info->u.browser.interface_id, &b->domain, info->flags)) @@ -2248,7 +2352,7 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d domainname tmp; ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain); LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()"); - external_start_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags); + external_start_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags, &b->q); } } return err; @@ -2268,18 +2372,18 @@ mDNSlocal void browse_termination_callback(request_state *info) { browser_t *ptr = info->u.browser.browsers; - if (callExternalHelpers(info->u.browser.interface_id, &ptr->domain, info->flags)) + if (callExternalHelpers(ptr->q.InterfaceID, &ptr->domain, ptr->q.flags)) { domainname tmp; ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain); LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()"); - external_stop_browsing_for_service(info->u.browser.interface_id, &tmp, kDNSType_PTR, info->flags); + external_stop_browsing_for_service(ptr->q.InterfaceID, &tmp, kDNSType_PTR, ptr->q.flags); } info->u.browser.browsers = ptr->next; LogOperation("%3d: DNSServiceBrowse(%##s) STOP PID[%d](%s)", info->sd, ptr->q.qname.c, info->process_id, info->pid_name); mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result - LogMcastQ(&mDNSStorage, &ptr->q, info, q_stop); + LogMcastQ(&mDNSStorage, &ptr->q, info, q_stop); freeL("browser_t/browse_termination_callback", ptr); } } @@ -2289,10 +2393,6 @@ mDNSlocal void udsserver_automatic_browse_domain_changed(const DNameListElem *co request_state *request; debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c); -#if APPLE_OSX_mDNSResponder - machserver_automatic_browse_domain_changed(&d->name, add); -#endif // APPLE_OSX_mDNSResponder - for (request = all_requests; request; request = request->next) { if (request->terminate != browse_termination_callback) continue; // Not a browse operation @@ -2455,9 +2555,13 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m) { int num_autoname = 0; request_state *req; + + // Don't need to register the device info record for kDNSServiceInterfaceIndexLocalOnly registrations. for (req = all_requests; req; req = req->next) - if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname) + { + if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname && req->interfaceIndex != kDNSServiceInterfaceIndexLocalOnly) num_autoname++; + } // If DeviceInfo record is currently registered, see if we need to deregister it if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered) @@ -2602,7 +2706,7 @@ mDNSlocal mStatus handle_browse_request(request_state *request) // If it's one of the specially defined inteface index values, just return an error. if (PreDefinedInterfaceIndex(interfaceIndex)) { - LogMsg("ERROR: handle_browse_request: bad interfaceIndex %d", interfaceIndex); + LogInfo("handle_browse_request: bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } @@ -2618,6 +2722,7 @@ mDNSlocal mStatus handle_browse_request(request_state *request) if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } request->flags = flags; + request->interfaceIndex = interfaceIndex; typedn.c[0] = 0; NumSubTypes = ChopSubTypes(regtype, &AnonData); // Note: Modifies regtype string to remove trailing subtypes if (NumSubTypes < 0 || NumSubTypes > 1) @@ -2644,7 +2749,7 @@ mDNSlocal mStatus handle_browse_request(request_state *request) if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr); // For over-long service types, we only allow domain "local" - if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrCopy(domain, "local."); + if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrLCopy(domain, "local.", sizeof(domain)); // Set up browser info request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0; @@ -2653,7 +2758,7 @@ mDNSlocal mStatus handle_browse_request(request_state *request) request->u.browser.default_domain = !domain[0]; request->u.browser.browsers = NULL; - LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)", + LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)", request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, request->process_id, request->pid_name); if (request->u.browser.default_domain) @@ -2709,10 +2814,12 @@ mDNSlocal mStatus handle_browse_request(request_state *request) mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) { size_t len = 0; - char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME]; + char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME] = "0"; char *data; reply_state *rep; request_state *req = question->QuestionContext; + const DNSServiceErrorType error = + (answer->RecordType == kDNSRecordTypePacketNegative) ? kDNSServiceErr_NoSuchRecord : kDNSServiceErr_NoError; (void)m; // Unused LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); @@ -2730,7 +2837,9 @@ mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, con if (!req->u.resolve.txt || !req->u.resolve.srv) return; // only deliver result to client if we have both answers ConvertDomainNameToCString(answer->name, fullname); - ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target); + + if (answer->RecordType != kDNSRecordTypePacketNegative) + ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target); // calculate reply length len += sizeof(DNSServiceFlags); @@ -2745,7 +2854,7 @@ mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, con rep = create_reply(resolve_reply_op, len, req); rep->rhdr->flags = dnssd_htonl(0); rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse)); - rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError); + rep->rhdr->error = dnssd_htonl(error); data = (char *)&rep->rhdr[1]; @@ -2767,7 +2876,7 @@ mDNSlocal void resolve_termination_callback(request_state *request) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_stop); - if (request->u.resolve.external_advertise) + if (request->u.resolve.external_advertise) external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags); } @@ -2800,7 +2909,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) // If it's one of the specially defined inteface index values, just return an error. if (PreDefinedInterfaceIndex(interfaceIndex)) { - LogMsg("ERROR: handle_resolve_request: bad interfaceIndex %d", interfaceIndex); + LogInfo("handle_resolve_request: bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } @@ -2823,6 +2932,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve)); request->flags = flags; + request->interfaceIndex = interfaceIndex; // format questions request->u.resolve.qsrv.InterfaceID = InterfaceID; @@ -2836,8 +2946,6 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qsrv.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; request->u.resolve.qsrv.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; request->u.resolve.qsrv.SuppressUnusable = mDNSfalse; - request->u.resolve.qsrv.DenyOnCellInterface = mDNSfalse; - request->u.resolve.qsrv.DenyOnExpInterface = mDNSfalse; request->u.resolve.qsrv.SearchListIndex = 0; request->u.resolve.qsrv.AppendSearchDomains = 0; request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse; @@ -2850,6 +2958,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qsrv.qnameOrig = mDNSNULL; request->u.resolve.qsrv.AnonInfo = mDNSNULL; request->u.resolve.qsrv.pid = request->process_id; + request->u.resolve.qsrv.euid = request->uid; request->u.resolve.qsrv.QuestionCallback = resolve_result_callback; request->u.resolve.qsrv.QuestionContext = request; @@ -2864,8 +2973,6 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qtxt.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; request->u.resolve.qtxt.SuppressUnusable = mDNSfalse; - request->u.resolve.qtxt.DenyOnCellInterface = mDNSfalse; - request->u.resolve.qtxt.DenyOnExpInterface = mDNSfalse; request->u.resolve.qtxt.SearchListIndex = 0; request->u.resolve.qtxt.AppendSearchDomains = 0; request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse; @@ -2878,6 +2985,7 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qtxt.qnameOrig = mDNSNULL; request->u.resolve.qtxt.AnonInfo = mDNSNULL; request->u.resolve.qtxt.pid = request->process_id; + request->u.resolve.qtxt.euid = request->uid; request->u.resolve.qtxt.QuestionCallback = resolve_result_callback; request->u.resolve.qtxt.QuestionContext = request; @@ -2890,15 +2998,15 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) #endif // ask the questions - LogOperation("%3d: DNSServiceResolve(%X %d %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, - request->u.resolve.qsrv.qname.c, request->process_id, request->pid_name); + LogOperation("%3d: DNSServiceResolve(%X %d %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, + request->u.resolve.qsrv.qname.c, request->process_id, request->pid_name); err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv); - + if (!err) { err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); if (err) - { + { mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); } else @@ -3163,6 +3271,7 @@ mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mS q2->TimeoutQuestion = 0; q2->AnonInfo = mDNSNULL; q2->pid = request->process_id; + q2->euid = request->uid; } LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); err = mDNS_StartQuery(&mDNSStorage, q2); @@ -3173,7 +3282,7 @@ mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mS (void) q; (void) request; (void) err; - + return mStatus_NoError; #endif // !UNICAST_DISABLED } @@ -3254,7 +3363,7 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req); if (AddRecord) - flags |= kDNSServiceFlagsAdd; + flags |= kDNSServiceFlagsAdd; if (question->ValidationStatus != 0) { error = kDNSServiceErr_NoError; @@ -3279,7 +3388,7 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu } } } - + rep->rhdr->flags = dnssd_htonl(flags); // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions @@ -3342,15 +3451,16 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu (x.cr_version == XUCRED_VERSION)) { struct sockaddr_storage addr; - const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data; addr.ss_len = 0; if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) { if (answer->rrtype == kDNSType_A) { - struct sockaddr_in *sin = (struct sockaddr_in *)&addr; + struct sockaddr_in *const sin = (struct sockaddr_in *)&addr; sin->sin_port = 0; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer)) + // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: + // sin->sin_addr.s_addr = answer->rdata->u.ipv4.NotAnInteger; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(mDNSv4Addr)), answer)) LogMsg("queryrecord_result_reply: WCF AF_INET putRData failed"); else { @@ -3360,9 +3470,14 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu } else if (answer->rrtype == kDNSType_AAAA) { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + struct sockaddr_in6 *const sin6 = (struct sockaddr_in6 *)&addr; sin6->sin6_port = 0; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer)) + // Instead of this stupid call to putRData it would be much simpler to just assign the value in the sensible way, like this: + // sin6->sin6_addr.__u6_addr.__u6_addr32[0] = answer->rdata->u.ipv6.l[0]; + // sin6->sin6_addr.__u6_addr.__u6_addr32[1] = answer->rdata->u.ipv6.l[1]; + // sin6->sin6_addr.__u6_addr.__u6_addr32[2] = answer->rdata->u.ipv6.l[2]; + // sin6->sin6_addr.__u6_addr.__u6_addr32[3] = answer->rdata->u.ipv6.l[3]; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(mDNSv6Addr)), answer)) LogMsg("queryrecord_result_reply: WCF AF_INET6 putRData failed"); else { @@ -3591,7 +3706,7 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, mDNSlocal void queryrecord_termination_callback(request_state *request) { LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP PID[%d](%s)", - request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), request->process_id, request->pid_name); + request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), request->process_id, request->pid_name); if (request->u.queryrecord.q.QuestionContext) { mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check @@ -3655,38 +3770,11 @@ mDNSlocal void queryrecord_termination_callback(request_state *request) #endif // APPLE_OSX_mDNSResponder } -mDNSlocal void SetQuestionPolicy(DNSQuestion *q, request_state *req) -{ - int i; - - // The policy is either based on pid or UUID. Pass a zero pid - // to the "core" if the UUID is valid. If we always pass the pid, - // then the "core" needs to determine whether the uuid is valid - // by examining all the 16 bytes at the time of the policy - // check and also when setting the delegate socket option. Also, it - // requires that we zero out the uuid wherever the question is - // initialized to make sure that it is not interpreted as valid. - // To prevent these intrusive changes, just pass a zero pid to indicate - // that pid is not valid when uuid is valid. In future if we need the - // pid in the question, we will reevaluate this strategy. - if (req->validUUID) - { - for (i = 0; i < UUID_SIZE; i++) - { - q->uuid[i] = req->uuid[i]; - } - q->pid = 0; - } - else - { - q->pid = req->process_id; - } -} - mDNSlocal mStatus handle_queryrecord_request(request_state *request) { DNSQuestion *const q = &request->u.queryrecord.q; char name[256]; + size_t nameLen; mDNSu16 rrtype, rrclass; mStatus err; @@ -3698,10 +3786,15 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) // interface is not currently in our list. if (interfaceIndex && !InterfaceID) { + if (interfaceIndex > 1) + LogMsg("handle_queryrecord_request: interfaceIndex %d is currently inactive requested by client[%d][%s]", + interfaceIndex, request->process_id, request->pid_name); // If it's one of the specially defined inteface index values, just return an error. - if (PreDefinedInterfaceIndex(interfaceIndex)) + // Also, caller should return an error immediately if lo0 (index 1) is not configured + // into the current active interfaces. See background in Radar 21967160. + if (PreDefinedInterfaceIndex(interfaceIndex) || interfaceIndex == 1) { - LogMsg("ERROR: handle_queryrecord_request: bad interfaceIndex %d", interfaceIndex); + LogInfo("handle_queryrecord_request: bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } @@ -3719,6 +3812,7 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) { LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } request->flags = flags; + request->interfaceIndex = interfaceIndex; mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord)); q->InterfaceID = InterfaceID; @@ -3738,8 +3832,6 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; q->WakeOnResolve = 0; q->UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - q->DenyOnCellInterface = (flags & kDNSServiceFlagsDenyCellular) != 0; - q->DenyOnExpInterface = (flags & kDNSServiceFlagsDenyExpensive) != 0; if ((flags & kDNSServiceFlagsValidate) != 0) q->ValidationRequired = DNSSEC_VALIDATION_SECURE; else if ((flags & kDNSServiceFlagsValidateOptional) != 0) @@ -3757,7 +3849,7 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) //Turn off dnssec validation for local domains and Question Types: RRSIG/ANY(ANY Type is not supported yet) if ((IsLocalDomain(&q->qname)) || (q->qtype == kDNSServiceType_RRSIG) || (q->qtype == kDNSServiceType_ANY)) q->ValidationRequired = 0; - + // Don't append search domains for fully qualified domain names including queries // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should @@ -3767,9 +3859,10 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) // argument "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. - if ((!(q->ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(q->ValidationRequired == DNSSEC_VALIDATION_INSECURE)) - && (rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' && - (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) + nameLen = strlen(name); + if ((!(q->ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(q->ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && (rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && ((nameLen == 0) || (name[nameLen - 1] != '.')) && + (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) { q->AppendSearchDomains = 1; q->AppendLocalSearchDomains = 1; @@ -3788,12 +3881,14 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) q->qnameOrig = mDNSNULL; SetQuestionPolicy(q, request); - LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)", - request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), request->process_id, request->pid_name); + LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)", + request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), request->process_id, request->pid_name); err = mDNS_StartQuery(&mDNSStorage, q); - - if (err) + + if (err) + { LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); + } else { request->terminate = queryrecord_termination_callback; @@ -3801,7 +3896,7 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) if (callExternalHelpers(q->InterfaceID, &q->qname, flags)) { LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()"); - external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, flags); + external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, flags, q); } } @@ -3850,7 +3945,8 @@ mDNSlocal void enum_termination_callback(request_state *request) else { LogInfo("%3d: DNSServiceEnumeration Cancel WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name); - uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY); + uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY | UDNS_WAB_LBROWSE_QUERY); + mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_autoall); } mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default); @@ -3907,6 +4003,9 @@ mDNSlocal mStatus handle_enum_request(request_state *request) if (!request->msgptr) { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } + request->flags = flags; + request->interfaceIndex = interfaceIndex; + // mark which kind of enumeration we're doing so that we know what domain enumeration queries to stop request->u.enumeration.flags = reg; @@ -3914,6 +4013,7 @@ mDNSlocal mStatus handle_enum_request(request_state *request) // necessary context can be reached from the callbacks request->u.enumeration.q_all.QuestionContext = request; request->u.enumeration.q_default.QuestionContext = request; + if (!reg) request->u.enumeration.q_autoall.QuestionContext = request; // if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list. if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly; @@ -3927,7 +4027,16 @@ mDNSlocal mStatus handle_enum_request(request_state *request) { err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request); if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); - else request->terminate = enum_termination_callback; + else if (!reg) + { + err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_autoall, mDNS_DomainTypeBrowseAutomatic, NULL, InterfaceID, enum_result_callback, request); + if (err) + { + mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); + mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default); + } + } + if (!err) request->terminate = enum_termination_callback; } if (!err) { @@ -3937,10 +4046,10 @@ mDNSlocal mStatus handle_enum_request(request_state *request) LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name); uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY); } - else + else { LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name); - uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY); + uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY | UDNS_WAB_LBROWSE_QUERY); } } @@ -4004,7 +4113,7 @@ mDNSlocal mStatus handle_release_request(request_state *request) LogOperation("%3d: PeerConnectionRelease(%X %##s) START PID[%d](%s)", request->sd, flags, instance.c, request->process_id, request->pid_name); - + external_connection_release(&instance); return(err); } @@ -4013,7 +4122,7 @@ mDNSlocal mStatus handle_release_request(request_state *request) mDNSlocal mStatus handle_release_request(request_state *request) { - (void) request; + (void) request; return mStatus_UnsupportedErr; } @@ -4068,6 +4177,10 @@ mDNSlocal void handle_connection_delegate_request(request_state *request) mDNSs32 pid; socklen_t len; + LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)", + request->sd, request->process_id, request->pid_name); + request->terminate = connection_termination; + len = 0; pid = get_uint32(&request->msgptr, request->msgend); #ifdef LOCAL_PEEREPID @@ -4075,12 +4188,15 @@ mDNSlocal void handle_connection_delegate_request(request_state *request) { len = sizeof(pid); if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREPID, &request->process_id, &len) != 0) + { + LogMsg("handle_connection_delegate_request: getsockopt for LOCAL_PEEREPID failed errno:%d / %s", errno, strerror(errno)); return; + } // to extract the process name from the pid value if (proc_pidinfo(request->process_id, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) return; - mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm); - //LogMsg("handle_connection_delegate_request: process id %d, name %s", request->process_id, request->pid_name); + mDNSPlatformStrLCopy(request->pid_name, proc.pbsi_comm, sizeof(request->pid_name)); + debugf("handle_connection_delegate_request: process id %d, name %s", request->process_id, request->pid_name); } #endif #ifdef LOCAL_PEEREUUID @@ -4088,7 +4204,10 @@ mDNSlocal void handle_connection_delegate_request(request_state *request) { len = UUID_SIZE; if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREUUID, request->uuid, &len) != 0) + { + LogMsg("handle_connection_delegate_request: getsockopt for LOCAL_PEEREUUID failed errno:%d / %s", errno, strerror(errno)); return; + } request->validUUID = mDNStrue; } #endif @@ -4114,7 +4233,7 @@ mDNSlocal void handle_getpid_request(request_state *request) const DNSQuestion *q = NULL; PIDInfo pi; - LogOperation("%3d: DNSServiceGetPID START", request->sd); + LogMsg("%3d: DNSServiceGetPID START", request->sd); for (req = all_requests; req; req=req->next) { @@ -4131,7 +4250,7 @@ mDNSlocal void handle_getpid_request(request_state *request) if (port == srcport) { pid = req->process_id; - LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s] question %##s", htons(srcport), pid, req->pid_name, q->qname.c); + LogMsg("DNSServiceGetPID: srcport %d, pid %d [%s] question %##s", htons(srcport), pid, req->pid_name, q->qname.c); break; } } @@ -4150,17 +4269,17 @@ mDNSlocal void handle_getpid_request(request_state *request) #if APPLE_OSX_mDNSResponder pid = getpid(); #endif // APPLE_OSX_mDNSResponder - LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s], question %##s", htons(srcport), pid, "_mDNSResponder", q->qname.c); + LogMsg("DNSServiceGetPID: srcport %d, pid %d [%s], question %##s", htons(srcport), pid, "_mDNSResponder", q->qname.c); break; } } } } - + pi.err = 0; pi.pid = pid; send_all(request->sd, (const char *)&pi, sizeof(PIDInfo)); - LogOperation("%3d: DNSServiceGetPID STOP", request->sd); + LogMsg("%3d: DNSServiceGetPID STOP", request->sd); } // *************************************************************************** @@ -4175,8 +4294,8 @@ mDNSlocal void port_mapping_termination_callback(request_state *request) { LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](%s)", request->sd, DNSServiceProtocol(request->u.pm.NATinfo.Protocol), - mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - request->process_id, request->pid_name); + mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + request->process_id, request->pid_name); mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo); } @@ -4258,6 +4377,8 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr); } + request->flags = flags; + request->interfaceIndex = interfaceIndex; request->u.pm.NATinfo.Protocol = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP; // u.pm.NATinfo.IntPort = already set above request->u.pm.NATinfo.RequestedPort = request->u.pm.ReqExt; @@ -4266,8 +4387,8 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) request->u.pm.NATinfo.clientContext = request; LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](%s)", request->sd, - protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - request->process_id, request->pid_name); + protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, + request->process_id, request->pid_name); err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo); if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err); else request->terminate = port_mapping_termination_callback; @@ -4284,13 +4405,19 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) mDNSlocal void addrinfo_termination_callback(request_state *request) { LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP PID[%d](%s)", request->sd, request->u.addrinfo.q4.qname.c, - request->process_id, request->pid_name); + request->process_id, request->pid_name); if (request->u.addrinfo.q4.QuestionContext) { mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_stop); request->u.addrinfo.q4.QuestionContext = mDNSNULL; + + if (callExternalHelpers(request->u.addrinfo.interface_id, &request->u.addrinfo.q4.qname, request->flags)) + { + LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for kDNSServiceType_A record"); + external_stop_browsing_for_service(request->u.addrinfo.interface_id, &request->u.addrinfo.q4.qname, kDNSServiceType_A, request->flags); + } } if (request->u.addrinfo.q4.qnameOrig) { @@ -4320,6 +4447,12 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_stop); request->u.addrinfo.q6.QuestionContext = mDNSNULL; + + if (callExternalHelpers(request->u.addrinfo.interface_id, &request->u.addrinfo.q6.qname, request->flags)) + { + LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for kDNSServiceType_AAAA record"); + external_stop_browsing_for_service(request->u.addrinfo.interface_id, &request->u.addrinfo.q6.qname, kDNSServiceType_AAAA, request->flags); + } } if (request->u.addrinfo.q6.qnameOrig) { @@ -4378,12 +4511,14 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) mDNSlocal mStatus handle_addrinfo_request(request_state *request) { char hostname[256]; + size_t hostnameLen; domainname d; mStatus err = 0; mDNSs32 serviceIndex = -1; // default unscoped value for ServiceID is -1 - + mDNSInterfaceID InterfaceID; + DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); - + mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); if (flags & kDNSServiceFlagsServiceIndex) @@ -4395,27 +4530,33 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) serviceIndex = interfaceIndex; interfaceIndex = 0; } - + mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo)); - mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); + InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex); // The request is scoped to a specific interface index, but the // interface is not currently in our list. if (interfaceIndex && !InterfaceID) { + if (interfaceIndex > 1) + LogMsg("handle_addrinfo_request: interfaceIndex %d is currently inactive requested by client[%d][%s]", + interfaceIndex, request->process_id, request->pid_name); // If it's one of the specially defined inteface index values, just return an error. if (PreDefinedInterfaceIndex(interfaceIndex)) { - LogMsg("ERROR: handle_addrinfo_request: bad interfaceIndex %d", interfaceIndex); + LogInfo("handle_addrinfo_request: bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); } - // Otherwise, use the specified interface index value and the registration will + // Otherwise, use the specified interface index value and the request will // be applied to that interface when it comes up. InterfaceID = (mDNSInterfaceID)(uintptr_t)interfaceIndex; LogInfo("handle_addrinfo_request: query pending for interface index %d", interfaceIndex); } + + request->flags = flags; + request->interfaceIndex = interfaceIndex; request->u.addrinfo.interface_id = InterfaceID; request->u.addrinfo.flags = flags; request->u.addrinfo.protocol = get_uint32(&request->msgptr, request->msgend); @@ -4453,8 +4594,6 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0; request->u.addrinfo.q4.UseBackgroundTrafficClass = request->u.addrinfo.q6.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - request->u.addrinfo.q4.DenyOnCellInterface = request->u.addrinfo.q6.DenyOnCellInterface = (flags & kDNSServiceFlagsDenyCellular) != 0; - request->u.addrinfo.q4.DenyOnExpInterface = request->u.addrinfo.q6.DenyOnExpInterface = (flags & kDNSServiceFlagsDenyExpensive) != 0; if ((flags & kDNSServiceFlagsValidate) != 0) request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE; else if ((flags & kDNSServiceFlagsValidateOptional) != 0) @@ -4474,13 +4613,18 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) if (IsLocalDomain(&d)) request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 0; + hostnameLen = strlen(hostname); + + LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", + request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c, request->process_id, request->pid_name); + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) { request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA; request->u.addrinfo.q6.SearchListIndex = 0; // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set - if ((!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) - && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) + if ((!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && ((hostnameLen == 0) || (hostname[hostnameLen - 1] != '.')) && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) { request->u.addrinfo.q6.AppendSearchDomains = 1; request->u.addrinfo.q6.AppendLocalSearchDomains = 1; @@ -4503,9 +4647,14 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err); #endif // APPLE_OSX_mDNSResponder if (!err) - { + { request->terminate = addrinfo_termination_callback; LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_start); + if (callExternalHelpers(InterfaceID, &d, flags)) + { + LogInfo("handle_addrinfo_request: calling external_start_browsing_for_service() for kDNSServiceType_AAAA record"); + external_start_browsing_for_service(InterfaceID, &d, kDNSServiceType_AAAA, flags, &request->u.addrinfo.q6); + } } } @@ -4518,8 +4667,8 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) // "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. - if ((!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) - && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) + if ((!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && ((hostnameLen == 0) || (hostname[hostnameLen - 1] != '.')) && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) { request->u.addrinfo.q4.AppendSearchDomains = 1; request->u.addrinfo.q4.AppendLocalSearchDomains = 1; @@ -4538,11 +4687,17 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); request->u.addrinfo.q4.QuestionContext = mDNSNULL; if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) - { - // If we started a query for IPv6, we need to cancel it + { + // If we started a query for IPv6, we need to cancel it mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); request->u.addrinfo.q6.QuestionContext = mDNSNULL; - } + + if (callExternalHelpers(InterfaceID, &d, flags)) + { + LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for kDNSServiceType_AAAA record"); + external_stop_browsing_for_service(InterfaceID, &d, kDNSServiceType_AAAA, flags); + } + } } #if APPLE_OSX_mDNSResponder err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err); @@ -4551,11 +4706,14 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) { request->terminate = addrinfo_termination_callback; LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_start); - } + if (callExternalHelpers(InterfaceID, &d, flags)) + { + LogInfo("handle_addrinfo_request: calling external_start_browsing_for_service() for kDNSServiceType_A record"); + external_start_browsing_for_service(InterfaceID, &d, kDNSServiceType_A, flags, &request->u.addrinfo.q4); + } + } } - LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, - request->u.addrinfo.protocol, d.c, request->process_id, request->pid_name); return(err); } @@ -4568,10 +4726,10 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) mDNSlocal request_state *NewRequest(void) { request_state **p = &all_requests; - while (*p) + while (*p) p=&(*p)->next; *p = mallocL("request_state", sizeof(request_state)); - if (!*p) + if (!*p) FatalError("ERROR: malloc"); mDNSPlatformMemZero(*p, sizeof(request_state)); return(*p); @@ -4660,12 +4818,10 @@ mDNSlocal void read_msg(request_state *req) #if !defined(_WIN32) cmsg = CMSG_FIRSTHDR(&msg); #if DEBUG_64BIT_SCM_RIGHTS - LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); - LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); + LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); + LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg ? cmsg->cmsg_len : -1, cmsg ? cmsg->cmsg_level : -1, cmsg ? cmsg->cmsg_type : -1); #endif // DEBUG_64BIT_SCM_RIGHTS - if (msg.msg_controllen != 0 && - cmsg->cmsg_level == SOL_SOCKET && - cmsg->cmsg_type == SCM_RIGHTS) + if (cmsg && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { #if APPLE_OSX_mDNSResponder // Strictly speaking BPF_fd belongs solely in the platform support layer, but because @@ -4685,7 +4841,7 @@ mDNSlocal void read_msg(request_state *req) #endif // DEBUG_64BIT_SCM_RIGHTS if (req->data_bytes < req->hdr.datalen) { - LogMsg("%3d: Client(PID [%d](%s)) sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", + LogMsg("%3d: Client(PID [%d](%s)) sent result code socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", req->sd, req->process_id, req->pid_name, req->errsd, req->data_bytes, req->hdr.datalen); req->ts = t_error; return; @@ -4714,7 +4870,7 @@ mDNSlocal void read_msg(request_state *req) get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH); // path is first element in message buffer mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr)); cliaddr.sun_family = AF_LOCAL; - mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path); + mDNSPlatformStrLCopy(cliaddr.sun_path, ctrl_path, sizeof(cliaddr.sun_path)); // If the error return path UDS name is empty string, that tells us // that this is a new version of the library that's going to pass us // the error return path socket via sendmsg/recvmsg @@ -4727,8 +4883,8 @@ mDNSlocal void read_msg(request_state *req) #endif req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0); - if (!dnssd_SocketValid(req->errsd)) - { + if (!dnssd_SocketValid(req->errsd)) + { my_throttled_perror("ERROR: socket"); req->ts = t_error; return; @@ -4752,7 +4908,7 @@ mDNSlocal void read_msg(request_state *req) #if !defined(USE_TCP_LOOPBACK) got_errfd: #endif - LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]); + LogOperation("%3d: Result code socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]); #if defined(_WIN32) if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0) #else @@ -4794,23 +4950,16 @@ mDNSlocal void request_callback(int fd, short filter, void *info) for (;;) { read_msg(req); - if (req->ts == t_morecoming) + if (req->ts == t_morecoming) + return; + if (req->ts == t_terminated || req->ts == t_error) + { + AbortUnlinkAndFree(req); return; - if (req->ts == t_terminated || req->ts == t_error) - { - AbortUnlinkAndFree(req); - return; - } - if (req->ts != t_complete) - { - LogMsg("request_callback: req->ts %d != t_complete PID[%d][%s]", req->ts, req->process_id, req->pid_name); - AbortUnlinkAndFree(req); - return; } - if (req->hdr.version != VERSION) + if (req->ts != t_complete) { - LogMsg("request_callback: ERROR: client IPC version %d incompatible with daemon IPC version %d PID[%d][%s]", - req->hdr.version, VERSION, req->process_id, req->pid_name); + LogMsg("request_callback: req->ts %d != t_complete PID[%d][%s]", req->ts, req->process_id, req->pid_name); AbortUnlinkAndFree(req); return; } @@ -4837,29 +4986,26 @@ mDNSlocal void request_callback(int fd, short filter, void *info) case send_bpf: // Same as cancel_request below case cancel_request: min_size = 0; break; case release_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; - default: LogMsg("request_callback: ERROR: validate_message - unsupported req type: %d PID[%d][%s]", - req->hdr.op, req->process_id, req->pid_name); + default: LogMsg("request_callback: ERROR: validate_message - unsupported req type: %d PID[%d][%s]", + req->hdr.op, req->process_id, req->pid_name); min_size = -1; break; } if ((mDNSs32)req->data_bytes < min_size) - { - LogMsg("request_callback: Invalid message %d bytes; min for %d is %d PID[%d][%s]", - req->data_bytes, req->hdr.op, min_size, req->process_id, req->pid_name); - AbortUnlinkAndFree(req); - return; + { + LogMsg("request_callback: Invalid message %d bytes; min for %d is %d PID[%d][%s]", + req->data_bytes, req->hdr.op, min_size, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; } if (LightweightOp(req->hdr.op) && !req->terminate) - { - LogMsg("request_callback: Reg/Add/Update/Remove %d require existing connection PID[%d][%s]", - req->hdr.op, req->process_id, req->pid_name); - AbortUnlinkAndFree(req); - return; + { + LogMsg("request_callback: Reg/Add/Update/Remove %d require existing connection PID[%d][%s]", + req->hdr.op, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; } - // check if client wants silent operation - if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1; - // If req->terminate is already set, this means this operation is sharing an existing connection if (req->terminate && !LightweightOp(req->hdr.op)) { @@ -4888,6 +5034,7 @@ mDNSlocal void request_callback(int fd, short filter, void *info) if (req->process_id) { newreq->process_id = req->process_id; + mDNSPlatformStrLCopy(newreq->pid_name, req->pid_name, (mDNSu32)sizeof(newreq->pid_name)); } else { @@ -4897,6 +5044,9 @@ mDNSlocal void request_callback(int fd, short filter, void *info) req = newreq; } + // Check if the request wants no asynchronous replies. + if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1; + // If we're shutting down, don't allow new client requests // We do allow "cancel" and "getproperty" during shutdown if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request) @@ -4904,7 +5054,7 @@ mDNSlocal void request_callback(int fd, short filter, void *info) err = mStatus_ServiceNotRunning; } else - { + { switch(req->hdr.op) { // These are all operations that have their own first-class request_state object @@ -4913,12 +5063,7 @@ mDNSlocal void request_callback(int fd, short filter, void *info) req->sd, req->process_id, req->pid_name); req->terminate = connection_termination; break; - case connection_delegate_request: - LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)", - req->sd, req->process_id, req->pid_name); - req->terminate = connection_termination; - handle_connection_delegate_request(req); - break; + case connection_delegate_request: handle_connection_delegate_request(req); break; case resolve_request: err = handle_resolve_request (req); break; case query_request: err = handle_queryrecord_request (req); break; case browse_request: err = handle_browse_request (req); break; @@ -4939,7 +5084,7 @@ mDNSlocal void request_callback(int fd, short filter, void *info) case remove_record_request: err = handle_removerecord_request(req); break; case cancel_request: handle_cancel_request (req); break; case release_request: err = handle_release_request (req); break; - default: LogMsg("request_callback: %3d:ERROR: Unsupported UDS req:%d PID[%d][%s]", + default: LogMsg("request_callback: %3d:ERROR: Unsupported UDS req:%d PID[%d][%s]", req->sd, req->hdr.op, req->process_id, req->pid_name); break; } } @@ -4954,7 +5099,7 @@ mDNSlocal void request_callback(int fd, short filter, void *info) send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder)); if (req->errsd != req->sd) { - LogOperation("%3d: Error socket %d closed %08X %08X (%d)", + LogOperation("%3d: Result code socket %d closed %08X %08X (%d)", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err); dnssd_close(req->errsd); req->errsd = req->sd; @@ -4988,7 +5133,7 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) if (!dnssd_SocketValid(sd)) { - if (dnssd_errno != dnssd_EWOULDBLOCK) + if (dnssd_errno != dnssd_EWOULDBLOCK) my_throttled_perror("ERROR: accept"); return; } @@ -5019,11 +5164,14 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) #if APPLE_OSX_mDNSResponder struct xucred x; socklen_t xucredlen = sizeof(x); - if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid; - else my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); + if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) + request->uid = x.cr_uid; // save the effective userid of the client + else + my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); + debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups); #endif // APPLE_OSX_mDNSResponder - LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid); + LogOperation("%3d: connect_callback: Adding FD for uid %u", request->sd, request->uid); udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data); } } @@ -5063,8 +5211,8 @@ mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt) return mDNSfalse; } else - { - LogMsg("%3d: Listening for incoming Unix Domain Socket client requests", skt); + { + LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt); mDNSStorage.uds_listener_skt = skt; } return mDNStrue; @@ -5121,19 +5269,19 @@ mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) #else { mode_t mask = umask(0); - unlink(MDNS_UDS_SERVERPATH); // OK if this fails + unlink(boundPath); // OK if this fails laddr.sun_family = AF_LOCAL; #ifndef NOT_HAVE_SA_LEN // According to Stevens (section 3.2), there is no portable way to // determine whether sa_len is defined on a particular platform. laddr.sun_len = sizeof(struct sockaddr_un); #endif - if (strlen(MDNS_UDS_SERVERPATH) >= sizeof(laddr.sun_path)) + if (strlen(boundPath) >= sizeof(laddr.sun_path)) { LogMsg("ERROR: MDNS_UDS_SERVERPATH must be < %d characters", (int)sizeof(laddr.sun_path)); goto error; } - mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH); + mDNSPlatformStrLCopy(laddr.sun_path, boundPath, sizeof(laddr.sun_path)); ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); umask(mask); if (ret < 0) @@ -5213,7 +5361,7 @@ mDNSexport int udsserver_exit(void) // Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody" // to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket. // It would be nice if we could find a solution to this problem - if (unlink(MDNS_UDS_SERVERPATH)) + if (unlink(boundPath)) debugf("Unable to remove %s", MDNS_UDS_SERVERPATH); #endif } @@ -5226,9 +5374,9 @@ mDNSexport int udsserver_exit(void) mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) { char prefix[16]; - if (req->primary) + if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> "); - else + else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); if (!req->terminate) @@ -5240,12 +5388,12 @@ mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) request_state *r; for (p = req->u.reg_recs; p; p=p->next) num_records++; for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; - LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", - prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", + LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", + prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", req->process_id, req->pid_name); for (p = req->u.reg_recs; p; p=p->next) - LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s PID[%d](%s)", p->key, ARDisplayString(m, p->rr), - req->process_id, req->pid_name); + LogMsgNoIdent(" -> DNSServiceRegisterRecord 0x%08X %2d %3d %s PID[%d](%s)", + req->flags, req->interfaceIndex, p->key, ARDisplayString(m, p->rr), req->process_id, req->pid_name); for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r); } else if (req->terminate == regservice_termination_callback) @@ -5253,32 +5401,34 @@ mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) service_instance *ptr; char anonstr[256]; for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) - LogMsgNoIdent("%s DNSServiceRegister %##s%s %u/%u PID[%d](%s)", - (ptr == req->u.servicereg.instances) ? prefix : " ", ptr->srs.RR_SRV.resrec.name->c, - AnonDataToString(ptr->srs.AnonData, 0, anonstr, sizeof(anonstr)), mDNSVal16(req->u.servicereg.port), - SRS_PORT(&ptr->srs), req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceRegister 0x%08X %2d %##s%s %u/%u PID[%d](%s)", + (ptr == req->u.servicereg.instances) ? prefix : " ", req->flags, req->interfaceIndex, ptr->srs.RR_SRV.resrec.name->c, + AnonDataToString(ptr->srs.AnonData, 0, anonstr, sizeof(anonstr)), mDNSVal16(req->u.servicereg.port), + SRS_PORT(&ptr->srs), req->process_id, req->pid_name); } else if (req->terminate == browse_termination_callback) { browser_t *blist; char anonstr[256]; for (blist = req->u.browser.browsers; blist; blist = blist->next) - LogMsgNoIdent("%s DNSServiceBrowse %##s%s PID[%d](%s)", - (blist == req->u.browser.browsers) ? prefix : " ",blist->q.qname.c, - AnonDataToString(req->u.browser.AnonData, 0, anonstr, sizeof(anonstr)), req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceBrowse 0x%08X %2d %##s%s PID[%d](%s)", + (blist == req->u.browser.browsers) ? prefix : " ", req->flags, req->interfaceIndex, blist->q.qname.c, + AnonDataToString(req->u.browser.AnonData, 0, anonstr, sizeof(anonstr)), req->process_id, req->pid_name); } else if (req->terminate == resolve_termination_callback) - LogMsgNoIdent("%s DNSServiceResolve %##s PID[%d](%s)", - prefix, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceResolve 0x%08X %2d %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name); else if (req->terminate == queryrecord_termination_callback) - LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s) PID[%d](%s)", - prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceQueryRecord 0x%08X %2d %##s (%s) PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name); else if (req->terminate == enum_termination_callback) - LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s PID[%d](%s)", prefix, req->u.enumeration.q_all.qname.c, - req->process_id, req->pid_name); + LogMsgNoIdent("%s DNSServiceEnumerateDomains 0x%08X %2d %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.enumeration.q_all.qname.c, req->process_id, req->pid_name); else if (req->terminate == port_mapping_termination_callback) - LogMsgNoIdent("%s DNSServiceNATPortMapping %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)", + LogMsgNoIdent("%s DNSServiceNATPortMapping 0x%08X %2d %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)", prefix, + req->flags, + req->interfaceIndex, req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", mDNSVal16(req->u.pm.NATinfo.IntPort), @@ -5287,9 +5437,10 @@ mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) mDNSVal16(req->u.pm.NATinfo.ExternalPort), req->u.pm.NATinfo.NATLease, req->u.pm.NATinfo.Lifetime, - req->process_id, req->pid_name); + req->process_id, req->pid_name); else if (req->terminate == addrinfo_termination_callback) - LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)", prefix, + LogMsgNoIdent("%s DNSServiceGetAddrInfo 0x%08X %2d %s%s %##s PID[%d](%s)", + prefix, req->flags, req->interfaceIndex, req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name); @@ -5304,18 +5455,18 @@ mDNSlocal void GetMcastClients(request_state *req) int num_records = 0, num_ops = 0; const registered_record_entry *p; request_state *r; - for (p = req->u.reg_recs; p; p=p->next) + for (p = req->u.reg_recs; p; p=p->next) num_records++; - for (r = req->next; r; r=r->next) - if (r->primary == req) + for (r = req->next; r; r=r->next) + if (r->primary == req) num_ops++; for (p = req->u.reg_recs; p; p=p->next) { if (!AuthRecord_uDNS(p->rr)) n_mrecords++; } - for (r = req->next; r; r=r->next) - if (r->primary == req) + for (r = req->next; r; r=r->next) + if (r->primary == req) GetMcastClients(r); } else if (req->terminate == regservice_termination_callback) @@ -5324,7 +5475,7 @@ mDNSlocal void GetMcastClients(request_state *req) for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) { if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) - n_mrecords++; + n_mrecords++; } } else if (req->terminate == browse_termination_callback) @@ -5367,19 +5518,19 @@ mDNSlocal void LogMcastClientInfo(request_state *req) int num_records = 0, num_ops = 0; const registered_record_entry *p; request_state *r; - for (p = req->u.reg_recs; p; p=p->next) + for (p = req->u.reg_recs; p; p=p->next) num_records++; - for (r = req->next; r; r=r->next) - if (r->primary == req) + for (r = req->next; r; r=r->next) + if (r->primary == req) num_ops++; for (p = req->u.reg_recs; p; p=p->next) { if (!AuthRecord_uDNS(p->rr)) - LogMcastNoIdent("R: -> DNSServiceRegisterRecord: %##s %s PID[%d](%s)", p->rr->resrec.name->c, + LogMcastNoIdent("R: -> DNSServiceRegisterRecord: %##s %s PID[%d](%s)", p->rr->resrec.name->c, DNSTypeName(p->rr->resrec.rrtype), req->process_id, req->pid_name, i_mcount++); } - for (r = req->next; r; r=r->next) - if (r->primary == req) + for (r = req->next; r; r=r->next) + if (r->primary == req) LogMcastClientInfo(r); } else if (req->terminate == regservice_termination_callback) @@ -5387,8 +5538,8 @@ mDNSlocal void LogMcastClientInfo(request_state *req) service_instance *ptr; for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) { - if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) - LogMcastNoIdent("R: DNSServiceRegister: %##s %u/%u PID[%d](%s)", ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), + if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) + LogMcastNoIdent("R: DNSServiceRegister: %##s %u/%u PID[%d](%s)", ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs), req->process_id, req->pid_name, i_mcount++); } } @@ -5411,7 +5562,7 @@ mDNSlocal void LogMcastClientInfo(request_state *req) else if (req->terminate == queryrecord_termination_callback) { if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0)) - LogMcastNoIdent("Q: DNSServiceQueryRecord %##s %s PID[%d](%s)", req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), + LogMcastNoIdent("Q: DNSServiceQueryRecord %##s %s PID[%d](%s)", req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name, i_mcount++); } else if (req->terminate == addrinfo_termination_callback) @@ -5426,7 +5577,7 @@ mDNSlocal void LogMcastClientInfo(request_state *req) { return; } - + } mDNSlocal char *RecordTypeName(mDNSu8 rtype) @@ -5499,9 +5650,14 @@ mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m) // Print a maximum of 400 records if (ar->ARType == AuthRecordLocalOnly) - LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); else if (ar->ARType == AuthRecordP2P) - LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + { + if (ar->resrec.InterfaceID == mDNSInterface_BLE) + LogMsgNoIdent(" %s BLE %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + else + LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); + } } } @@ -5523,19 +5679,24 @@ mDNSlocal void LogOneAuthRecord(mDNS *const m, const AuthRecord *ar, mDNSs32 now char anstr[256]; if (AuthRecord_uDNS(ar)) { - LogMsgNoIdent("%7d %7d %7d %7d %s", + LogMsgNoIdent("%7d %7d %7d %-7s %4d %s %s", ar->ThisAPInterval / mDNSPlatformOneSecond, (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, - ar->state, ARDisplayString(m, ar)); + "-U-", + ar->state, + ar->AllowRemoteQuery ? "☠" : " ", + ARDisplayString(m, ar)); } else { - LogMsgNoIdent("%7d %7d %7d %7s %s%s", + LogMsgNoIdent("%7d %7d %7d %-7s 0x%02X %s %s%s", ar->ThisAPInterval / mDNSPlatformOneSecond, ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, ifname ? ifname : "ALL", + ar->resrec.RecordType, + ar->AllowRemoteQuery ? "☠" : " ", ARDisplayString(m, ar), AnonInfoToString(ar->resrec.AnonInfo, anstr, sizeof(anstr))); } } @@ -5550,7 +5711,7 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID); if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL)) { - if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire State"); } + if (showheader) { showheader = mDNSfalse; LogMsgNoIdent(" Int Next Expire if State"); } if (proxy) (*proxy)++; if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner))) { @@ -5572,7 +5733,10 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso } else if (ar->ARType == AuthRecordP2P) { - LogMsgNoIdent(" PP %s", ARDisplayString(m, ar)); + if (ar->resrec.InterfaceID == mDNSInterface_BLE) + LogMsgNoIdent(" BLE %s", ARDisplayString(m, ar)); + else + LogMsgNoIdent(" PP %s", ARDisplayString(m, ar)); } else { @@ -5678,7 +5842,7 @@ mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int a mDNSexport void LogMDNSStatistics(mDNS *const m) { LogMsgNoIdent("--- MDNS Statistics ---"); - + LogMsgNoIdent("Name Conflicts %u", m->mDNSStats.NameConflicts); LogMsgNoIdent("KnownUnique Name Conflicts %u", m->mDNSStats.KnownUniqueNameConflicts); LogMsgNoIdent("Duplicate Query Suppressions %u", m->mDNSStats.DupQuerySuppressions); @@ -5796,10 +5960,11 @@ mDNSexport void udsserver_info(mDNS *const m) LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); } - LogMsgNoIdent("----- Local-Only Questions -----"); + LogMsgNoIdent("----- LocalOnly, P2P Questions -----"); if (!m->LocalOnlyQuestions) LogMsgNoIdent(""); else for (q = m->LocalOnlyQuestions; q; q=q->next) - LogMsgNoIdent(" %5d %-6s%##s%s", + LogMsgNoIdent(" %3s %5d %-6s%##s%s", + q->InterfaceID == mDNSInterface_LocalOnly ? "LO ": q->InterfaceID == mDNSInterface_BLE ? "BLE": "P2P", q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); LogMsgNoIdent("---- Active UDS Client Requests ----"); @@ -5819,7 +5984,7 @@ mDNSexport void udsserver_info(mDNS *const m) foundparent:; } } - + LogMsgNoIdent("-------- NAT Traversals --------"); LogMsgNoIdent("ExtAddress %.4a Retry %d Interval %d", &m->ExtAddress, @@ -5912,7 +6077,7 @@ foundparent:; } } LogInfo("--- Trust Anchors ---"); - if (!m->TrustAnchors) + if (!m->TrustAnchors) { LogInfo(""); } @@ -5964,6 +6129,14 @@ foundparent:; LogMsgNoIdent("---- Task Scheduling Timers ----"); +#if BONJOUR_ON_DEMAND + LogMsgNoIdent("BonjourEnabled %d", m->BonjourEnabled); +#endif // BONJOUR_ON_DEMAND + +#if APPLE_OSX_mDNSResponder + LogMsgNoIdent("EnableBLEBasedDiscovery %d", EnableBLEBasedDiscovery); +#endif // APPLE_OSX_mDNSResponder + if (!m->NewQuestions) LogMsgNoIdent("NewQuestion "); else @@ -5988,11 +6161,8 @@ foundparent:; LogMsgNoIdent("m->WABBrowseQueriesCount %d", m->WABBrowseQueriesCount); LogMsgNoIdent("m->WABLBrowseQueriesCount %d", m->WABLBrowseQueriesCount); LogMsgNoIdent("m->WABRegQueriesCount %d", m->WABRegQueriesCount); - LogMsgNoIdent("m->mDNSOppCaching %d", m->mDNSOppCaching); LogMsgNoIdent("m->AutoTargetServices %d", m->AutoTargetServices); -#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) - LogMsgNoIdent(" ABS (hex) ABS (dec) REL (hex) REL (dec)"); LogMsgNoIdent("m->timenow %08X %11d", now, now); LogMsgNoIdent("m->timenow_adjust %08X %11d", m->timenow_adjust, m->timenow_adjust); @@ -6008,6 +6178,11 @@ foundparent:; LogTimer("m->NextCacheCheck ", m->NextCacheCheck); LogTimer("m->NextScheduledSPS ", m->NextScheduledSPS); LogTimer("m->NextScheduledKA ", m->NextScheduledKA); + +#if BONJOUR_ON_DEMAND + LogTimer("m->NextBonjourDisableTime ", m->NextBonjourDisableTime); +#endif // BONJOUR_ON_DEMAND + LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry); LogTimer("m->DelaySleep ", m->DelaySleep); @@ -6097,7 +6272,6 @@ mDNSlocal int send_msg(request_state *const req) { reply_state *const rep = req->replies; // Send the first waiting reply ssize_t nwriten; - if (req->no_reply) return(t_complete); ConvertHeaderBytes(rep->mhdr); nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0); @@ -6137,15 +6311,16 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0) { r->u.resolve.ReportTime = 0; - LogMsgNoIdent("Client application bug PID[%d](%s) : DNSServiceResolve(%##s) active for over two minutes. " - "This places considerable burden on the network.", r->process_id, r->pid_name, r->u.resolve.qsrv.qname.c); + // if client received results and resolve still active + if (r->u.resolve.txt && r->u.resolve.srv) + LogMsgNoIdent("Client application PID[%d](%s) has received results for DNSServiceResolve(%##s) yet remains active over two minutes.", r->process_id, r->pid_name, r->u.resolve.qsrv.qname.c); } // Note: Only primary req's have reply lists, not subordinate req's. while (r->replies) // Send queued replies { transfer_state result; - if (r->replies->next) + if (r->replies->next) r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing); result = send_msg(r); // Returns t_morecoming if buffer full because client is not reading if (result == t_complete) @@ -6157,9 +6332,14 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) r->unresponsiveness_reports = 0; continue; } - else if (result == t_terminated || result == t_error) + else if (result == t_terminated) { - LogMsg("%3d: Could not write data to clientPID[%d](%s) because of error - aborting connection", r->sd, r->process_id, r->pid_name); + LogInfo("%3d: Could not write data to client PID[%d](%s) because connection is terminated by the client", r->sd, r->process_id, r->pid_name); + abort_request(r); + } + else if (result == t_error) + { + LogMsg("%3d: Could not write data to client PID[%d](%s) because of error - aborting connection", r->sd, r->process_id, r->pid_name); LogClientInfo(&mDNSStorage, r); abort_request(r); } @@ -6168,21 +6348,21 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) if (r->replies) // If we failed to send everything, check our time_blocked timer { - if (nextevent - now > mDNSPlatformOneSecond) + if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; - if (mDNSStorage.SleepState != SleepState_Awake) + if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0; - else if (!r->time_blocked) + else if (!r->time_blocked) r->time_blocked = NonZeroTime(now); else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1)) { int num = 0; struct reply_state *x = r->replies; - while (x) - { - num++; - x=x->next; + while (x) + { + num++; + x=x->next; } LogMsg("%3d: Could not write data to client PID[%d](%s) after %ld seconds, %d repl%s waiting", r->sd, r->process_id, r->pid_name, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies"); @@ -6212,10 +6392,10 @@ struct CompileTimeAssertionChecks_uds_daemon // 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. - char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 1 : -1]; + char sizecheck_request_state [(sizeof(request_state) <= 2954) ? 1 : -1]; char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <= 60) ? 1 : -1]; char sizecheck_service_instance [(sizeof(service_instance) <= 6552) ? 1 : -1]; - char sizecheck_browser_t [(sizeof(browser_t) <= 1096) ? 1 : -1]; + char sizecheck_browser_t [(sizeof(browser_t) <= 1150) ? 1 : -1]; char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1]; char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1]; }; diff --git a/mDNSShared/uds_daemon.h b/mDNSShared/uds_daemon.h index ca36117..ef98450 100644 --- a/mDNSShared/uds_daemon.h +++ b/mDNSShared/uds_daemon.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 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. @@ -13,13 +13,6 @@ * 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. - - File: uds_daemon.h - - Contains: Interfaces necessary to talk to uds_daemon.c. - - Version: 1.0 - */ #include "mDNSEmbeddedAPI.h" @@ -29,6 +22,8 @@ #define SRS_PORT(S) mDNSVal16((S)->RR_SRV.resrec.rdata->u.srv.port) +#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) + extern int udsserver_init(dnssd_sock_t skts[], mDNSu32 count); extern mDNSs32 udsserver_idle(mDNSs32 nextevent); extern void udsserver_info(mDNS *const m); // print out info about current state @@ -58,15 +53,14 @@ extern DNameListElem *AutoBrowseDomains; extern mDNSs32 ChopSubTypes(char *regtype, char **AnonData); extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData); extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port); +extern mDNSBool callExternalHelpers(mDNSInterfaceID InterfaceID, const domainname *const domain, DNSServiceFlags flags); extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result); extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs); #if APPLE_OSX_mDNSResponder -extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add); -extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add); // D2D interface support -extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); +extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags, DNSQuestion * q); extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); extern void external_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); @@ -74,12 +68,17 @@ extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags); extern void external_connection_release(const domainname *instance); +extern void internal_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); +extern void internal_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); +extern void internal_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); +extern void internal_stop_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); + #else // APPLE_OSX_mDNSResponder -#define external_start_browsing_for_service(A,B,C,D) (void)(A) +#define external_start_browsing_for_service(A,B,C,D,E) (void)(A) #define external_stop_browsing_for_service(A,B,C,D) (void)(A) #define external_start_advertising_service(A,B) (void)(A) -#define external_stop_advertising_service(A,B) (void)(A) +#define external_stop_advertising_service(A,B) do { (void)(A); (void)(B); } while (0) #define external_start_resolving_service(A,B,C) (void)(A) #define external_stop_resolving_service(A,B,C) (void)(A) #define external_connection_release(A) (void)(A) @@ -88,3 +87,8 @@ extern void external_connection_release(const domainname *instance); extern const char mDNSResponderVersionString_SCCS[]; #define mDNSResponderVersionString (mDNSResponderVersionString_SCCS+5) + +#if DEBUG +extern void SetDebugBoundPath(void); +extern int IsDebugSocketInUse(void); +#endif diff --git a/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp b/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp index 5fae955..34c418e 100755 --- a/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp +++ b/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp @@ -227,8 +227,8 @@ CConfigPropertySheet::DecodeDomainName( const char * raw, CString & decoded ) for (i = 0; i < labels; i++) { buffer = (char *)GetNextLabel(buffer, nextLabel); - strcat(decodedDomainString, nextLabel); - strcat(decodedDomainString, "."); + strcat_s(decodedDomainString, sizeof(decodedDomainString), nextLabel); + strcat_s(decodedDomainString, sizeof(decodedDomainString), "."); } // Remove trailing dot from domain name. diff --git a/mDNSWindows/ControlPanel/ControlPanelExe.cpp b/mDNSWindows/ControlPanel/ControlPanelExe.cpp index 36447d1..94de794 100755 --- a/mDNSWindows/ControlPanel/ControlPanelExe.cpp +++ b/mDNSWindows/ControlPanel/ControlPanelExe.cpp @@ -23,6 +23,7 @@ #include #include "loclibrary.h" +#include #ifdef _DEBUG @@ -205,7 +206,7 @@ CCPApp::Register( LPCTSTR inClsidString, LPCTSTR inName, LPCTSTR inCanonicalName n = sizeof_array( entries ); for( i = 0; i < n; ++i ) { - wsprintf( keyName, entries[ i ].subKey, inClsidString ); + StringCbPrintf( keyName, sizeof( keyName ), entries[ i ].subKey, inClsidString ); err = RegCreateKeyEx( entries[ i ].rootKey, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL ); require_noerr( err, exit ); @@ -232,10 +233,10 @@ CCPApp::Unregister( LPCTSTR clsidString ) { TCHAR keyName[ MAX_PATH * 2 ]; - wsprintf( keyName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\NameSpace\\%s", clsidString ); + StringCbPrintf( keyName, sizeof( keyName ), L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\NameSpace\\%s", clsidString ); MyRegDeleteKey( HKEY_LOCAL_MACHINE, keyName ); - wsprintf( keyName, L"CLSID\\%s", clsidString ); + StringCbPrintf( keyName, sizeof( keyName ), L"CLSID\\%s", clsidString ); MyRegDeleteKey( HKEY_CLASSES_ROOT, keyName ); } @@ -313,7 +314,7 @@ CCPApp::InitInstance() require_noerr( err, exit ); - wsprintf( iconPath, L"%s,-%d", exePath, IDR_APPLET ); + StringCbPrintf( iconPath, sizeof( iconPath ), L"%s,-%d", exePath, IDR_APPLET ); localizedName.LoadString( IDS_APPLET_NAME ); toolTip.LoadString( IDS_APPLET_TOOLTIP ); diff --git a/mDNSWindows/ControlPanel/FourthPage.cpp b/mDNSWindows/ControlPanel/FourthPage.cpp index b6c8d15..7ded08f 100755 --- a/mDNSWindows/ControlPanel/FourthPage.cpp +++ b/mDNSWindows/ControlPanel/FourthPage.cpp @@ -183,7 +183,7 @@ void CFourthPage::OnBnClickedPowerManagement() - sprintf( buf, "check box: %d", m_checkBox.GetCheck() ); + snprintf( buf, sizeof( buf ), "check box: %d", m_checkBox.GetCheck() ); OutputDebugStringA( buf ); diff --git a/mDNSWindows/DLLStub/DLLStub.cpp b/mDNSWindows/DLLStub/DLLStub.cpp index 503465f..38fd5e0 100755 --- a/mDNSWindows/DLLStub/DLLStub.cpp +++ b/mDNSWindows/DLLStub/DLLStub.cpp @@ -80,12 +80,12 @@ DLLStub::GetProcAddress( FARPROC * func, LPCSTR lpProcName ) } -int DNSSD_API +dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef) { typedef int (DNSSD_API * Func)(DNSServiceRef sdRef); static Func func = NULL; - int ret = -1; + int ret = INVALID_SOCKET; if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) ) { diff --git a/mDNSWindows/DLLX/DNSSD.cpp b/mDNSWindows/DLLX/DNSSD.cpp index 8813d2d..f011dd1 100755 --- a/mDNSWindows/DLLX/DNSSD.cpp +++ b/mDNSWindows/DLLX/DNSSD.cpp @@ -643,7 +643,7 @@ CDNSSD::ResolveReply record->AddRef(); char buf[ 64 ]; - sprintf( buf, "txtLen = %d", txtLen ); + snprintf( buf, sizeof( buf ), "txtLen = %d", txtLen ); OutputDebugStringA( buf ); if ( txtLen > 0 ) diff --git a/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp b/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp index 9e4db65..fe46be0 100644 --- a/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp +++ b/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp @@ -269,7 +269,7 @@ static void DNSStatus inStatusCode, const DNSBrowserEvent * inEvent ); -static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString ); +static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, size_t inLen, char *outString ); static DWORD UTF8StringToStringObject( const char *inUTF8, CString &inObject ); static DWORD StringObjectToUTF8String( CString &inObject, std::string &outUTF8 ); @@ -1273,8 +1273,8 @@ static void serviceInstance->name = inEvent->data.resolved->name; serviceInstance->type = inEvent->data.resolved->type; serviceInstance->domain = inEvent->data.resolved->domain; - serviceInstance->ip = DNSNetworkAddressToString( &inEvent->data.resolved->address, s ); - serviceInstance->ifIP = DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s ); + serviceInstance->ip = DNSNetworkAddressToString( &inEvent->data.resolved->address, sizeof( s ), s ); + serviceInstance->ifIP = DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, sizeof( s ), s ); serviceInstance->text = inEvent->data.resolved->textRecord; serviceInstance->hostName = inEvent->data.resolved->hostName; @@ -1303,7 +1303,7 @@ static void // Note: Currently only supports IPv4 network addresses. //=========================================================================================================================== -static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString ) +static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, size_t inLen, char *outString ) { const DNSUInt8 * p; DNSUInt16 port; @@ -1312,11 +1312,11 @@ static char * DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char * port = ntohs( inAddr->u.ipv4.port.v16 ); if( port != kDNSPortInvalid ) { - sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port ); + snprintf( outString, inLen, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port ); } else { - sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] ); + snprintf( outString, inLen, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] ); } return( outString ); } diff --git a/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp index 92cdeb3..67b9cb8 100644 --- a/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp +++ b/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp @@ -204,7 +204,7 @@ void { char ip[ 64 ]; - sprintf( ip, "%u.%u.%u.%u:%u", + snprintf( ip, sizeof( ip ), "%u.%u.%u.%u:%u", inEvent->data.resolved->address.u.ipv4.addr.v8[ 0 ], inEvent->data.resolved->address.u.ipv4.addr.v8[ 1 ], inEvent->data.resolved->address.u.ipv4.addr.v8[ 2 ], diff --git a/mDNSWindows/Java/makefile b/mDNSWindows/Java/makefile index 2e4b6bd..da23797 100644 --- a/mDNSWindows/Java/makefile +++ b/mDNSWindows/Java/makefile @@ -34,7 +34,7 @@ COREDIR = ..\..\mDNSCore SHAREDDIR = ..\..\mDNSShared -JDK = $(JAVA_HOME) +JDK = "$(JAVA_HOME)" CC = cl RC = rc diff --git a/mDNSWindows/Java/makefile64 b/mDNSWindows/Java/makefile64 index fb0ff9c..5845ab5 100644 --- a/mDNSWindows/Java/makefile64 +++ b/mDNSWindows/Java/makefile64 @@ -34,7 +34,7 @@ COREDIR = ..\..\mDNSCore SHAREDDIR = ..\..\mDNSShared -JDK = $(JAVA_HOME) +JDK = "$(JAVA_HOME)" CC = cl RC = rc diff --git a/mDNSWindows/NSPTool/NSPTool.c b/mDNSWindows/NSPTool/NSPTool.c index f3052d1..0be174d 100644 --- a/mDNSWindows/NSPTool/NSPTool.c +++ b/mDNSWindows/NSPTool/NSPTool.c @@ -38,7 +38,7 @@ DEBUG_LOCAL OSStatus ListNameSpaces( void ); DEBUG_LOCAL OSStatus ReorderNameSpaces( void ); DEBUG_LOCAL WCHAR * CharToWCharString( const char *inCharString, WCHAR *outWCharString ); -DEBUG_LOCAL char * GUIDtoString( const GUID *inGUID, char *outString ); +DEBUG_LOCAL char * GUIDtoString( const GUID *inGUID, size_t inLen, char *outString ); DEBUG_LOCAL OSStatus StringToGUID( const char *inCharString, GUID *outGUID ); DEBUG_LOCAL BOOL gToolQuietMode = FALSE; @@ -390,7 +390,7 @@ DEBUG_LOCAL OSStatus ListNameSpaces( void ) for( i = 0; i < n; ++i ) { fprintf( stdout, "Name Space %d\n", i + 1 ); - fprintf( stdout, " NSProviderId: %s\n", GUIDtoString( &array[ i ].NSProviderId, s ) ); + fprintf( stdout, " NSProviderId: %s\n", GUIDtoString( &array[ i ].NSProviderId, sizeof( s ), s ) ); fprintf( stdout, " dwNameSpace: %d\n", array[ i ].dwNameSpace ); fprintf( stdout, " fActive: %s\n", array[ i ].fActive ? "YES" : "NO" ); fprintf( stdout, " dwVersion: %d\n", array[ i ].dwVersion ); @@ -536,12 +536,12 @@ DEBUG_LOCAL WCHAR * CharToWCharString( const char *inCharString, WCHAR *outWChar // GUIDtoString //=========================================================================================================================== -DEBUG_LOCAL char * GUIDtoString( const GUID *inGUID, char *outString ) +DEBUG_LOCAL char * GUIDtoString( const GUID *inGUID, size_t inLen, char *outString ) { check( inGUID ); check( outString ); - sprintf( outString, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + _snprintf( outString, inLen, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", inGUID->Data1, inGUID->Data2, inGUID->Data3, inGUID->Data4[ 0 ], inGUID->Data4[ 1 ], inGUID->Data4[ 2 ], inGUID->Data4[ 3 ], inGUID->Data4[ 4 ], inGUID->Data4[ 5 ], inGUID->Data4[ 6 ], inGUID->Data4[ 7 ] ); diff --git a/mDNSWindows/SystemService/Service.c b/mDNSWindows/SystemService/Service.c index d175326..1dea5c7 100644 --- a/mDNSWindows/SystemService/Service.c +++ b/mDNSWindows/SystemService/Service.c @@ -786,7 +786,7 @@ static void ReportStatus( int inType, const char *inFormat, ... ) BOOL ok; const char * array[ 1 ]; - vsprintf( s, inFormat, args ); + vsnprintf( s, sizeof( s ), inFormat, args ); array[ 0 ] = s; ok = ReportEventA( gServiceEventSource, (WORD) inType, 0, MDNSRESPONDER_LOG, NULL, 1, 0, array, NULL ); check_translated_errno( ok, GetLastError(), kUnknownErr ); diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c index fd11c58..f2c4319 100755 --- a/mDNSWindows/mDNSWin32.c +++ b/mDNSWindows/mDNSWin32.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013, 2015 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. @@ -106,7 +106,6 @@ mDNSlocal int getifaddrs( struct ifaddrs **outAddrs ); mDNSlocal void freeifaddrs( struct ifaddrs *inAddrs ); - // Platform Accessors #ifdef __cplusplus @@ -127,8 +126,6 @@ mDNSexport mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInter // Wakeup Structs -#define kUnicastWakeupNumTries ( 1 ) -#define kUnicastWakeupSleepBetweenTries ( 0 ) #define kMulticastWakeupNumTries ( 18 ) #define kMulticastWakeupSleepBetweenTries ( 100 ) @@ -152,7 +149,6 @@ typedef struct MulticastWakeupStruct mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ); - mDNSlocal DWORD GetPrimaryInterface(); mDNSlocal mStatus AddressToIndexAndMask( struct sockaddr * address, uint32_t * index, struct sockaddr * mask ); mDNSlocal mDNSBool CanReceiveUnicast( void ); @@ -216,12 +212,10 @@ mDNSlocal BOOL gEnableIPv6 = TRUE; #endif - #ifndef HCRYPTPROV typedef ULONG_PTR HCRYPTPROV; // WinCrypt.h, line 249 #endif - #ifndef CRYPT_MACHINE_KEYSET # define CRYPT_MACHINE_KEYSET 0x00000020 #endif @@ -277,7 +271,6 @@ mDNSlocal HANDLE gSMBThreadQuitEvent = NULL; #define kSMBRegisterEvent ( WAIT_OBJECT_0 + 1 ) #define kSMBDeregisterEvent ( WAIT_OBJECT_0 + 2 ) - #if 0 #pragma mark - #pragma mark == Platform Support == @@ -570,7 +563,6 @@ mDNSexport void mDNSPlatformClose( mDNS * const inMDNS ) dlog( kDebugLevelTrace, DEBUG_NAME "platform close done\n" ); } - //=========================================================================================================================== // mDNSPlatformLock //=========================================================================================================================== @@ -602,6 +594,39 @@ mDNSexport void mDNSPlatformStrCopy( void *inDst, const void *inSrc ) } //=========================================================================================================================== +// mDNSPlatformStrLCopy +//=========================================================================================================================== + +mDNSexport mDNSu32 mDNSPlatformStrLCopy( void *inDst, const void *inSrc, mDNSu32 inSize ) +{ + const char * src = (const char *) inSrc; + + if( inSize > 0 ) + { + size_t n; + char * dst = (char *) inDst; + + for( n = inSize - 1; n > 0; --n ) + { + if( ( *dst++ = *src++ ) == '\0' ) + { + // Null terminator encountered, so exit. + goto exit; + } + } + *dst = '\0'; + } + + while( *src++ != '\0' ) + { + // Stop at null terminator. + } + +exit: + return( (mDNSu32)( src - (const char *) inSrc ) - 1 ); +} + +//=========================================================================================================================== // mDNSPlatformStrLen //=========================================================================================================================== @@ -940,7 +965,6 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDN return( index ); } - //=========================================================================================================================== // mDNSPlatformTCPSocket //=========================================================================================================================== @@ -1077,12 +1101,10 @@ exit: return err; } - //=========================================================================================================================== // mDNSPlatformTCPAccept //=========================================================================================================================== -mDNSexport mDNSexport TCPSocket *mDNSPlatformTCPAccept( TCPSocketFlags flags, int fd ) { TCPSocket * sock = NULL; @@ -1109,7 +1131,6 @@ exit: return sock; } - //=========================================================================================================================== // mDNSPlatformTCPCloseConnection //=========================================================================================================================== @@ -1133,7 +1154,6 @@ mDNSexport void mDNSPlatformTCPCloseConnection( TCPSocket *sock ) } } - //=========================================================================================================================== // mDNSPlatformReadTCP //=========================================================================================================================== @@ -1173,7 +1193,6 @@ mDNSexport long mDNSPlatformReadTCP( TCPSocket *sock, void *inBuffer, unsigned l return nread; } - //=========================================================================================================================== // mDNSPlatformWriteTCP //=========================================================================================================================== @@ -1207,8 +1226,6 @@ mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock ) return ( int ) sock->fd; } - - //=========================================================================================================================== // TCPSocketNotification //=========================================================================================================================== @@ -1248,8 +1265,6 @@ exit: return; } - - //=========================================================================================================================== // mDNSPlatformUDPSocket //=========================================================================================================================== @@ -1362,7 +1377,6 @@ mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock ) } } - //=========================================================================================================================== // mDNSPlatformSendUDP //=========================================================================================================================== @@ -1450,21 +1464,12 @@ exit: return( err ); } - -mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src) -{ - DEBUG_UNUSED( m ); - DEBUG_UNUSED( src ); - return mDNSfalse; -} - mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) { DEBUG_UNUSED( m ); DEBUG_UNUSED( InterfaceID ); } - mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason) { DEBUG_UNUSED( m ); @@ -1482,14 +1487,13 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte unsigned char buf[ 102 ]; char hex[ 3 ] = { 0 }; unsigned char *bufPtr = buf; - struct sockaddr_storage saddr; - INT len = sizeof( saddr ); - mDNSBool unicast = mDNSfalse; MulticastWakeupStruct *info; int i; mStatus err; - (void) InterfaceID; + (void) InterfaceID; // unused + (void) ipaddr; // unused + (void) iteration; // unused require_action( ethaddr, exit, err = mStatus_BadParamErr ); @@ -1512,44 +1516,6 @@ mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID Inte bufPtr += sizeof( mac ); } - if ( ipaddr ) - { - if ( WSAStringToAddressA( ipaddr, AF_INET, NULL, ( LPSOCKADDR ) &saddr, &len ) == 0 ) - { - struct sockaddr_in * saddr4 = ( struct sockaddr_in* ) &saddr; - saddr4->sin_port = htons( 9 ); - len = sizeof( *saddr4 ); - - if ( saddr4->sin_addr.s_addr != htonl( INADDR_ANY ) ) - { - unicast = mDNStrue; - } - } - else if ( WSAStringToAddressA( ipaddr, AF_INET6, NULL, ( LPSOCKADDR ) &saddr, &len ) == 0 ) - { - mDNSInterfaceData *ifd = ( mDNSInterfaceData* ) InterfaceID; - struct sockaddr_in6 * saddr6 = ( struct sockaddr_in6* ) &saddr; - saddr6->sin6_port = htons( 9 ); - - if ( ifd != NULL ) - { - saddr6->sin6_scope_id = ifd->scopeID; - } - - len = sizeof( *saddr6 ); - - if ( memcmp( &saddr6->sin6_addr, &in6addr_any, sizeof( IN6_ADDR ) ) != 0 ) - { - unicast = mDNStrue; - } - } - } - - if ( ( iteration < 2 ) && ( unicast ) ) - { - SendWakeupPacket( m, ( LPSOCKADDR ) &saddr, len, ( const char* ) buf, sizeof( buf ), kUnicastWakeupNumTries, kUnicastWakeupSleepBetweenTries ); - } - info = ( MulticastWakeupStruct* ) malloc( sizeof( MulticastWakeupStruct ) ); require_action( info, exit, err = mStatus_NoMemoryErr ); info->inMDNS = m; @@ -1570,11 +1536,10 @@ exit: return; } - -mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf) +mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(const AuthRecord *rr, mDNSInterfaceID InterfaceID) { DEBUG_UNUSED( rr ); - DEBUG_UNUSED( intf ); + DEBUG_UNUSED( InterfaceID ); return mDNStrue; } @@ -1601,7 +1566,6 @@ mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsiz if (bufsize) buf[0] = 0; } - mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID) { DEBUG_UNUSED( m ); @@ -1610,7 +1574,6 @@ mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSA DEBUG_UNUSED( InterfaceID ); } - mDNSexport void mDNSPlatformReceiveRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID) { DEBUG_UNUSED( msg ); @@ -1711,7 +1674,6 @@ mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, return mDNStrue; } - //=========================================================================================================================== // mDNSPlatformDynDNSHostNameStatusChanged //=========================================================================================================================== @@ -1756,7 +1718,6 @@ exit: return; } - //=========================================================================================================================== // SetDomainSecrets //=========================================================================================================================== @@ -1798,7 +1759,6 @@ SetDomainSecrets( mDNS * const m ) } } - //=========================================================================================================================== // SetSearchDomainList //=========================================================================================================================== @@ -1849,7 +1809,6 @@ exit: SetReverseMapSearchDomainList(); } - //=========================================================================================================================== // SetReverseMapSearchDomainList //=========================================================================================================================== @@ -1871,10 +1830,10 @@ SetReverseMapSearchDomainList( void ) if (!SetupAddr(&netmask, ifa->ifa_netmask)) { - sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3], - addr.ip.v4.b[2] & netmask.ip.v4.b[2], - addr.ip.v4.b[1] & netmask.ip.v4.b[1], - addr.ip.v4.b[0] & netmask.ip.v4.b[0]); + _snprintf(buffer, sizeof( buffer ), "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3], + addr.ip.v4.b[2] & netmask.ip.v4.b[2], + addr.ip.v4.b[1] & netmask.ip.v4.b[1], + addr.ip.v4.b[0] & netmask.ip.v4.b[0]); mDNS_AddSearchDomain_CString(buffer, mDNSNULL); } } @@ -1885,7 +1844,6 @@ SetReverseMapSearchDomainList( void ) return; } - //=========================================================================================================================== // SetDNSServers //=========================================================================================================================== @@ -1978,7 +1936,6 @@ exit: } } - //=========================================================================================================================== // SetDomainFromDHCP //=========================================================================================================================== @@ -2071,7 +2028,6 @@ exit: } } - //=========================================================================================================================== // mDNSPlatformGetPrimaryInterface //=========================================================================================================================== @@ -2145,7 +2101,7 @@ exit: } mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win) - { +{ (void) sadd; // Unused (void) dadd; // Unused (void) lport; // Unused @@ -2153,52 +2109,54 @@ mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIP (void) seq; // Unused (void) ack; // Unused (void) win; // Unused - } +} -mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNSAddr *raddr, char *eth) - { - (void) raddr; // Unused - (void) eth; // Unused - } +mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr) +{ + (void) m; // Unused + (void) raddr; // Unused + + return mStatus_UnsupportedErr; +} mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) - { +{ (void) spsaddr; // Unused (void) ifname; // Unused - } -mDNSexport mStatus mDNSPlatformClearSPSMACAddr(void) - { - } + return mStatus_UnsupportedErr; +} + +mDNSexport mStatus mDNSPlatformClearSPSData(void) +{ + return mStatus_UnsupportedErr; +} + +mDNSexport mStatus mDNSPlatformStoreOwnerOptRecord(char *ifname, DNSMessage *msg, int length) +{ + (void) ifname; // Unused + (void) msg; // Unused + (void) length; // Unused + return mStatus_UnsupportedErr; +} mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) - { +{ (void) m; // Unused (void) laddr; // Unused (void) raddr; // Unused (void) lport; // Unused (void) rport; // Unused (void) mti; // Unused - } -mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) - { - (void) m; - (void) q; - return mDNStrue; - } - -mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q) - { - (void) m; - (void) q; - return -1; - } + return mStatus_UnsupportedErr; +} -mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +mDNSexport void mDNSPlatformSetSocktOpt(void *sock, mDNSTransport_Type transType, mDNSAddr_Type addrType, const DNSQuestion *q) { - (void) src; - (void) dst; + (void) sock; + (void) transType; + (void) addrType; (void) q; } @@ -2262,13 +2220,11 @@ mDNSexport void verbosedebugf_( const char *inFormat, ... ) } #endif - #if 0 #pragma mark - #pragma mark == Platform Internals == #endif - //=========================================================================================================================== // SetupNiceName //=========================================================================================================================== @@ -2340,7 +2296,7 @@ mStatus SetupNiceName( mDNS * const inMDNS ) { // Invalidate name so fall back to a default name. - strcpy( utf8, kMDNSDefaultName ); + strcpy_s( utf8, sizeof( utf8 ), kMDNSDefaultName ); } utf8[ sizeof( utf8 ) - 1 ] = '\0'; @@ -2410,7 +2366,7 @@ mDNSlocal mStatus SetupHostName( mDNS * const inMDNS ) { // Invalidate name so fall back to a default name. - strcpy( tempString, kMDNSDefaultName ); + strcpy_s( tempString, sizeof( tempString ), kMDNSDefaultName ); } tempString[ sizeof( tempString ) - 1 ] = '\0'; @@ -2455,7 +2411,6 @@ mDNSlocal mStatus SetupName( mDNS * const inMDNS ) return err; } - //=========================================================================================================================== // SetupInterfaceList //=========================================================================================================================== @@ -2886,6 +2841,7 @@ mDNSlocal mStatus SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inI // If interface is a direct link, address record will be marked as kDNSRecordTypeKnownUnique // and skip the probe phase of the probe/announce packet sequence. ifd->interfaceInfo.DirectLink = mDNSfalse; + ifd->interfaceInfo.SupportsUnicastMDNSResponse = mDNStrue; err = mDNS_RegisterInterface( inMDNS, &ifd->interfaceInfo, mDNSfalse ); require_noerr( err, exit ); @@ -3207,7 +3163,6 @@ mDNSlocal mStatus SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAd return( err ); } - #if 0 #pragma mark - #endif @@ -3402,7 +3357,6 @@ exit: return; } - //=========================================================================================================================== // InterfaceListDidChange //=========================================================================================================================== @@ -3433,7 +3387,6 @@ void InterfaceListDidChange( mDNS * const inMDNS ) mDNSCoreMachineSleep( inMDNS, mDNSfalse ); // What is this for? Mac OS X does not do this } - //=========================================================================================================================== // ComputerDescriptionDidChange //=========================================================================================================================== @@ -3446,7 +3399,6 @@ void ComputerDescriptionDidChange( mDNS * const inMDNS ) SetupNiceName( inMDNS ); } - //=========================================================================================================================== // TCPIPConfigDidChange //=========================================================================================================================== @@ -3461,7 +3413,6 @@ void TCPIPConfigDidChange( mDNS * const inMDNS ) check_noerr( err ); } - //=========================================================================================================================== // DynDNSConfigDidChange //=========================================================================================================================== @@ -3478,7 +3429,6 @@ void DynDNSConfigDidChange( mDNS * const inMDNS ) check_noerr( err ); } - //=========================================================================================================================== // FileSharingDidChange //=========================================================================================================================== @@ -3490,7 +3440,6 @@ void FileSharingDidChange( mDNS * const inMDNS ) CheckFileShares( inMDNS ); } - //=========================================================================================================================== // FilewallDidChange //=========================================================================================================================== @@ -3502,7 +3451,6 @@ void FirewallDidChange( mDNS * const inMDNS ) CheckFileShares( inMDNS ); } - #if 0 #pragma mark - #pragma mark == Utilities == @@ -3980,7 +3928,7 @@ mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs ) ifa->ifa_name = (char *) malloc( 16 ); require_action( ifa->ifa_name, exit, err = WSAENOBUFS ); - sprintf( ifa->ifa_name, "%d", i + 1 ); + _snprintf( ifa->ifa_name, 16, "%d", i + 1 ); // Get interface flags. @@ -4090,7 +4038,6 @@ mDNSlocal void freeifaddrs( struct ifaddrs *inIFAs ) } } - //=========================================================================================================================== // GetPrimaryInterface //=========================================================================================================================== @@ -4121,7 +4068,6 @@ GetPrimaryInterface() err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder); require_noerr( err, exit ); - // Search for the row in the table we want. for ( i = 0; i < pIpForwardTable->dwNumEntries; i++) @@ -4150,7 +4096,6 @@ exit: return index; } - //=========================================================================================================================== // AddressToIndexAndMask //=========================================================================================================================== @@ -4211,7 +4156,6 @@ exit: return err; } - //=========================================================================================================================== // CanReceiveUnicast //=========================================================================================================================== @@ -4242,7 +4186,6 @@ mDNSlocal mDNSBool CanReceiveUnicast( void ) return( ok ); } - //=========================================================================================================================== // IsPointToPoint //=========================================================================================================================== @@ -4284,7 +4227,6 @@ exit: return ret; } - //=========================================================================================================================== // GetWindowsVersionString //=========================================================================================================================== @@ -4385,7 +4327,6 @@ exit: return( err ); } - //=========================================================================================================================== // RegQueryString //=========================================================================================================================== @@ -4434,7 +4375,6 @@ exit: return err; } - //=========================================================================================================================== // StringToAddress //=========================================================================================================================== @@ -4474,7 +4414,6 @@ exit: return err; } - //=========================================================================================================================== // myGetIfAddrs //=========================================================================================================================== @@ -4498,7 +4437,6 @@ myGetIfAddrs(int refresh) return ifa; } - //=========================================================================================================================== // TCHARtoUTF8 //=========================================================================================================================== @@ -4521,7 +4459,6 @@ exit: #endif } - //=========================================================================================================================== // WindowsLatin1toUTF8 //=========================================================================================================================== @@ -4559,7 +4496,6 @@ exit: return( err ); } - //=========================================================================================================================== // TCPCloseSocket //=========================================================================================================================== @@ -4576,7 +4512,6 @@ TCPCloseSocket( TCPSocket * sock ) } } - //=========================================================================================================================== // UDPCloseSocket //=========================================================================================================================== @@ -4594,7 +4529,6 @@ UDPCloseSocket( UDPSocket * sock ) } } - //=========================================================================================================================== // SetupAddr //=========================================================================================================================== @@ -4624,7 +4558,6 @@ mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa) return(mStatus_Invalid); } - mDNSlocal void GetDDNSFQDN( domainname *const fqdn ) { LPSTR name = NULL; @@ -4668,7 +4601,6 @@ exit: } } - #ifdef UNICODE mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey ) #else @@ -4752,7 +4684,6 @@ exit: } } - mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain ) { char domainUTF8[ 256 ]; @@ -4799,7 +4730,6 @@ exit: return; } - mDNSlocal VOID CALLBACK CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue ) { @@ -4811,7 +4741,6 @@ CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue ) CheckFileShares( m ); } - mDNSlocal unsigned __stdcall SMBRegistrationThread( void * arg ) { @@ -4900,7 +4829,6 @@ exit: return 0; } - mDNSlocal void CheckFileShares( mDNS * const m ) { @@ -5054,7 +4982,6 @@ exit: } } - BOOL IsWOMPEnabled( mDNS * const m ) { @@ -5076,7 +5003,6 @@ IsWOMPEnabled( mDNS * const m ) return enabled; } - mDNSlocal mDNSu8 IsWOMPEnabledForAdapter( const char * adapterName ) { @@ -5126,7 +5052,6 @@ exit: return ( mDNSu8 ) ok; } - mDNSlocal void SendWakeupPacket( mDNS * const inMDNS, LPSOCKADDR addr, INT addrlen, const char * buf, INT buflen, INT numTries, INT msecSleep ) { @@ -5173,7 +5098,6 @@ exit: } } - mDNSlocal void _cdecl SendMulticastWakeupPacket( void *arg ) { @@ -5188,7 +5112,6 @@ SendMulticastWakeupPacket( void *arg ) _endthread(); } - mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result) { DEBUG_UNUSED( m ); diff --git a/mDNSWindows/mdnsNSP/mdnsNSP.c b/mDNSWindows/mdnsNSP/mdnsNSP.c index 2cd01ef..21baa86 100644 --- a/mDNSWindows/mdnsNSP/mdnsNSP.c +++ b/mDNSWindows/mdnsNSP/mdnsNSP.c @@ -1847,7 +1847,7 @@ InHostsTable( const char * name ) HostsFile * hFile; GetSystemDirectory( systemDirectory, sizeof( systemDirectory ) ); - sprintf( hFileName, "%s\\drivers\\etc\\hosts", systemDirectory ); + snprintf( hFileName, sizeof( hFileName ), "%s\\drivers\\etc\\hosts", systemDirectory ); err = HostsFileOpen( &hFile, hFileName ); require_noerr( err, exit ); @@ -2072,6 +2072,7 @@ HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo ) int idx; int i; short family; + size_t len; OSStatus err = kNoErr; check( self ); @@ -2146,9 +2147,10 @@ HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo ) // Now we have the name - (*hInfo)->m_host.h_name = (char*) malloc( strlen( tok ) + 1 ); + len = strlen( tok ) + 1; + (*hInfo)->m_host.h_name = (char*) malloc( len ); require_action( (*hInfo)->m_host.h_name, exit, err = kNoMemoryErr ); - strcpy( (*hInfo)->m_host.h_name, tok ); + strcpy_s( (*hInfo)->m_host.h_name, len, tok ); // Now create the address (IPv6/IPv4) @@ -2220,10 +2222,11 @@ HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo ) require_action( (*hInfo)->m_host.h_aliases, exit, err = kNoMemoryErr ); } - (*hInfo)->m_host.h_aliases[i] = (char*) malloc( strlen( tok ) + 1 ); + len = strlen( tok ) + 1; + (*hInfo)->m_host.h_aliases[i] = (char*) malloc( len ); require_action( (*hInfo)->m_host.h_aliases[i], exit, err = kNoMemoryErr ); - strcpy( (*hInfo)->m_host.h_aliases[i], tok ); + strcpy_s( (*hInfo)->m_host.h_aliases[i], len, tok ); if (( tok = strpbrk( tok, " \t")) != NULL ) { diff --git a/unittests/DNSMessageTest.c b/unittests/DNSMessageTest.c new file mode 100644 index 0000000..fedfed3 --- /dev/null +++ b/unittests/DNSMessageTest.c @@ -0,0 +1,50 @@ +#include "mDNSEmbeddedAPI.h" +#include "DNSMessageTest.h" +#include "../mDNSCore/DNSCommon.h" + +int SizeTest(void); +int InitializeTest(void); +int PutDomainNameAsLabels(void); +int PutRData(void); +int Finalize(void); + + +DNSMessage *msg; + + +UNITTEST_HEADER(DNSMessageTest) + UNITTEST_TEST(SizeTest) + UNITTEST_TEST(InitializeTest) + UNITTEST_TEST(Finalize) +UNITTEST_FOOTER + + +UNITTEST_HEADER(SizeTest) + msg = (DNSMessage *)malloc (sizeof(DNSMessage)); + UNITTEST_ASSERT_RETURN(msg != NULL); + + // message header should be 12 bytes + UNITTEST_ASSERT(sizeof(msg->h) == 12); +UNITTEST_FOOTER + + +UNITTEST_HEADER(InitializeTest) + // Initialize the message + InitializeDNSMessage(&msg->h, onesID, QueryFlags); + + // Check that the message is initialized properly + UNITTEST_ASSERT(msg->h.numAdditionals == 0); + UNITTEST_ASSERT(msg->h.numAnswers == 0); + UNITTEST_ASSERT(msg->h.numQuestions == 0); + UNITTEST_ASSERT(msg->h.numAuthorities == 0); +UNITTEST_FOOTER + + +UNITTEST_HEADER(PutDomainNameAsLabels) + +UNITTEST_FOOTER + +UNITTEST_HEADER(Finalize) + UNITTEST_ASSERT_RETURN(msg != NULL) + free(msg); +UNITTEST_FOOTER \ No newline at end of file diff --git a/unittests/DNSMessageTest.h b/unittests/DNSMessageTest.h new file mode 100644 index 0000000..147175f --- /dev/null +++ b/unittests/DNSMessageTest.h @@ -0,0 +1,8 @@ +#ifndef DNSMessageTest_h +#define DNSMessageTest_h + +#include "unittest.h" + +int DNSMessageTest(void); + +#endif /* DNSMessageTest_h */ \ No newline at end of file diff --git a/unittests/DomainNameTest.c b/unittests/DomainNameTest.c new file mode 100644 index 0000000..571725a --- /dev/null +++ b/unittests/DomainNameTest.c @@ -0,0 +1,28 @@ +#include "DomainNameTest.h" +#include "mDNSEmbeddedAPI.h" +#include "../mDNSCore/DNSCommon.h" + +int SameDomainNameTest(void); +int SameDomainLabelTest(void); +int LocalDomainTest(void); + + +UNITTEST_HEADER(DomainNameTest) + UNITTEST_TEST(SameDomainLabelTest) + UNITTEST_TEST(SameDomainNameTest) + UNITTEST_TEST(LocalDomainTest) +UNITTEST_FOOTER + + + + +UNITTEST_HEADER(SameDomainLabelTest) +UNITTEST_FOOTER + + +UNITTEST_HEADER(SameDomainNameTest) +UNITTEST_FOOTER + + +UNITTEST_HEADER(LocalDomainTest) +UNITTEST_FOOTER \ No newline at end of file diff --git a/unittests/DomainNameTest.h b/unittests/DomainNameTest.h new file mode 100644 index 0000000..1c429c2 --- /dev/null +++ b/unittests/DomainNameTest.h @@ -0,0 +1,7 @@ +#ifndef DomainNameTest_h +#define DomainNameTest_h + +#include "unittest.h" +int DomainNameTest(void); + +#endif /* DomainNameTest_h */ diff --git a/unittests/InterfaceTest.c b/unittests/InterfaceTest.c new file mode 100644 index 0000000..a973814 --- /dev/null +++ b/unittests/InterfaceTest.c @@ -0,0 +1,19 @@ +#include "InterfaceTest.h" +#include "mDNSEmbeddedAPI.h" + + +NetworkInterfaceInfo *intf; +mDNS *m; + +int LocalSubnetTest(void); + +UNITTEST_HEADER(InterfaceTest) + UNITTEST_TEST(LocalSubnetTest) +UNITTEST_FOOTER + +UNITTEST_HEADER(LocalSubnetTest) + // need a way to initialize m before we call into the class of APIs that use a ptr to mDNS + // should that pointer be common to all tests? + // mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) + // TEST_ASSERT_RETURN (for IPv4/IPv6 local subnet) +UNITTEST_FOOTER diff --git a/unittests/InterfaceTest.h b/unittests/InterfaceTest.h new file mode 100644 index 0000000..f94b3da --- /dev/null +++ b/unittests/InterfaceTest.h @@ -0,0 +1,9 @@ + +#ifndef InterfaceTest_h +#define InterfaceTest_h + +#include "unittest.h" + +int InterfaceTest(void); + +#endif /* InterfaceTest_h */ diff --git a/unittests/ResourceRecordTest.c b/unittests/ResourceRecordTest.c new file mode 100644 index 0000000..d053008 --- /dev/null +++ b/unittests/ResourceRecordTest.c @@ -0,0 +1,60 @@ +#include "mDNSEmbeddedAPI.h" +#include "../mDNSCore/DNSCommon.h" +#include "ResourceRecordTest.h" + +int TXTSetupTest(void); +int ASetupTest(void); +int OPTSetupTest(void); + + +UNITTEST_HEADER(ResourceRecordTest) + UNITTEST_TEST(TXTSetupTest) + UNITTEST_TEST(ASetupTest) + UNITTEST_TEST(OPTSetupTest) +UNITTEST_FOOTER + + +UNITTEST_HEADER(TXTSetupTest) + + AuthRecord authRec; + mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeShared, AuthRecordAny,mDNSNULL, mDNSNULL); + UNITTEST_ASSERT_RETURN(authRec.resrec.RecordType == kDNSType_TXT); + UNITTEST_ASSERT_RETURN(authRec.resrec.rdata->MaxRDLength == sizeof(RDataBody)); + + // Retest with a RDataStorage set to a a buffer +UNITTEST_FOOTER + + +UNITTEST_HEADER(ASetupTest) + AuthRecord authRec; + mDNS_SetupResourceRecord(&authRec, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + UNITTEST_ASSERT_RETURN(authRec.resrec.RecordType == kDNSType_A); + // Add more verifications + +UNITTEST_FOOTER + + +UNITTEST_HEADER(OPTSetupTest) + AuthRecord opt; + mDNSu32 updatelease = 7200; +/* mDNSu8 data[AbsoluteMaxDNSMessageData]; + mDNSu8 *p = data; + mDNSu16 numAdditionals; +*/ + // Setup the OPT Record + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + + // Verify the basic initialization is all ok + + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdestimate = sizeof(rdataOPT); + opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease; + opt.resrec.rdata->u.opt[0].u.updatelease = updatelease; + + // Put the resource record in and verify everything is fine + // p = PutResourceRecordTTLWithLimit(&data, p, &numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, data + AbsoluteMaxDNSMessageData); + + + // Repeat with bad data to make sure it bails out cleanly +UNITTEST_FOOTER \ No newline at end of file diff --git a/unittests/ResourceRecordTest.h b/unittests/ResourceRecordTest.h new file mode 100644 index 0000000..d762d01 --- /dev/null +++ b/unittests/ResourceRecordTest.h @@ -0,0 +1,10 @@ + + +#ifndef ResourceRecordTest_h +#define ResourceRecordTest_h + +#include "unittest.h" + +int ResourceRecordTest(void); + +#endif /* ResourceRecordTest_h */ diff --git a/mDNSMacOSX/VPNService.c b/unittests/main.c similarity index 52% rename from mDNSMacOSX/VPNService.c rename to unittests/main.c index 623ddbf..752fffb 100644 --- a/mDNSMacOSX/VPNService.c +++ b/unittests/main.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2013 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2015 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. @@ -14,20 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#include "mDNSMacOSX.h" -#include -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; -} +#include "unittest.h" +#include "DNSMessageTest.h" + +const char *HWVersionString = "unittestMac1,1"; +const char *OSVersionString = "unittest 1.1.1 (1A111)"; +const char *BinaryNameString = "unittest"; +const char *VersionString = "unittest mDNSResponer-00 (Jan 1 1970 00:00:00)"; + +int run_tests(void); + +UNITTEST_HEADER(run_tests) +UNITTEST_GROUP(DNSMessageTest) +UNITTEST_FOOTER + +UNITTEST_MAIN diff --git a/unittests/unittest.c b/unittests/unittest.c new file mode 100644 index 0000000..1ad4b03 --- /dev/null +++ b/unittests/unittest.c @@ -0,0 +1,105 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include "unittest.h" + +int _unittest_assert_i(const int condition, const int i, const char * const conditionStr, + const char * const filename, const unsigned int linenum, + const char * const functionname, __test_item ** __i, int * const __success) +{ + if (!condition) + { + __test_item* tba = malloc(sizeof(__test_item)); + tba->next = *__i; + tba->file = filename; + tba->line = linenum; + tba->func = functionname; + tba->s = conditionStr; + tba->iter_count = i; + *__i = tba; + *__success = 0; + printf("F"); + } + else + { + printf("."); + } + + fflush(NULL); + return condition; +} + +void _unittest_print_list(__test_item* __i) +{ + __test_item* __tmp = NULL; + while (__i) + { + __test_item* __o = __i->next; + __i->next = __tmp; + __tmp = __i; + __i = __o; + } + __i = __tmp; + + while(__i) + { + printf("%s: In function `%s':\n%s:%d: error: failed UNITTEST_ASSERT", __i->file, __i->func, __i->file, __i->line); + if (__i->iter_count != -1) printf(" at iteration %d", __i->iter_count); + printf(": %s\n", __i->s); + __test_item* tbd = __i; + __i = __i->next; + free(tbd); + } +} + +// test by building like: +// gcc -g -Wall -Werror -DTEST_UNITTEST_SCAFFOLD unittest.c +// #define TEST_UNITTEST_SCAFFOLD 1 +#if TEST_UNITTEST_SCAFFOLD + +// modify this test as necessary to test the scaffold +UNITTEST_HEADER(test1) + int i = 0; + int j = 1; + int k = 2; + + UNITTEST_ASSERT(i==j); + UNITTEST_ASSERTI(j==i, k); + UNITTEST_ASSERT(i==i); + UNITTEST_ASSERTI(j==j, k); + UNITTEST_ASSERT_RETURN(j==j); + UNITTEST_ASSERTI_RETURN(j==j, k); +UNITTEST_FOOTER + +UNITTEST_HEADER(test2) + UNITTEST_ASSERT(1); + UNITTEST_ASSERT(0); + UNITTEST_ASSERT(1); +UNITTEST_FOOTER + +UNITTEST_HEADER(unittest_tests) +UNITTEST_TEST(test1) +UNITTEST_TEST(test2) +UNITTEST_FOOTER + +UNITTEST_HEADER(run_tests) +UNITTEST_GROUP(unittest_tests) +UNITTEST_FOOTER + +UNITTEST_MAIN + +#endif // TEST_UNITTEST_SCAFFOLD diff --git a/unittests/unittest.h b/unittests/unittest.h new file mode 100644 index 0000000..a219aa8 --- /dev/null +++ b/unittests/unittest.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2015 Apple Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#ifndef _UNITTEST_H_ +#define _UNITTEST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct __test_item_ +{ + struct __test_item_* next; + const char* file; + unsigned int line; + const char* func; + const char* s; + int iter_count; +} __test_item; + + +#define UNITTEST_HEADER(X) int X() { int __success = 1; __test_item* __i = NULL; + +#define UNITTEST_GROUP(X) { printf("== %s ==\n", #X); __success = X() && __success; } +#define UNITTEST_TEST(X) { printf("%s: ", #X); fflush(NULL); __success = X() && __success; } + +int _unittest_assert_i(const int condition, const int i, const char * const conditionStr, + const char * const filename, const unsigned int linenum, + const char * const functionname, __test_item ** __i, int * const __success); +#define UNITTEST_ASSERTI(X,I) (_unittest_assert_i((X)!=0, (I), #X, __FILE__, __LINE__, __func__, &__i, &__success)) +#define UNITTEST_ASSERT(X) UNITTEST_ASSERTI(X, -1) +#define UNITTEST_ASSERTI_RETURN(X,I) { if (!UNITTEST_ASSERTI(X,I)) goto __unittest_footer__; } +#define UNITTEST_ASSERT_RETURN(X) UNITTEST_ASSERTI_RETURN(X, -1) + +void _unittest_print_list(__test_item* __i); +#define UNITTEST_FOOTER goto __unittest_footer__; __unittest_footer__: printf("\n"); fflush(NULL); _unittest_print_list(__i); return __success; } + +#define UNITTEST_MAIN int main (int argc, char** argv) \ + { \ + (void)(argv); \ + signal(SIGPIPE, SIG_IGN); \ + FILE* fp; \ + unlink("unittest_success"); \ + if (!run_tests()) return -1; \ + fp = fopen("unittest_success", "w"); \ + if (!fp) return -2; \ + fclose(fp); \ + printf("unit test SUCCESS\n"); \ + if (argc != 1) \ + { \ + char c; \ + printf("run leaks now\n"); \ + read(STDIN_FILENO, &c, 1); \ + } \ + return 0; \ + } + +#define UNITTEST_FAIL_ASSERT { assert(((void*)__func__) == 0); } + +#ifdef __cplusplus +} +#endif + +#endif // ndef _UNITTEST_H_ -- 2.7.4