From a6b1d332b1d22fb47edd711a82e1857e42110b62 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 18 Jun 2020 13:07:18 +0200 Subject: mDNSResponder: Update to v878.200.35 The sources can be obtained via: https://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-878.200.35.tar.gz Update #4010. --- mDNSResponder/Clients/dns-sd.c | 107 +- mDNSResponder/Clients/dnssdutil.c | 5813 ++++++++++++++++++-- mDNSResponder/Makefile | 6 +- mDNSResponder/mDNSCore/DNSCommon.c | 359 +- mDNSResponder/mDNSCore/DNSCommon.h | 4 +- mDNSResponder/mDNSCore/mDNS.c | 265 +- mDNSResponder/mDNSCore/mDNSDebug.h | 6 +- mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h | 60 +- mDNSResponder/mDNSCore/uDNS.c | 2 +- mDNSResponder/mDNSMacOSX/D2D.c | 49 +- mDNSResponder/mDNSMacOSX/DNS64.c | 8 +- mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c | 10 +- mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h | 15 +- .../AppleInternal/com.apple.mDNSResponder.plist | 14 + .../LoggingProfiles/com.apple.mDNSResponder.plist | 16 + mDNSResponder/mDNSMacOSX/Metrics.h | 2 +- mDNSResponder/mDNSMacOSX/Metrics.m | 29 +- .../Private/com.apple.mDNSResponder.plist | 19 - .../mDNSMacOSX/Scripts/bonjour-mcast-diagnose | 380 ++ .../mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump | 56 + .../com.apple.mDNSResponder.mdns-tcpdump.plist | 21 + mDNSResponder/mDNSMacOSX/daemon.c | 20 +- mDNSResponder/mDNSMacOSX/helper-stubs.c | 18 +- mDNSResponder/mDNSMacOSX/helper.c | 15 +- mDNSResponder/mDNSMacOSX/mDNSMacOSX.c | 280 +- mDNSResponder/mDNSMacOSX/mDNSMacOSX.h | 3 +- mDNSResponder/mDNSMacOSX/mDNSResponder.sb | 4 +- .../mDNSResponder.xcodeproj/project.pbxproj | 156 +- mDNSResponder/mDNSMacOSX/uDNSPathEvalulation.c | 8 +- mDNSResponder/mDNSPosix/ProxyResponder.c | 2 +- mDNSResponder/mDNSPosix/Responder.c | 2 +- mDNSResponder/mDNSPosix/mDNSUNP.c | 60 +- mDNSResponder/mDNSPosix/mDNSUNP.h | 8 +- mDNSResponder/mDNSShared/dns_sd.h | 62 +- mDNSResponder/mDNSShared/dns_sd_private.h | 8 +- mDNSResponder/mDNSShared/dnssd_clientlib.c | 16 +- mDNSResponder/mDNSShared/dnssd_clientstub.c | 1 - mDNSResponder/mDNSShared/mDNSDebug.c | 3 +- mDNSResponder/mDNSShared/uds_daemon.c | 114 +- mDNSResponder/unittests/mDNSCoreReceiveTest.c | 18 +- mDNSResponder/unittests/mdns_ut.c | 9 - mDNSResponder/unittests/unittest_common.h | 2 - 42 files changed, 6914 insertions(+), 1136 deletions(-) create mode 100644 mDNSResponder/mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist create mode 100644 mDNSResponder/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist delete mode 100644 mDNSResponder/mDNSMacOSX/Private/com.apple.mDNSResponder.plist create mode 100755 mDNSResponder/mDNSMacOSX/Scripts/bonjour-mcast-diagnose create mode 100755 mDNSResponder/mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump create mode 100644 mDNSResponder/mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist diff --git a/mDNSResponder/Clients/dns-sd.c b/mDNSResponder/Clients/dns-sd.c index 78c8f29f..5260b3f2 100644 --- a/mDNSResponder/Clients/dns-sd.c +++ b/mDNSResponder/Clients/dns-sd.c @@ -174,10 +174,6 @@ static const char kFilePathSep = '/'; #include "../mDNSShared/dnssd_clientstub.c" #endif -#if _DNS_SD_LIBDISPATCH -#include -#endif - //************************************************************************************************************* // Globals @@ -416,46 +412,36 @@ static unsigned int keytag(unsigned char *key, unsigned int keysize) return ac & 0xFFFF; } -static void base64Encode(char *buffer, int buflen, void *rdata, unsigned int rdlen) +// Base 64 encoding according to . +#define kBase64EncodingTable "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +static void base64Encode(char *buffer, size_t buflen, void *rdata, size_t rdlen) { -#if _DNS_SD_LIBDISPATCH - const void *result = NULL; - size_t size; - dispatch_data_t src_data = NULL, dest_data = NULL, null_str = NULL, data = NULL, map = NULL; - - src_data = dispatch_data_create(rdata, rdlen, dispatch_get_global_queue(0, 0), ^{}); - if (!src_data) - goto done; - - dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE, DISPATCH_DATA_FORMAT_TYPE_BASE64); - if (!dest_data) - goto done; - - null_str = dispatch_data_create("", 1, dispatch_get_global_queue(0, 0), ^{}); - if (!null_str) - goto done; - - data = dispatch_data_create_concat(dest_data, null_str); - if (!data) - goto done; - - map = dispatch_data_create_map(data, &result, &size); - if (!map) - goto done; - - snprintf(buffer, buflen, " %s", (char *)result); - -done: - if (src_data) dispatch_release(src_data); - if (dest_data) dispatch_release(dest_data); - if (data) dispatch_release(data); - if (null_str) dispatch_release(null_str); - if (map) dispatch_release(map); - return; -#else //_DNS_SD_LIBDISPATCH - snprintf(buffer, buflen, " %s", "."); - return; -#endif //_DNS_SD_LIBDISPATCH + const uint8_t *src = (const uint8_t *)rdata; + const uint8_t *const end = &src[rdlen]; + char *dst = buffer; + const char *lim; + + if (buflen == 0) return; + lim = &buffer[buflen - 1]; + while ((src < end) && (dst < lim)) + { + uint32_t i; + const size_t rem = (size_t)(end - src); + + // Form a 24-bit input group. If less than 24 bits remain, pad with zero bits. + if ( rem >= 3) i = (src[0] << 16) | (src[1] << 8) | src[2]; // 24 bits are equal to 4 6-bit groups. + else if (rem == 2) i = (src[0] << 16) | (src[1] << 8); // 16 bits are treated as 3 6-bit groups + 1 pad + else i = src[0] << 16; // 8 bits are treated as 2 6-bit groups + 2 pads + + // Encode each 6-bit group. + *dst++ = kBase64EncodingTable[(i >> 18) & 0x3F]; + if (dst < lim) *dst++ = kBase64EncodingTable[(i >> 12) & 0x3F]; + if (dst < lim) *dst++ = (rem >= 2) ? kBase64EncodingTable[(i >> 6) & 0x3F] : '='; + if (dst < lim) *dst++ = (rem >= 3) ? kBase64EncodingTable[ i & 0x3F] : '='; + src += (rem > 3) ? 3 : rem; + } + *dst = '\0'; } static DNSServiceProtocol GetProtocol(const char *s) @@ -924,9 +910,9 @@ static int snprintd(char *p, int max, const unsigned char **rd) return(p-buf); } -static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, char *p, unsigned const char *rd, uint16_t rdlen) +static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, size_t rdb_size, unsigned const char *rd, uint16_t rdlen) { - int rdb_size = 1000; + char *p = rdb; switch (rrtype) { case kDNSServiceType_DS: @@ -945,7 +931,7 @@ static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, char *p, unsigned con case kDNSServiceType_DNSKEY: { rdataDNSKey *rrkey = (rdataDNSKey *)rd; - p += snprintf(p, rdb + rdb_size - p, "%d %d %d %u", swap16(rrkey->flags), rrkey->proto, + p += snprintf(p, rdb + rdb_size - p, "%d %d %d %u ", swap16(rrkey->flags), rrkey->proto, rrkey->alg, (unsigned int)keytag((unsigned char *)rrkey, rdlen)); base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + DNSKEY_FIXED_SIZE), rdlen - DNSKEY_FIXED_SIZE); break; @@ -1027,6 +1013,11 @@ static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, char *p, unsigned con p += snprintd(p, rdb + rdb_size - p, &q); len = p - k + 1; + if ((&rdb[rdb_size] - p) >= 2) + { + *p++ = ' '; + *p = '\0'; + } base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + len + RRSIG_FIXED_SIZE), rdlen - (len + RRSIG_FIXED_SIZE)); break; } @@ -1058,7 +1049,7 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, if (operation == 'D') printf("Timestamp A/R if %-30s%-6s%-7s%-18s Rdata\n", "Name", "Type", "Class", "DNSSECStatus"); else - printf("Timestamp A/R Flags if %-30s%-6s%-7s Rdata\n", "Name", "Type", "Class"); + printf("Timestamp A/R Flags if %-30s%-6s%-7s Rdata\n", "Name", "Type", "Class"); } printtimestamp(); @@ -1115,7 +1106,7 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, case kDNSServiceType_DNSKEY: case kDNSServiceType_NSEC: case kDNSServiceType_RRSIG: - ParseDNSSECRecords(rrtype, rdb, p, rd, rdlen); + ParseDNSSECRecords(rrtype, rdb, sizeof(rdb), rd, rdlen); break; default: @@ -1143,7 +1134,7 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, if (operation == 'D') printf("%s%3d %-30s%-6s%-7s%-18s %s", op, ifIndex, fullname, rr_type, rr_class, dnssec_status, rdb); else - printf("%s%6X%3d %-30s%-7s%-6s %s", op, flags, ifIndex, fullname, rr_type, rr_class, rdb); + printf("%s%9X%3d %-30s%-7s%-6s %s", op, flags, ifIndex, fullname, rr_type, rr_class, rdb); if (unknowntype) { while (rd < end) @@ -1208,7 +1199,7 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags if (operation == 'g') printf("Timestamp A/R if %-25s %-44s %-18s\n", "Hostname", "Address", "DNSSECStatus"); else - printf("Timestamp A/R Flags if %-38s %-44s %s\n", "Hostname", "Address", "TTL"); + printf("Timestamp A/R Flags if %-38s %-44s %s\n", "Hostname", "Address", "TTL"); } printtimestamp(); @@ -1248,7 +1239,7 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags if (operation == 'g') printf("%s%3d %-25s %-44s %-18s", op, interfaceIndex, hostname, addr, dnssec_status); else - printf("%s%6X%3d %-38s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); + printf("%s%9X%3d %-38s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); if (errorCode) { if (errorCode == kDNSServiceErr_NoSuchRecord) @@ -1513,12 +1504,6 @@ static int API_string_limit_test() 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 various pointer parameters set to NULL:\n"); @@ -1916,6 +1901,14 @@ int main(int argc, char **argv) opinterface = kDNSServiceInterfaceIndexBLE; } + if (argc > 1 && !strcasecmp(argv[1], "-allowexpired")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsAllowExpiredAnswers; + printf("Setting kDNSServiceFlagsAllowExpiredAnswers\n"); + } + if (argc > 1 && !strcasecmp(argv[1], "-includep2p")) { argc--; diff --git a/mDNSResponder/Clients/dnssdutil.c b/mDNSResponder/Clients/dnssdutil.c index 1bde0daf..206e2b6c 100644 --- a/mDNSResponder/Clients/dnssdutil.c +++ b/mDNSResponder/Clients/dnssdutil.c @@ -6,10 +6,13 @@ #include // Include early. #include +#include #include #include #include #include +#include +#include #include #include #include @@ -20,10 +23,17 @@ #include #include +#include CF_RUNTIME_HEADER + #if( TARGET_OS_DARWIN ) + #include + #include + #include #include #include #include + #include + #include #include #endif @@ -36,17 +46,19 @@ #endif //=========================================================================================================================== -// Global Constants +// Versioning //=========================================================================================================================== -// Versioning - #define kDNSSDUtilNumVersion NumVersionBuild( 2, 0, 0, kVersionStageBeta, 0 ) #if( !MDNSRESPONDER_PROJECT && !defined( DNSSDUTIL_SOURCE_VERSION ) ) #define DNSSDUTIL_SOURCE_VERSION "0.0.0" #endif +//=========================================================================================================================== +// DNS-SD +//=========================================================================================================================== + // DNS-SD API flag descriptors #define kDNSServiceFlagsDescriptors \ @@ -90,19 +102,76 @@ "\x05" "TCP\0" \ "\x00" -// (m)DNS +#define kBadDNSServiceRef ( (DNSServiceRef)(intptr_t) -1 ) + +//=========================================================================================================================== +// DNS +//=========================================================================================================================== + +#define kDNSPort 53 +#define kDNSCompressionOffsetMax 0x3FFF +#define kDNSMaxUDPMessageSize 512 +#define kDNSMaxTCPMessageSize UINT16_MAX + +#define kDomainLabelLengthMax 63 +#define kDomainNameLengthMax 256 + +typedef struct +{ + uint8_t id[ 2 ]; + uint8_t flags[ 2 ]; + uint8_t questionCount[ 2 ]; + uint8_t answerCount[ 2 ]; + uint8_t authorityCount[ 2 ]; + uint8_t additionalCount[ 2 ]; + +} DNSHeader; + +#define kDNSHeaderLength 12 +check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength ); + +#define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id ) +#define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags ) +#define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount ) +#define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount ) +#define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount ) +#define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount ) + +#define DNSHeaderSetID( HDR, X ) WriteBig16( ( HDR )->id, (X) ) +#define DNSHeaderSetFlags( HDR, X ) WriteBig16( ( HDR )->flags, (X) ) +#define DNSHeaderSetQuestionCount( HDR, X ) WriteBig16( ( HDR )->questionCount, (X) ) +#define DNSHeaderSetAnswerCount( HDR, X ) WriteBig16( ( HDR )->answerCount, (X) ) +#define DNSHeaderSetAuthorityCount( HDR, X ) WriteBig16( ( HDR )->authorityCount, (X) ) +#define DNSHeaderSetAdditionalCount( HDR, X ) WriteBig16( ( HDR )->additionalCount, (X) ) + +// Single-bit DNS header fields + +#define kDNSHeaderFlag_Response ( 1 << 15 ) // QR (bit 15), Query (0)/Response (1) +#define kDNSHeaderFlag_AuthAnswer ( 1 << 10 ) // AA (bit 10), Authoritative Answer +#define kDNSHeaderFlag_Truncation ( 1 << 9 ) // TC (bit 9), TrunCation +#define kDNSHeaderFlag_RecursionDesired ( 1 << 8 ) // RD (bit 8), Recursion Desired +#define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 ) // RA (bit 7), Recursion Available +#define kDNSHeaderFlag_Z ( 1 << 6 ) // Z (bit 6), Reserved (must be zero) +#define kDNSHeaderFlag_AuthenticData ( 1 << 5 ) // AD (bit 5), Authentic Data (RFC 2535, Section 6) +#define kDNSHeaderFlag_CheckingDisabled ( 1 << 4 ) // CD (bit 4), Checking Disabled (RFC 2535, Section 6) -#define kDNSHeaderFlag_Response ( 1 << 15 ) -#define kDNSHeaderFlag_AuthAnswer ( 1 << 10 ) -#define kDNSHeaderFlag_Truncation ( 1 << 9 ) -#define kDNSHeaderFlag_RecursionDesired ( 1 << 8 ) -#define kDNSHeaderFlag_RecursionAvailable ( 1 << 7 ) +// OPCODE (bits 14-11), Operation Code -#define kDNSOpCode_Query 0 -#define kDNSOpCode_InverseQuery 1 -#define kDNSOpCode_Status 2 -#define kDNSOpCode_Notify 4 -#define kDNSOpCode_Update 5 +#define DNSFlagsGetOpCode( FLAGS ) ( ( (FLAGS) >> 11 ) & 0x0FU ) +#define DNSFlagsSetOpCode( FLAGS, OPCODE ) \ + do{ (FLAGS) = ( (FLAGS) & ~0x7800U ) | ( ( (OPCODE) & 0x0FU ) << 11 ); } while( 0 ) + +#define kDNSOpCode_Query 0 // QUERY (standard query) +#define kDNSOpCode_InverseQuery 1 // IQUERY (inverse query) +#define kDNSOpCode_Status 2 // STATUS +#define kDNSOpCode_Notify 4 // NOTIFY +#define kDNSOpCode_Update 5 // UPDATE + +// RCODE (bits 3-0), Response Code + +#define DNSFlagsGetRCode( FLAGS ) ( (FLAGS) & 0x0FU ) +#define DNSFlagsSetRCode( FLAGS, RCODE ) \ + do{ (FLAGS) = ( (FLAGS) & ~0x000FU ) | ( (RCODE) & 0x0FU ); } while( 0 ) #define kDNSRCode_NoError 0 #define kDNSRCode_FormatError 1 @@ -111,11 +180,66 @@ #define kDNSRCode_NotImplemented 4 #define kDNSRCode_Refused 5 -#define kQClassUnicastResponseBit ( 1U << 15 ) -#define kRRClassCacheFlushBit ( 1U << 15 ) +typedef struct +{ + uint8_t type[ 2 ]; + uint8_t class[ 2 ]; + +} DNSQuestionFixedFields; + +check_compile_time( sizeof( DNSQuestionFixedFields ) == 4 ); -#define kDomainLabelLengthMax 63 -#define kDomainNameLengthMax 256 +#define DNSQuestionFixedFieldsInit( FIELDS, QTYPE, QCLASS ) \ + do { WriteBig16( (FIELDS)->type, QTYPE ); WriteBig16( (FIELDS)->class, QCLASS ); } while( 0 ) + +#define DNSQuestionFixedFieldsGetType( FIELDS ) ReadBig16( (FIELDS)->type ) +#define DNSQuestionFixedFieldsGetClass( FIELDS ) ReadBig16( (FIELDS)->class ) + +typedef struct +{ + uint8_t type[ 2 ]; + uint8_t class[ 2 ]; + uint8_t ttl[ 4 ]; + uint8_t rdlength[ 2 ]; + +} DNSRecordFixedFields; + +check_compile_time( sizeof( DNSRecordFixedFields ) == 10 ); + +#define DNSRecordFixedFieldsInit( FIELDS, TYPE, CLASS, TTL, RDLENGTH ) \ + do \ + { \ + WriteBig16( (FIELDS)->type, TYPE ); \ + WriteBig16( (FIELDS)->class, CLASS ); \ + WriteBig32( (FIELDS)->ttl, TTL ); \ + WriteBig16( (FIELDS)->rdlength, RDLENGTH ); \ + \ + } while( 0 ) + +//=========================================================================================================================== +// mDNS +//=========================================================================================================================== + +#define kMDNSPort 5353 + +#define kDefaultMDNSMessageID 0 +#define kDefaultMDNSQueryFlags 0 + +#define kQClassUnicastResponseBit ( 1U << 15 ) +#define kRRClassCacheFlushBit ( 1U << 15 ) + +//=========================================================================================================================== +// Test DNS Server +//=========================================================================================================================== + +// IPv4 address block 203.0.113.0/24 (TEST-NET-3) is reserved for documentation. See . + +#define kTestDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0 + +// IPv6 address block 2001:db8::/32 is reserved for documentation. See . + +#define kTestDNSServerBaseAddrV6 \ + ( (const uint8_t *) "\x20\x01\x0D\xB8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" ) //=========================================================================================================================== // Gerneral Command Options @@ -153,6 +277,11 @@ #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \ StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL ) +#define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \ + CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, \ + (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP, \ + (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL ) + // DNS-SD API flag options static int gDNSSDFlags = 0; @@ -634,7 +763,6 @@ static CLIOption kPortMappingOpts[] = static const char * gBrowseAll_Domain = NULL; static char ** gBrowseAll_ServiceTypes = NULL; static size_t gBrowseAll_ServiceTypesCount = 0; -static int gBrowseAll_IncludeAWDL = false; static int gBrowseAll_BrowseTimeSecs = 5; static int gBrowseAll_MaxConnectTimeSecs = 0; @@ -653,6 +781,36 @@ static CLIOption kBrowseAllOpts[] = CLI_OPTION_END() }; +//=========================================================================================================================== +// GetNameInfo Command Options +//=========================================================================================================================== + +static void GetNameInfoCmd( void ); + +static char * gGetNameInfo_IPAddress = NULL; +static int gGetNameInfoFlag_DGram = false; +static int gGetNameInfoFlag_NameReqd = false; +static int gGetNameInfoFlag_NoFQDN = false; +static int gGetNameInfoFlag_NumericHost = false; +static int gGetNameInfoFlag_NumericScope = false; +static int gGetNameInfoFlag_NumericServ = false; + +static CLIOption kGetNameInfoOpts[] = +{ + StringOption( 'a', "address", &gGetNameInfo_IPAddress, "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ), + + CLI_OPTION_GROUP( "Flags" ), + BooleanOption( 0 , "flag-dgram", &gGetNameInfoFlag_DGram, "Use NI_DGRAM flag." ), + BooleanOption( 0 , "flag-namereqd", &gGetNameInfoFlag_NameReqd, "Use NI_NAMEREQD flag." ), + BooleanOption( 0 , "flag-nofqdn", &gGetNameInfoFlag_NoFQDN, "Use NI_NOFQDN flag." ), + BooleanOption( 0 , "flag-numerichost", &gGetNameInfoFlag_NumericHost, "Use NI_NUMERICHOST flag." ), + BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ), + BooleanOption( 0 , "flag-numericserv", &gGetNameInfoFlag_NumericServ, "Use NI_NUMERICSERV flag." ), + + CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ), + CLI_OPTION_END() +}; + //=========================================================================================================================== // GetAddrInfoStress Command Options //=========================================================================================================================== @@ -781,6 +939,298 @@ static CLIOption kPIDToUUIDOpts[] = CLI_OPTION_END() }; +//=========================================================================================================================== +// DNSServer Command Options +//=========================================================================================================================== + +#define kDNSServerInfoText_Intro \ + "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n" \ + "presence of special labels in the query's QNAME. There are currently seven types of special labels that can be\n" \ + "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n" \ + "IPv4 label, and the IPv6 label.\n" + +#define kDNSServerInfoText_NameExistence \ + "A name is considered to exist if and only if it ends in d.test., and the other labels, if\n" \ + "any, consist of\n" \ + "\n" \ + " 1. at most one Alias or Alias-TTL label as the first label;\n" \ + " 2. at most one Count label;\n" \ + " 3. zero or more Tag labels;\n" \ + " 4. at most one TTL label; and\n" \ + " 5. at most one IPv4 or IPv6 label.\n" + +#define kDNSServerInfoText_ResourceRecords \ + "Currently, the server only provides CNAME, A, and AAAA records.\n" \ + "\n" \ + "Names that exist and begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n" \ + "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n" \ + "\n" \ + "Names that exist and have an IPv4 label have at least one A record, but no AAAA records. Names that exist and\n" \ + "have an IPv6 label, have at least one AAAA record, but no A records. All other names that exist have at least\n" \ + "one A record and at least one AAAA record. See \"Count Labels\" for how the number of address records for a\n" \ + "given name is determined.\n" \ + "\n" \ + "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n" \ + "2001:db8::/32 block. Both of these address blocks are reserved for documentation.\n" \ + "See and .\n" \ + "\n" \ + "Unless otherwise specified, all resource records will use a default TTL. The default TTL can be set with the\n" \ + "--defaultTTL option. See \"Alias-TTL Labels\" and \"TTL Labels\" for details on how to query for records with\n" \ + "specific TTL values.\n" + +#define kDNSServerInfoText_AliasLabel \ + "Alias labels are of the form \"alias\" or \"alias-N\", where N is an integer in [2 .. 2^31 - 1].\n" \ + "\n" \ + "If QNAME exist and its first label is Alias label \"alias-N\", then the response will contain exactly N CNAME\n" \ + "records:\n" \ + "\n" \ + " 1. For each i in [3 .. N], the response will contain a CNAME record whose name is identical to QNAME,\n" \ + " except that the first label is \"alias-i\" instead, and whose RDATA is the name of the other CNAME\n" \ + " record whose name has \"alias-(i - 1)\" as its first label.\n" \ + "\n" \ + " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \ + " is \"alias-2\" instead, and whose RDATA is the name identical to QNAME, except that the first label is\n" \ + " \"alias\" instead.\n" \ + "\n" \ + " 3. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \ + " is \"alias\" instead, and whose RDATA is the name identical to QNAME stripped of its first label.\n" \ + "\n" \ + "If QNAME exist and its first label is Alias label \"alias\", then the response will contain a single CNAME\n" \ + "record. The CNAME record's name will be equal to QNAME and its RDATA will be the name identical to QNAME\n" \ + "stripped of its first label.\n" \ + "\n" \ + "Example. A response to a query with a QNAME of alias-3.count-5.d.test will contain the following CNAME\n" \ + "records:\n" \ + "\n" \ + " alias-4.count-5.d.test. 60 IN CNAME alias-3.count-5.d.test.\n" \ + " alias-3.count-5.d.test. 60 IN CNAME alias-2.count-5.d.test.\n" \ + " alias-2.count-5.d.test. 60 IN CNAME alias.count-5.d.test.\n" \ + " alias.count-5.d.test. 60 IN CNAME count-5.d.test.\n" + +#define kDNSServerInfoText_AliasTTLLabel \ + "Alias-TTL labels are of the form \"alias-ttl-T_1[-T_2[...-T_N]]\", where each T_i is an integer in\n" \ + "[0 .. 2^31 - 1] and N is a positive integer bounded by the size of the maximum legal label length (63 octets).\n" \ + "\n" \ + "If QNAME exists and its first label is Alias-TTL label \"alias-ttl-T_1...-T_N\", then the response will contain\n" \ + "exactly N CNAME records:\n" \ + "\n" \ + " 1. For each i in [1 .. N - 1], the response will contain a CNAME record whose name is identical to QNAME,\n" \ + " except that the first label is \"alias-ttl-T_i...-T_N\" instead, whose TTL value is T_i, and whose RDATA\n" \ + " is the name of the other CNAME record whose name has \"alias-ttl-T_(i+1)...-T_N\" as its first label.\n" \ + "\n" \ + " 2. The response will contain a CNAME record whose name is identical to QNAME, except that the first label\n" \ + " is \"alias-ttl-T_N\", whose TTL is T_N, and whose RDATA is identical to QNAME stripped of its first\n" \ + " label.\n" \ + "\n" \ + "Example. A response to a query with a QNAME of alias-ttl-20-40-80.count-5.d.test will contain the following\n" \ + "CNAME records:\n" \ + "\n" \ + " alias-ttl-20-40-80.count-5.d.test. 20 IN CNAME alias-ttl-40-80.count-5.d.test.\n" \ + " alias-ttl-40-80.count-5.d.test. 40 IN CNAME alias-ttl-80.count-5.d.test.\n" \ + " alias-ttl-80.count-5.d.test. 80 IN CNAME count-5.d.test.\n" + +#define kDNSServerInfoText_CountLabel \ + "Count labels are of the form \"count-N_1\" or \"count-N_1-N_2\", where N_1 is an integer in [1 .. 255] and N_2\n" \ + "is an integer in [N_1 .. 255].\n" \ + "\n" \ + "If QNAME exists, contains Count label \"count-N\", and has the type of address records specified by QTYPE, then\n" \ + "the response will contain exactly N address records:\n" \ + "\n" \ + " 1. For i in [1 .. N], the response will contain an address record of type QTYPE whose name is equal to\n" \ + " QNAME and whose RDATA is an address equal to a constant base address + i.\n" \ + "\n" \ + " 2. The address records will be ordered by the address contained in RDATA in ascending order.\n" \ + "\n" \ + "Example. A response to an A record query with a QNAME of alias.count-3.d.test will contain the following A\n" \ + "records:\n" \ + "\n" \ + " count-3.d.test. 60 IN A 203.0.113.1\n" \ + " count-3.d.test. 60 IN A 203.0.113.2\n" \ + " count-3.d.test. 60 IN A 203.0.113.3\n" \ + "\n" \ + "If QNAME exists, contains Count label \"count-N_1-N_2\", and has the type of address records specified by\n" \ + "QTYPE, then the response will contain exactly N_1 address records:\n" \ + "\n" \ + " 1. Each of the address records will be of type QTYPE, have name equal to QNAME, and have as its RDATA a\n" \ + " unique address equal to a constant base address + i, where i is a randomly chosen integer in [1 .. N_2].\n" \ + "\n" \ + " 2. The order of the address records will be random.\n" \ + "\n" \ + "Example. A response to a AAAA record query with a QNAME of count-3-100.ttl-20.d.test could contain the\n" \ + "following AAAA records:\n" \ + "\n" \ + " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::c\n" \ + " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::3a\n" \ + " count-3-100.ttl-20.d.test. 20 IN AAAA 2001:db8::4f\n" \ + "\n" \ + "If QNAME exists, but doesn't have the type of address records specified by QTYPE, then the response will\n" \ + "contain no address records, regardless of whether it contains a Count label.\n" \ + "\n" \ + "QNAMEs that exist, but don't have a Count label are treated as though they contain a count label equal to\n" \ + "\"count-1\".\n" + +#define kDNSServerInfoText_TagLabel \ + "Tag labels are labels prefixed with \"tag-\" and contain zero or more arbitrary octets after the prefix.\n" \ + "\n" \ + "This type of label exists to allow testers to \"uniquify\" domain names. Tag labels can also serve as padding\n" \ + "to increase the sizes of domain names.\n" + +#define kDNSServerInfoText_TTLLabel \ + "TTL labels are of the form \"ttl-T\", where T is an integer in [0 .. 2^31 - 1].\n" \ + "\n" \ + "If the name specified by QNAME exists, and contains TTL label \"ttl-T\", then all non-CNAME records contained\n" \ + "in the response will have a TTL value equal to T.\n" + +#define kDNSServerInfoText_IPv4Label \ + "The IPv4 label is \"ipv4\". See \"Resource Records\" for the affect of this label.\n" + +#define kDNSServerInfoText_IPv6Label \ + "The IPv6 label is \"ipv6\". See \"Resource Records\" for the affect of this label.\n" + +#define kDNSServerDefaultTTL 60 + +static int gDNSServer_LoopbackOnly = false; +static int gDNSServer_Foreground = false; +static int gDNSServer_ResponseDelayMs = 0; +static int gDNSServer_DefaultTTL = kDNSServerDefaultTTL; +#if( TARGET_OS_DARWIN ) +static const char * gDNSServer_FollowPID = NULL; +#endif + +static CLIOption kDNSServerOpts[] = +{ + BooleanOption( 'l', "loopback", &gDNSServer_LoopbackOnly, "Bind to to the loopback interface." ), + BooleanOption( 'f', "foreground", &gDNSServer_Foreground, "Directlog output to stdout instead of system logging." ), + IntegerOption( 'd', "responseDelay", &gDNSServer_ResponseDelayMs, "ms", "The amount of additional delay in milliseconds to apply to responses. (default: 0)", false ), + IntegerOption( 0 , "defaultTTL", &gDNSServer_DefaultTTL, "seconds", "Resource record TTL value to use when unspecified. (default: 60)", false ), +#if( TARGET_OS_DARWIN ) + StringOption( 0 , "followPID", &gDNSServer_FollowPID, "pid", "Exit when the process (usually the parent proccess) specified by PID exits.", false ), +#endif + + CLI_SECTION( "Intro", kDNSServerInfoText_Intro ), + CLI_SECTION( "Name Existence", kDNSServerInfoText_NameExistence ), + CLI_SECTION( "Resource Records", kDNSServerInfoText_ResourceRecords ), + CLI_SECTION( "Alias Labels", kDNSServerInfoText_AliasLabel ), + CLI_SECTION( "Alias-TTL Labels", kDNSServerInfoText_AliasTTLLabel ), + CLI_SECTION( "Count Labels", kDNSServerInfoText_CountLabel ), + CLI_SECTION( "Tag Labels", kDNSServerInfoText_TagLabel ), + CLI_SECTION( "TTL Labels", kDNSServerInfoText_TTLLabel ), + CLI_SECTION( "IPv4 Label", kDNSServerInfoText_IPv4Label ), + CLI_SECTION( "IPv6 Label", kDNSServerInfoText_IPv6Label ), + CLI_OPTION_END() +}; + +static void DNSServerCmd( void ); + +//=========================================================================================================================== +// Test Command Options +//=========================================================================================================================== + +static const char * gGAIPerf_TestSuite = NULL; +static int gGAIPerf_CallDelayMs = 10; +static int gGAIPerf_ServerDelayMs = 10; +static int gGAIPerf_DefaultIterCount = 100; +static const char * gGAIPerf_OutputFilePath = NULL; +static const char * gGAIPerf_OutputFormat = "json"; +static int gGAIPerf_OutputAppendNewLine = false; + +static void GAIPerfCmd( void ); + +#define kGAIPerfSectionTitle_TestSuiteBasic "Test Suite \"Basic\"" +#define kGAIPerfSectionText_TestSuiteBasic \ + "This test suite consists of the following three test cases:\n" \ + "\n" \ + "Test Case #1: Resolve a domain name with\n" \ + "\n" \ + " 2 CNAME records, 4 A records, and 4 AAAA records\n" \ + "\n" \ + "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \ + "server queries.\n" \ + "\n" \ + "Test Case #2: Resolve a domain name with\n" \ + "\n" \ + " 2 CNAME records, 4 A records, and 4 AAAA records\n" \ + "\n" \ + "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \ + "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \ + "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \ + "\n" \ + "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \ + "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \ + "domain name in the preliminary iteration isn't counted in the performance stats.\n" \ + "\n" \ + "Test Case #3: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n" + +#define kGAIPerfSectionTitle_TestSuiteAdvanced "Test Suite \"Advanced\"" +#define kGAIPerfSectionText_TestSuiteAdvanced \ + "This test suite consists of 33 test cases. Test cases 1 through 32 can be described in the following way\n" \ + "\n" \ + "Test Case #N (where N is in [1, 32] and odd): Resolve a domain name with\n" \ + "\n" \ + " N_c CNAME records, N_a A records, and N_a AAAA records\n" \ + "\n" \ + "to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which requires\n" \ + "server queries.\n" \ + "\n" \ + "Test Case #N (where N is in [1, 32] and even): Resolve a domain name with\n" \ + "\n" \ + " N_c CNAME records, N_a A records, and N_a AAAA records\n" \ + "\n" \ + "to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which\n" \ + "requires server queries. Each subsequent iteration resolves the same domain name as the preliminary iteration,\n" \ + "which should ideally require no additional server queries, i.e., the results should come from the cache.\n" \ + "\n" \ + "Unlike the preceding test case, this test case is concerned with DNSServiceGetAddrInfo() performance when the\n" \ + "records of the domain name being resolved are already in the cache. Therefore, the time required to resolve the\n" \ + "domain name in the preliminary iteration isn't counted in the performance stats.\n" \ + "\n" \ + "N_c and N_a take on the following values, depending on the value of N:\n" \ + "\n" \ + " N_c is 0 if N is in [1, 8].\n" \ + " N_c is 1 if N is in [9, 16].\n" \ + " N_c is 2 if N is in [17, 24].\n" \ + " N_c is 4 if N is in [25, 32].\n" \ + "\n" \ + " N_a is 1 if N mod 8 is 1 or 2.\n" \ + " N_a is 2 if N mod 8 is 3 or 4.\n" \ + " N_a is 4 if N mod 8 is 5 or 6.\n" \ + " N_a is 8 if N mod 8 is 7 or 0.\n" \ + "\n" \ + "Finally,\n" \ + "\n" \ + "Test Case #33: Each iteration resolves localhost to its IPv4 and IPv6 addresses.\n" + +static CLIOption kGAIPerfOpts[] = +{ + StringOptionEx( 's', "suite", &gGAIPerf_TestSuite, "name", "Name of the predefined test suite to run.", true, + "\n" + "There are currently two predefined test suites, 'basic' and 'advanced', which are described below.\n" + "\n" + ), + StringOption( 'o', "output", &gGAIPerf_OutputFilePath, "path", "Path of the file to write test results to instead of standard output (stdout).", false ), + StringOptionEx( 'f', "format", &gGAIPerf_OutputFormat, "format", "Specifies the test results output format. (default: json)", false, + "\n" + "Use 'json' for JavaScript Object Notation (JSON).\n" + "Use 'xml' for property list XML version 1.0.\n" + "Use 'binary' for property list binary version 1.0.\n" + "\n" + ), + BooleanOption( 'n', "appendNewline", &gGAIPerf_OutputAppendNewLine, "If the output format is JSON, output a trailing newline character." ), + IntegerOption( 0 , "callDelay", &gGAIPerf_CallDelayMs, "ms", "Time to wait before calling DNSServiceGetAddrInfo() in milliseconds. (default: 10)", false ), + IntegerOption( 0 , "responseDelay", &gGAIPerf_ServerDelayMs, "ms", "Additional delay in milliseconds to have the test DNS server apply to responses. (default: 0)", false ), + IntegerOption( 'i', "iterations", &gGAIPerf_DefaultIterCount, "count", "The default number of test case iterations. (default: 100)", false ), + + CLI_SECTION( kGAIPerfSectionTitle_TestSuiteBasic, kGAIPerfSectionText_TestSuiteBasic ), + CLI_SECTION( kGAIPerfSectionTitle_TestSuiteAdvanced, kGAIPerfSectionText_TestSuiteAdvanced ), + CLI_OPTION_END() +}; + +static CLIOption kTestOpts[] = +{ + Command( "gaiperf", GAIPerfCmd, kGAIPerfOpts, "Run DNSServiceGetAddrInfo() performance tests.", false ), + CLI_OPTION_END() +}; + //=========================================================================================================================== // SSDP Command Options //=========================================================================================================================== @@ -812,10 +1262,13 @@ static CLIOption kSSDPOpts[] = CLI_OPTION_END() }; +#if( TARGET_OS_DARWIN ) //=========================================================================================================================== // res_query Command Options //=========================================================================================================================== +static void ResQueryCmd( void ); + static const char * gResQuery_Name = NULL; static const char * gResQuery_Type = NULL; static const char * gResQuery_Class = NULL; @@ -834,6 +1287,8 @@ static CLIOption kResQueryOpts[] = // dns_query Command Options //=========================================================================================================================== +static void ResolvDNSQueryCmd( void ); + static const char * gResolvDNSQuery_Name = NULL; static const char * gResolvDNSQuery_Type = NULL; static const char * gResolvDNSQuery_Class = NULL; @@ -848,6 +1303,78 @@ static CLIOption kResolvDNSQueryOpts[] = CLI_OPTION_END() }; +//=========================================================================================================================== +// CFHost Command Options +//=========================================================================================================================== + +static void CFHostCmd( void ); + +static const char * gCFHost_Name = NULL; +static int gCFHost_WaitSecs = 0; + +static CLIOption kCFHostOpts[] = +{ + StringOption( 'n', "name", &gCFHost_Name, "hostname", "Hostname to resolve.", true ), + IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds", "Time in seconds to wait before a normal exit. (default: 0)", false ), + CLI_OPTION_END() +}; + +static CLIOption kLegacyOpts[] = +{ + Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ), + Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ), + Command( "cfhost", CFHostCmd, kCFHostOpts, "Uses CFHost to resolve a hostname.", true ), + CLI_OPTION_END() +}; + +//=========================================================================================================================== +// DNSConfigAdd Command Options +//=========================================================================================================================== + +static void DNSConfigAddCmd( void ); + +static CFStringRef gDNSConfigAdd_ID = NULL; +static char ** gDNSConfigAdd_IPAddrArray = NULL; +static size_t gDNSConfigAdd_IPAddrCount = 0; +static char ** gDNSConfigAdd_DomainArray = NULL; +static size_t gDNSConfigAdd_DomainCount = 0; +static const char * gDNSConfigAdd_Interface = NULL; + +static CLIOption kDNSConfigAddOpts[] = +{ + CFStringOption( 0 , "id", &gDNSConfigAdd_ID, "ID", "Arbitrary ID to use for resolver entry.", true ), + MultiStringOption( 'a', "address", &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ), + MultiStringOption( 'd', "domain", &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ), + StringOption( 'i', "interface", &gDNSConfigAdd_Interface, "interface name", "Specific interface for the resolver entry.", false ), + + CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ), + CLI_OPTION_END() +}; + +//=========================================================================================================================== +// DNSConfigRemove Command Options +//=========================================================================================================================== + +static void DNSConfigRemoveCmd( void ); + +static CFStringRef gDNSConfigRemove_ID = NULL; + +static CLIOption kDNSConfigRemoveOpts[] = +{ + CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ), + + CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ), + CLI_OPTION_END() +}; + +static CLIOption kDNSConfigOpts[] = +{ + Command( "add", DNSConfigAddCmd, kDNSConfigAddOpts, "Add a supplemental resolver entry to the system's DNS configuration.", true ), + Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ), + CLI_OPTION_END() +}; +#endif // TARGET_OS_DARWIN + //=========================================================================================================================== // Command Table //=========================================================================================================================== @@ -872,10 +1399,6 @@ static void DNSCryptCmd( void ); #endif static void MDNSQueryCmd( void ); static void PIDToUUIDCmd( void ); -#if( TARGET_OS_DARWIN ) -static void ResQueryCmd( void ); -static void ResolvDNSQueryCmd( void ); -#endif static void DaemonVersionCmd( void ); static CLIOption kGlobalOpts[] = @@ -900,6 +1423,7 @@ static CLIOption kGlobalOpts[] = // Uncommon commands. + Command( "getnameinfo", GetNameInfoCmd, kGetNameInfoOpts, "Calls getnameinfo() and prints results.", true ), Command( "getAddrInfoStress", GetAddrInfoStressCmd, kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ), Command( "DNSQuery", DNSQueryCmd, kDNSQueryOpts, "Crafts and sends a DNS query.", true ), #if( DNSSDUTIL_INCLUDE_DNSCRYPT ) @@ -907,10 +1431,12 @@ static CLIOption kGlobalOpts[] = #endif Command( "mDNSQuery", MDNSQueryCmd, kMDNSQueryOpts, "Crafts and sends an mDNS query over the specified interface.", true ), Command( "pid2uuid", PIDToUUIDCmd, kPIDToUUIDOpts, "Prints the UUID of a process.", true ), - Command( "ssdp", NULL, kSSDPOpts, "Commands for testing with Simple Service Discovery Protocol (SSDP).", true ), + Command( "server", DNSServerCmd, kDNSServerOpts, "DNS server for testing.", true ), + Command( "test", NULL, kTestOpts, "Commands for testing DNS-SD.", true ), + Command( "ssdp", NULL, kSSDPOpts, "Commands for testing Simple Service Discovery Protocol (SSDP).", true ), #if( TARGET_OS_DARWIN ) - Command( "res_query", ResQueryCmd, kResQueryOpts, "Uses res_query() from either libresolv or libinfo to query for a record.", true ), - Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ), + Command( "legacy", NULL, kLegacyOpts, "Commands for legacy non-DNS-SD API.", true ), + Command( "dnsconfig", NULL, kDNSConfigOpts, "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ), #endif Command( "daemonVersion", DaemonVersionCmd, NULL, "Prints the version of the DNS-SD daemon.", true ), @@ -930,9 +1456,19 @@ static CLIOption kGlobalOpts[] = static void Exit( void *inContext ) ATTRIBUTE_NORETURN; -#define kTimestampBufLen 27 +static int + PrintFTimestampHandler( + PrintFContext * inContext, + PrintFFormat * inFormat, + PrintFVAList * inArgs, + void * inUserContext ); +static int + PrintFDNSMessageHandler( + PrintFContext * inContext, + PrintFFormat * inFormat, + PrintFVAList * inArgs, + void * inUserContext ); -static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] ); static DNSServiceFlags GetDNSSDFlagsFromOpts( void ); typedef enum @@ -972,29 +1508,6 @@ static OSStatus RecordClassFromArgString( const char *inString, uint16_t *outV static char * InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] ); static const char * RecordTypeToString( unsigned int inValue ); -// DNS message helpers - -typedef struct -{ - uint8_t id[ 2 ]; - uint8_t flags[ 2 ]; - uint8_t questionCount[ 2 ]; - uint8_t answerCount[ 2 ]; - uint8_t authorityCount[ 2 ]; - uint8_t additionalCount[ 2 ]; - -} DNSHeader; - -#define kDNSHeaderLength 12 -check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength ); - -#define DNSHeaderGetID( HDR ) ReadBig16( ( HDR )->id ) -#define DNSHeaderGetFlags( HDR ) ReadBig16( ( HDR )->flags ) -#define DNSHeaderGetQuestionCount( HDR ) ReadBig16( ( HDR )->questionCount ) -#define DNSHeaderGetAnswerCount( HDR ) ReadBig16( ( HDR )->answerCount ) -#define DNSHeaderGetAuthorityCount( HDR ) ReadBig16( ( HDR )->authorityCount ) -#define DNSHeaderGetAdditionalCount( HDR ) ReadBig16( ( HDR )->additionalCount ) - static OSStatus DNSMessageExtractDomainName( const uint8_t * inMsgPtr, @@ -1036,6 +1549,7 @@ static OSStatus const char * inString, uint8_t ** outEndPtr ); static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 ); +static size_t DomainNameLength( const uint8_t *inName ); static OSStatus DomainNameFromString( uint8_t inDomainName[ kDomainNameLengthMax ], @@ -1048,10 +1562,13 @@ static OSStatus char inBuf[ kDNSServiceMaxDomainName ], const uint8_t ** outNextPtr ); -static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, Boolean inIsMDNS, Boolean inPrintRaw ); - -#define PrintMDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, true, RAW ) -#define PrintUDNSMessage( MSGPTR, MSGLEN, RAW ) PrintDNSMessage( MSGPTR, MSGLEN, false, RAW ) +static OSStatus + DNSMessageToText( + const uint8_t * inMsgPtr, + size_t inMsgLen, + Boolean inIsMDNS, + Boolean inPrintRaw, + char ** outText ); #define kDNSQueryMessageMaxLen ( kDNSHeaderLength + kDomainNameLengthMax + 4 ) @@ -1076,37 +1593,82 @@ static OSStatus void * inContext, dispatch_source_t * outSource ); static OSStatus - DispatchReadSourceCreate( - SocketRef inSock, - DispatchHandler inEventHandler, - DispatchHandler inCancelHandler, - void * inContext, - dispatch_source_t * outSource ); + DispatchSocketSourceCreate( + SocketRef inSock, + dispatch_source_type_t inType, + dispatch_queue_t inQueue, + DispatchHandler inEventHandler, + DispatchHandler inCancelHandler, + void * inContext, + dispatch_source_t * outSource ); + +#define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \ + DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) + +#define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \ + DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) + static OSStatus DispatchTimerCreate( dispatch_time_t inStart, uint64_t inIntervalNs, uint64_t inLeewayNs, + dispatch_queue_t inQueue, DispatchHandler inEventHandler, DispatchHandler inCancelHandler, void * inContext, dispatch_source_t * outTimer ); +static OSStatus + DispatchProcessMonitorCreate( + pid_t inPID, + unsigned long inFlags, + dispatch_queue_t inQueue, + DispatchHandler inEventHandler, + DispatchHandler inCancelHandler, + void * inContext, + dispatch_source_t * outMonitor ); static const char * ServiceTypeDescription( const char *inName ); typedef struct { - SocketRef sock; - void * context; + SocketRef sock; // Socket. + void * userContext; // User context. + int32_t refCount; // Reference count. } SocketContext; -static void SocketContextCancelHandler( void *inContext ); +static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext ); +static SocketContext * SocketContextRetain( SocketContext *inContext ); +static void SocketContextRelease( SocketContext *inContext ); +static void SocketContextCancelHandler( void *inContext ); + +#define ForgetSocketContext( X ) ForgetCustom( X, SocketContextRelease ) + static OSStatus StringToInt32( const char *inString, int32_t *outValue ); static OSStatus StringToUInt32( const char *inString, uint32_t *outValue ); +static OSStatus StringToLongLong( const char *inString, long long *outValue ); +static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen ); +static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen ); +static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen ); #if( TARGET_OS_DARWIN ) static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr ); #endif +static OSStatus + _ServerSocketOpenEx2( + int inFamily, + int inType, + int inProtocol, + const void * inAddr, + int inPort, + int * outPort, + int inRcvBufSize, + Boolean inNoPortReuse, + SocketRef * outSock ); + +typedef uint64_t MicroTime64; + +static MicroTime64 GetCurrentMicroTime( void ); // Gets the number of milliseconds since 1970-01-01T00:00:00Z #define AddRmvString( X ) ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" ) #define Unused( X ) (void)(X) @@ -1117,12 +1679,17 @@ static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr ); int main( int argc, const char **argv ) { + OSStatus err; + // Route DebugServices logging output to stderr. dlog_control( "DebugServices:output=file;stderr" ); + PrintFRegisterExtension( "du:time", PrintFTimestampHandler, NULL ); + PrintFRegisterExtension( "du:dnsmsg", PrintFDNSMessageHandler, NULL ); CLIInit( argc, argv ); - CLIParse( kGlobalOpts, kCLIFlags_None ); + err = CLIParse( kGlobalOpts, kCLIFlags_None ); + if( err ) exit( 1 ); return( gExitCode ); } @@ -1299,7 +1866,7 @@ static void BrowseCmd( void ) { DNSServiceRef sdRef; - if( useMainConnection ) sdRef = context->mainRef; + sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef; err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain, BrowseCallback, context ); require_noerr( err, exit ); @@ -1334,23 +1901,22 @@ exit: static void BrowsePrintPrologue( const BrowseContext *inContext ) { const int timeLimitSecs = inContext->timeLimitSecs; - const char * const * serviceType = (const char **) inContext->serviceTypes; + const char * const * ptr = (const char **) inContext->serviceTypes; const char * const * const end = (const char **) inContext->serviceTypes + inContext->serviceTypesCount; - char time[ kTimestampBufLen ]; char ifName[ kInterfaceNameBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); - FPrintF( stdout, "Service types: %s", *serviceType++ ); - while( serviceType < end ) FPrintF( stdout, ", %s", *serviceType++ ); + FPrintF( stdout, "Service types: %s", *ptr++ ); + while( ptr < end ) FPrintF( stdout, ", %s", *ptr++ ); FPrintF( stdout, "\n" ); FPrintF( stdout, "Domain: %s\n", inContext->domain ? inContext->domain : " (default domains)" ); FPrintF( stdout, "Time limit: " ); if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); else FPrintF( stdout, "∞\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -1433,11 +1999,11 @@ static void DNSSD_API BrowseResolveOp * newOp = NULL; BrowseResolveOp ** p; char fullName[ kDNSServiceMaxDomainName ]; - char time[ kTimestampBufLen ]; + struct timeval now; Unused( inSDRef ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); err = inError; require_noerr( err, exit ); @@ -1447,8 +2013,8 @@ static void DNSSD_API FPrintF( stdout, "%-26s A/R Flags IF %-20s %-20s Instance Name\n", "Timestamp", "Domain", "Service Type" ); context->printedHeader = true; } - FPrintF( stdout, "%-26s %-3s %5X %2d %-20s %-20s %s\n", - time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName ); + FPrintF( stdout, "%{du:time} %-3s %5X %2d %-20s %-20s %s\n", + &now, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inDomain, inRegType, inName ); if( !context->doResolve && !context->doResolveTXTOnly ) goto exit; @@ -1536,22 +2102,22 @@ static void DNSSD_API uint32_t inTTL, void * inContext ) { - OSStatus err; - char time[ kTimestampBufLen ]; + OSStatus err; + struct timeval now; Unused( inSDRef ); Unused( inClass ); Unused( inTTL ); Unused( inContext ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); err = inError; require_noerr( err, exit ); require_action( inType == kDNSServiceType_TXT, exit, err = kTypeErr ); - FPrintF( stdout, "%s %s %s TXT on interface %d\n TXT: %#{txt}\n", - time, AddRmvString( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr, (size_t) inRDataLen ); + FPrintF( stdout, "%{du:time} %s %s TXT on interface %d\n TXT: %#{txt}\n", + &now, AddRmvString( inFlags ), inFullName, (int32_t) inInterfaceIndex, inRDataPtr, (size_t) inRDataLen ); exit: if( err ) exit( 1 ); @@ -1574,19 +2140,19 @@ static void DNSSD_API const unsigned char * inTXTPtr, void * inContext ) { - char time[ kTimestampBufLen ]; - char errorStr[ 64 ]; + struct timeval now; + char errorStr[ 64 ]; Unused( inSDRef ); Unused( inFlags ); Unused( inContext ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError ); - FPrintF( stdout, "%s %s can be reached at %s:%u (interface %d)%?s\n", - time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr ); + FPrintF( stdout, "%{du:time} %s can be reached at %s:%u (interface %d)%?s\n", + &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr ); if( inTXTLen == 1 ) { FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX ); @@ -1701,7 +2267,7 @@ static void GetAddrInfoCmd( void ) // Start operation. - if( useMainConnection ) sdRef = context->mainRef; + sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef; err = DNSServiceGetAddrInfo( &sdRef, context->flags, context->ifIndex, context->protocols, context->name, GetAddrInfoCallback, context ); require_noerr( err, exit ); @@ -1736,19 +2302,18 @@ static void GetAddrInfoPrintPrologue( const GetAddrInfoContext *inContext ) { const int timeLimitSecs = inContext->timeLimitSecs; char ifName[ kInterfaceNameBufLen ]; - char time[ kTimestampBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); - FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); - FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); - FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors ); - FPrintF( stdout, "Name: %s\n", inContext->name ); - FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" ); + FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); + FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); + FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors ); + FPrintF( stdout, "Name: %s\n", inContext->name ); + FPrintF( stdout, "Mode: %s\n", inContext->oneShotMode ? "one-shot" : "continuous" ); FPrintF( stdout, "Time limit: " ); if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); else FPrintF( stdout, "∞\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -1779,14 +2344,14 @@ static void DNSSD_API void * inContext ) { GetAddrInfoContext * const context = (GetAddrInfoContext *) inContext; + struct timeval now; OSStatus err; const char * addrStr; char addrStrBuf[ kSockAddrStringMaxSize ]; - char time[ kTimestampBufLen ]; Unused( inSDRef ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); switch( inError ) { @@ -1826,8 +2391,8 @@ static void DNSSD_API FPrintF( stdout, "%-26s A/R Flags IF %-32s %-38s %6s\n", "Timestamp", "Hostname", "Address", "TTL" ); context->printedHeader = true; } - FPrintF( stdout, "%-26s %s %5X %2d %-32s %-38s %6u\n", - time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL ); + FPrintF( stdout, "%{du:time} %s %5X %2d %-32s %-38s %6u\n", + &now, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inHostname, addrStr, inTTL ); if( context->oneShotMode ) { @@ -1952,7 +2517,7 @@ static void QueryRecordCmd( void ) // Start operation. - if( useMainConnection ) sdRef = context->mainRef; + sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef; err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType, kDNSServiceClass_IN, QueryRecordCallback, context ); require_noerr( err, exit ); @@ -1998,7 +2563,6 @@ static void QueryRecordPrintPrologue( const QueryRecordContext *inContext ) { const int timeLimitSecs = inContext->timeLimitSecs; char ifName[ kInterfaceNameBufLen ]; - char time[ kTimestampBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); @@ -2010,7 +2574,7 @@ static void QueryRecordPrintPrologue( const QueryRecordContext *inContext ) FPrintF( stdout, "Time limit: " ); if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); else FPrintF( stdout, "∞\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -2034,13 +2598,13 @@ static void DNSSD_API void * inContext ) { QueryRecordContext * const context = (QueryRecordContext *) inContext; + struct timeval now; OSStatus err; char * rdataStr = NULL; - char time[ kTimestampBufLen ]; Unused( inSDRef ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); switch( inError ) { @@ -2076,8 +2640,8 @@ static void DNSSD_API FPrintF( stdout, "%-26s A/R Flags IF %-32s %-5s %-5s %6s RData\n", "Timestamp", "Name", "Type", "Class", "TTL" ); context->printedHeader = true; } - FPrintF( stdout, "%-26s %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n", - time, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ), + FPrintF( stdout, "%{du:time} %-3s %5X %2d %-32s %-5s %?-5s%?5u %6u %s\n", + &now, AddRmvString( inFlags ), inFlags, (int32_t) inInterfaceIndex, inFullName, RecordTypeToString( inType ), ( inClass == kDNSServiceClass_IN ), "IN", ( inClass != kDNSServiceClass_IN ), inClass, inTTL, rdataStr ); if( context->oneShotMode ) @@ -2273,7 +2837,6 @@ static void RegisterPrintPrologue( const RegisterContext *inContext ) size_t i; int infinite; char ifName[ kInterfaceNameBufLen ]; - char time[ kTimestampBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); @@ -2304,7 +2867,7 @@ static void RegisterPrintPrologue( const RegisterContext *inContext ) FPrintF( stdout, " TTL: %u%?s\n", record->ttl, record->ttl == 0, " (system will use a default value.)" ); FPrintF( stdout, " RData: %#H\n\n", record->dataPtr, (int) record->dataLen, INT_MAX ); } - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -2345,19 +2908,19 @@ static void DNSSD_API { RegisterContext * const context = (RegisterContext *) inContext; OSStatus err; - char time[ kTimestampBufLen ]; + struct timeval now; Unused( inSDRef ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); if( !context->printedHeader ) { FPrintF( stdout, "%-26s A/R Flags Service\n", "Timestamp" ); context->printedHeader = true; } - FPrintF( stdout, "%-26s %-3s %5X %s.%s%s %?#m\n", - time, AddRmvString( inFlags ), inFlags, inName, inType, inDomain, inError, inError ); + FPrintF( stdout, "%{du:time} %-3s %5X %s.%s%s %?#m\n", + &now, AddRmvString( inFlags ), inFlags, inName, inType, inDomain, inError, inError ); require_noerr_action_quiet( inError, exit, err = inError ); @@ -2548,7 +3111,6 @@ exit: static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext ) { int infinite; - char time[ kTimestampBufLen ]; char ifName[ kInterfaceNameBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); @@ -2569,7 +3131,7 @@ static void RegisterRecordPrintPrologue( const RegisterRecordContext *inContext inContext->updateTTL, inContext->updateTTL == 0, " (system will use a default value.)" ); FPrintF( stdout, " RData: %#H\n", inContext->updateDataPtr, (int) inContext->updateDataLen, INT_MAX ); } - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -2598,15 +3160,15 @@ static void void * inContext ) { RegisterRecordContext * context = (RegisterRecordContext *) inContext; - char time[ kTimestampBufLen ]; + struct timeval now; Unused( inSDRef ); Unused( inRecordRef ); Unused( inFlags ); Unused( context ); - GetTimestampStr( time ); - FPrintF( stdout, "%s Record registration result (error %#m)\n", time, inError ); + gettimeofday( &now, NULL ); + FPrintF( stdout, "%{du:time} Record registration result (error %#m)\n", &now, inError ); if( !context->didRegister && !inError ) { @@ -2749,7 +3311,7 @@ static void ResolveCmd( void ) // Start operation. - if( useMainConnection ) sdRef = context->mainRef; + sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef; err = DNSServiceResolve( &sdRef, context->flags, context->ifIndex, context->name, context->type, context->domain, ResolveCallback, NULL ); require_noerr( err, exit ); @@ -2850,19 +3412,18 @@ static void ResolvePrintPrologue( const ResolveContext *inContext ) { const int timeLimitSecs = inContext->timeLimitSecs; char ifName[ kInterfaceNameBufLen ]; - char time[ kTimestampBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); - FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); - FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); - FPrintF( stdout, "Name: %s\n", inContext->name ); - FPrintF( stdout, "Type: %s\n", inContext->type ); - FPrintF( stdout, "Domain: %s\n", inContext->domain ); + FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); + FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); + FPrintF( stdout, "Name: %s\n", inContext->name ); + FPrintF( stdout, "Type: %s\n", inContext->type ); + FPrintF( stdout, "Domain: %s\n", inContext->domain ); FPrintF( stdout, "Time limit: " ); if( timeLimitSecs > 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); else FPrintF( stdout, "∞\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -2894,19 +3455,19 @@ static void DNSSD_API const unsigned char * inTXTPtr, void * inContext ) { - char time[ kTimestampBufLen ]; - char errorStr[ 64 ]; + struct timeval now; + char errorStr[ 64 ]; Unused( inSDRef ); Unused( inFlags ); Unused( inContext ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " error %#m", inError ); - FPrintF( stdout, "%s: %s can be reached at %s:%u (interface %d)%?s\n", - time, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr ); + FPrintF( stdout, "%{du:time}: %s can be reached at %s:%u (interface %d)%?s\n", + &now, inFullName, inHostname, ntohs( inPort ), (int32_t) inInterfaceIndex, inError, errorStr ); if( inTXTLen == 1 ) { FPrintF( stdout, " TXT record: %#H\n", inTXTPtr, (int) inTXTLen, INT_MAX ); @@ -2958,10 +3519,10 @@ static void GetAddrInfoPOSIXCmd( void ) { OSStatus err; struct addrinfo hints; + struct timeval now; const struct addrinfo * addrInfo; struct addrinfo * addrInfoList = NULL; const FlagStringPair * pair; - char time[ kTimestampBufLen ]; memset( &hints, 0, sizeof( hints ) ); hints.ai_socktype = SOCK_STREAM; @@ -3009,13 +3570,13 @@ static void GetAddrInfoPOSIXCmd( void ) if( ( (unsigned int) hints.ai_flags ) & pair->flag ) FPrintF( stdout, "%s ", pair->str ); } FPrintF( stdout, ">\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); // Call getaddrinfo(). err = getaddrinfo( gGAIPOSIX_HostName, gGAIPOSIX_ServName, &hints, &addrInfoList ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); if( err ) { FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) ); @@ -3033,7 +3594,7 @@ static void GetAddrInfoPOSIXCmd( void ) } } FPrintF( stdout, "---\n" ); - FPrintF( stdout, "End time: %s\n", time ); + FPrintF( stdout, "End time: %{du:time}\n", &now ); exit: if( addrInfoList ) freeaddrinfo( addrInfoList ); @@ -3151,7 +3712,7 @@ static void ReverseLookupCmd( void ) // Start operation. - if( useMainConnection ) sdRef = context->mainRef; + sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef; err = DNSServiceQueryRecord( &sdRef, context->flags, context->ifIndex, context->recordName, context->recordType, kDNSServiceClass_IN, QueryRecordCallback, context ); require_noerr( err, exit ); @@ -3284,7 +3845,7 @@ static void PortMappingCmd( void ) // Start operation. - if( useMainConnection ) sdRef = context->mainRef; + sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef; err = DNSServiceNATPortMappingCreate( &sdRef, context->flags, context->ifIndex, context->protocols, htons( context->internalPort ), htons( context->externalPort ), context->ttl, PortMappingCallback, context ); require_noerr( err, exit ); @@ -3311,17 +3872,17 @@ exit: static void PortMappingPrintPrologue( const PortMappingContext *inContext ) { char ifName[ kInterfaceNameBufLen ]; - char time[ kTimestampBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); - FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); - FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); - FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors ); - FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort ); - FPrintF( stdout, "External Port: %u\n", inContext->externalPort ); - FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl, " (system will use a default value.)" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Flags: %#{flags}\n", inContext->flags, kDNSServiceFlagsDescriptors ); + FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, ifName ); + FPrintF( stdout, "Protocols: %#{flags}\n", inContext->protocols, kDNSServiceProtocolDescriptors ); + FPrintF( stdout, "Internal Port: %u\n", inContext->internalPort ); + FPrintF( stdout, "External Port: %u\n", inContext->externalPort ); + FPrintF( stdout, "TTL: %u%?s\n", inContext->ttl, !inContext->ttl, + " (system will use a default value.)" ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -3355,13 +3916,13 @@ static void DNSSD_API void * inContext ) { PortMappingContext * const context = (PortMappingContext *) inContext; - char time[ kTimestampBufLen ]; + struct timeval now; char errorStr[ 128 ]; Unused( inSDRef ); Unused( inFlags ); - GetTimestampStr( time ); + gettimeofday( &now, NULL ); if( inError ) SNPrintF( errorStr, sizeof( errorStr ), " (error: %#m)", inError ); if( !context->printedHeader ) @@ -3369,8 +3930,8 @@ static void DNSSD_API FPrintF( stdout, "%-26s IF %7s %15s %7s %6s Protocol\n", "Timestamp", "IntPort", "ExtAddr", "ExtPort", "TTL" ); context->printedHeader = true; } - FPrintF( stdout, "%-26s %2u %7u %15.4a %7u %6u %#{flags}%?s\n", - time, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL, + FPrintF( stdout, "%{du:time} %2u %7u %15.4a %7u %6u %#{flags}%?s\n", + &now, inInterfaceIndex, ntohs( inInternalPort), &inExternalIPv4Address, ntohs( inExternalPort ), inTTL, inProtocol, kDNSServiceProtocolDescriptors, inError, errorStr ); } @@ -3632,7 +4193,7 @@ static void BrowseAllCmd( void ) gBrowseAll_ServiceTypesCount = 0; context->browseTimeSecs = gBrowseAll_BrowseTimeSecs; context->maxConnectTimeSecs = gBrowseAll_MaxConnectTimeSecs; - context->includeAWDL = gBrowseAll_IncludeAWDL ? true : false; + context->includeAWDL = gDNSSDFlag_IncludeAWDL ? true : false; #if( TARGET_OS_POSIX ) context->useColoredText = isatty( STDOUT_FILENO ) ? true : false; #endif @@ -3683,7 +4244,6 @@ static void BrowseAllPrintPrologue( const BrowseAllContext *inContext ) { size_t i; char ifName[ kInterfaceNameBufLen ]; - char time[ kTimestampBufLen ]; InterfaceIndexToName( inContext->ifIndex, ifName ); @@ -3703,7 +4263,8 @@ static void BrowseAllPrintPrologue( const BrowseAllContext *inContext ) FPrintF( stdout, "Browse time: %d second%?c\n", inContext->browseTimeSecs, inContext->browseTimeSecs != 1, 's' ); FPrintF( stdout, "Max connect time: %d second%?c\n", inContext->maxConnectTimeSecs, inContext->maxConnectTimeSecs != 1, 's' ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "IncludeAWDL: %s\n", inContext->includeAWDL ? "YES" : "NO" ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -4063,7 +4624,7 @@ static void BrowseAllStop( void *inContext ) { check( !context->exitTimer ); err = DispatchTimerCreate( dispatch_time_seconds( context->maxConnectTimeSecs ), DISPATCH_TIME_FOREVER, - 100 * kNanosecondsPerMillisecond, BrowseAllExit, NULL, context, &context->exitTimer ); + 100 * kNanosecondsPerMillisecond, NULL, BrowseAllExit, NULL, context, &context->exitTimer ); require_noerr( err, exit ); dispatch_resume( context->exitTimer ); } @@ -4743,6 +5304,81 @@ static void BrowseIPAddrReleaseList( BrowseIPAddr *inList ) } } +//=========================================================================================================================== +// GetNameInfoCmd +//=========================================================================================================================== + +const FlagStringPair kGetNameInfoFlagStringPairs[] = +{ + CaseFlagStringify( NI_NUMERICSCOPE ), + CaseFlagStringify( NI_DGRAM ), + CaseFlagStringify( NI_NUMERICSERV ), + CaseFlagStringify( NI_NAMEREQD ), + CaseFlagStringify( NI_NUMERICHOST ), + CaseFlagStringify( NI_NOFQDN ), + { 0, NULL } +}; + +static void GetNameInfoCmd( void ) +{ + OSStatus err; + sockaddr_ip sip; + size_t sockAddrLen; + unsigned int flags; + const FlagStringPair * pair; + struct timeval now; + char host[ NI_MAXHOST ]; + char serv[ NI_MAXSERV ]; + + err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen ); + check_noerr( err ); + if( err ) + { + FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress ); + goto exit; + } + + flags = 0; + if( gGetNameInfoFlag_DGram ) flags |= NI_DGRAM; + if( gGetNameInfoFlag_NameReqd ) flags |= NI_NAMEREQD; + if( gGetNameInfoFlag_NoFQDN ) flags |= NI_NOFQDN; + if( gGetNameInfoFlag_NumericHost ) flags |= NI_NUMERICHOST; + if( gGetNameInfoFlag_NumericScope ) flags |= NI_NUMERICSCOPE; + if( gGetNameInfoFlag_NumericServ ) flags |= NI_NUMERICSERV; + + // Print prologue. + + FPrintF( stdout, "SockAddr: %##a\n", &sip.sa ); + FPrintF( stdout, "Flags: 0x%X < ", flags ); + for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair ) + { + if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str ); + } + FPrintF( stdout, ">\n" ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); + FPrintF( stdout, "---\n" ); + + // Call getnameinfo(). + + err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ), + (int) flags ); + gettimeofday( &now, NULL ); + if( err ) + { + FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) ); + } + else + { + FPrintF( stdout, "host: %s\n", host ); + FPrintF( stdout, "serv: %s\n", serv ); + } + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "End time: %{du:time}\n", &now ); + +exit: + gExitCode = err ? 1 : 0; +} + //=========================================================================================================================== // GetAddrInfoStressCmd //=========================================================================================================================== @@ -4782,7 +5418,6 @@ static void GetAddrInfoStressCmd( void ) DNSServiceFlags flags; uint32_t ifIndex; char ifName[ kInterfaceNameBufLen ]; - char time[ kTimestampBufLen ]; if( gGAIStress_TestDurationSecs < 0 ) { @@ -4863,11 +5498,11 @@ static void GetAddrInfoStressCmd( void ) { FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs ); } - FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount ); - FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs ); - FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs ); - FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Connection count: %d\n", gGAIStress_ConnectionCount ); + FPrintF( stdout, "Request duration min: %d ms\n", gGAIStress_DurationMinMs ); + FPrintF( stdout, "Request duration max: %d ms\n", gGAIStress_DurationMaxMs ); + FPrintF( stdout, "Request count max: %d\n", gGAIStress_RequestCountMax ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL); FPrintF( stdout, "---\n" ); dispatch_main(); @@ -4893,7 +5528,6 @@ static void GetAddrInfoStressEvent( void *inContext ) unsigned int nextMs; char randomStr[ kStressRandStrLen + 1 ]; char hostname[ kStressRandStrLen + 4 + 1 ]; - char time[ kTimestampBufLen ]; Boolean isConnectionNew = false; static Boolean printedHeader = false; @@ -4923,8 +5557,8 @@ static void GetAddrInfoStressEvent( void *inContext ) FPrintF( stdout, "%-26s Conn Hostname Dur (ms)\n", "Timestamp" ); printedHeader = true; } - FPrintF( stdout, "%-26s %3u%c %9s %8u\n", - GetTimestampStr( time ), context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs ); + FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n", + NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs ); DNSServiceForget( &context->sdRef ); sdRef = context->mainRef; @@ -4970,8 +5604,6 @@ static void DNSSD_API // DNSQueryCmd //=========================================================================================================================== -#define kDNSPort 53 - typedef struct { sockaddr_ip serverAddr; @@ -5073,8 +5705,7 @@ static void DNSQueryCmd( void ) if( gDNSQuery_Verbose ) { - FPrintF( stdout, "DNS message to send:\n\n" ); - PrintUDNSMessage( msgPtr, msgLen, false ); + FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen ); FPrintF( stdout, "---\n" ); } @@ -5103,7 +5734,7 @@ static void DNSQueryCmd( void ) if( context->timeLimitSecs == 0 ) goto exit; - err = DispatchReadSourceCreate( context->sock, DNSQueryReadHandler, DNSQueryCancelHandler, context, + err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context, &context->readSource ); require_noerr( err, exit ); dispatch_resume( context->readSource ); @@ -5132,7 +5763,6 @@ exit: static void DNSQueryPrintPrologue( const DNSQueryContext *inContext ) { const int timeLimitSecs = inContext->timeLimitSecs; - char time[ kTimestampBufLen ]; FPrintF( stdout, "Name: %s\n", inContext->name ); FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( inContext->type ), inContext->type ); @@ -5141,7 +5771,7 @@ static void DNSQueryPrintPrologue( const DNSQueryContext *inContext ) FPrintF( stdout, "Time limit: " ); if( timeLimitSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' ); else FPrintF( stdout, "∞\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); } @@ -5152,11 +5782,11 @@ static void DNSQueryPrintPrologue( const DNSQueryContext *inContext ) static void DNSQueryReadHandler( void *inContext ) { OSStatus err; + struct timeval now; const uint64_t nowTicks = UpTicks(); DNSQueryContext * const context = (DNSQueryContext *) inContext; - char time[ kTimestampBufLen ]; - GetTimestampStr( time ); + gettimeofday( &now, NULL ); if( context->useTCP ) { @@ -5198,11 +5828,11 @@ static void DNSQueryReadHandler( void *inContext ) check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 ); } - FPrintF( stdout, "Receive time: %s\n", time ); + FPrintF( stdout, "Receive time: %{du:time}\n", &now ); FPrintF( stdout, "Source: %##a\n", &context->serverAddr ); FPrintF( stdout, "Message size: %zu\n", context->msgLen ); FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) ); - PrintUDNSMessage( context->msgPtr, context->msgLen, context->printRawRData ); + FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen ); if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) ) { @@ -5336,7 +5966,7 @@ static void DNSCryptCmd( void ) DNSCryptContext * context = NULL; size_t writtenBytes; size_t totalBytes; - SocketContext * sockContext; + SocketContext * sockCtx; SocketRef sock = kInvalidSocketRef; const char * ptr; @@ -5403,17 +6033,15 @@ static void DNSCryptCmd( void ) err = SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 ); require_noerr( err, exit ); - sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); - require_action( sockContext, exit, err = kNoMemoryErr ); + err = SocketContextCreate( sock, context, &sockCtx ); + require_noerr( err, exit ); + sock = kInvalidSocketRef; - err = DispatchReadSourceCreate( sock, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockContext, + err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx, &context->readSource ); - if( err ) ForgetMem( &sockContext ); + if( err ) ForgetSocketContext( &sockCtx ); require_noerr( err, exit ); - sockContext->context = context; - sockContext->sock = sock; - sock = kInvalidSocketRef; dispatch_resume( context->readSource ); if( context->timeLimitSecs > 0 ) @@ -5436,9 +6064,10 @@ exit: static void DNSCryptReceiveCertHandler( void *inContext ) { OSStatus err; + struct timeval now; const uint64_t nowTicks = UpTicks(); - SocketContext * const sockContext = (SocketContext *) inContext; - DNSCryptContext * const context = (DNSCryptContext *) sockContext->context; + SocketContext * const sockCtx = (SocketContext *) inContext; + DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext; const DNSHeader * hdr; sockaddr_ip fromAddr; const uint8_t * ptr; @@ -5446,23 +6075,21 @@ static void DNSCryptReceiveCertHandler( void *inContext ) size_t txtLen; unsigned int answerCount, i; uint8_t targetName[ kDomainNameLengthMax ]; - char time[ kTimestampBufLen ]; - GetTimestampStr( time ); + gettimeofday( &now, NULL ); dispatch_source_forget( &context->readSource ); - err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen, + err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen, &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL ); require_noerr( err, exit ); check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 ); - FPrintF( stdout, "Receive time: %s\n", time ); + FPrintF( stdout, "Receive time: %{du:time}\n", &now ); FPrintF( stdout, "Source: %##a\n", &context->serverAddr ); FPrintF( stdout, "Message size: %zu\n", context->msgLen ); FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) ); - - PrintUDNSMessage( context->msgBuf, context->msgLen, context->printRawRData ); + FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen ); require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr ); @@ -5521,28 +6148,28 @@ exit: static void DNSCryptReceiveResponseHandler( void *inContext ) { OSStatus err; + struct timeval now; const uint64_t nowTicks = UpTicks(); - SocketContext * const sockContext = (SocketContext *) inContext; - DNSCryptContext * const context = (DNSCryptContext *) sockContext->context; + SocketContext * const sockCtx = (SocketContext *) inContext; + DNSCryptContext * const context = (DNSCryptContext *) sockCtx->userContext; sockaddr_ip fromAddr; DNSCryptResponseHeader * hdr; const uint8_t * end; uint8_t * ciphertext; uint8_t * plaintext; const uint8_t * response; - char time[ kTimestampBufLen ]; uint8_t nonce[ crypto_box_NONCEBYTES ]; - GetTimestampStr( time ); + gettimeofday( &now, NULL ); dispatch_source_forget( &context->readSource ); - err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen, + err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen, &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL ); require_noerr( err, exit ); check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 ); - FPrintF( stdout, "Receive time: %s\n", time ); + FPrintF( stdout, "Receive time: %{du:time}\n", &now ); FPrintF( stdout, "Source: %##a\n", &context->serverAddr ); FPrintF( stdout, "Message size: %zu\n", context->msgLen ); FPrintF( stdout, "RTT: %llu ms\n\n", UpTicksToMilliseconds( nowTicks - context->sendTicks ) ); @@ -5586,7 +6213,7 @@ static void DNSCryptReceiveResponseHandler( void *inContext ) require_noerr( err, exit ); response = plaintext + crypto_box_ZEROBYTES; - PrintUDNSMessage( response, (size_t)( end - response ), context->printRawRData ); + FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) ); Exit( kExitReason_ReceivedResponse ); exit: @@ -5762,7 +6389,7 @@ exit: static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext ) { OSStatus err; - SocketContext * sockContext; + SocketContext * sockCtx; SocketRef sock = kInvalidSocketRef; check( inContext->msgLen > 0 ); @@ -5775,18 +6402,15 @@ static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext ) err = SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 ); require_noerr( err, exit ); - sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); - require_action( sockContext, exit, err = kNoMemoryErr ); + err = SocketContextCreate( sock, inContext, &sockCtx ); + require_noerr( err, exit ); + sock = kInvalidSocketRef; - err = DispatchReadSourceCreate( sock, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockContext, + err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx, &inContext->readSource ); - if( err ) ForgetMem( &sockContext ); + if( err ) ForgetSocketContext( &sockCtx ); require_noerr( err, exit ); - sockContext->context = inContext; - sockContext->sock = sock; - sock = kInvalidSocketRef; - dispatch_resume( inContext->readSource ); exit: @@ -5857,11 +6481,6 @@ static char * CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] ) // MDNSQueryCmd //=========================================================================================================================== -#define kMDNSPort 5353 - -#define kDefaultMDNSMessageID 0 -#define kDefaultMDNSQueryFlags 0 - typedef struct { const char * qnameStr; // Name (QNAME) of the record being queried as a C string. @@ -6039,37 +6658,33 @@ static void MDNSQueryCmd( void ) if( IsValidSocket( sockV4 ) ) { - SocketContext * sockContext; + SocketContext * sockCtx; - sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); - require_action( sockContext, exit, err = kNoMemoryErr ); + err = SocketContextCreate( sockV4, context, &sockCtx ); + require_noerr( err, exit ); + sockV4 = kInvalidSocketRef; - err = DispatchReadSourceCreate( sockV4, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext, + err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx, &context->readSourceV4 ); - if( err ) ForgetMem( &sockContext ); + if( err ) ForgetSocketContext( &sockCtx ); require_noerr( err, exit ); - sockContext->context = context; - sockContext->sock = sockV4; - sockV4 = kInvalidSocketRef; dispatch_resume( context->readSourceV4 ); } if( IsValidSocket( sockV6 ) ) { - SocketContext * sockContext; + SocketContext * sockCtx; - sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); - require_action( sockContext, exit, err = kNoMemoryErr ); + err = SocketContextCreate( sockV6, context, &sockCtx ); + require_noerr( err, exit ); + sockV6 = kInvalidSocketRef; - err = DispatchReadSourceCreate( sockV6, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext, + err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx, &context->readSourceV6 ); - if( err ) ForgetMem( &sockContext ); + if( err ) ForgetSocketContext( &sockCtx ); require_noerr( err, exit ); - sockContext->context = context; - sockContext->sock = sockV6; - sockV6 = kInvalidSocketRef; dispatch_resume( context->readSourceV6 ); } @@ -6093,7 +6708,6 @@ exit: static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext ) { const int receiveSecs = inContext->receiveSecs; - char time[ kTimestampBufLen ]; FPrintF( stdout, "Interface: %d (%s)\n", (int32_t) inContext->ifIndex, inContext->ifName ); FPrintF( stdout, "Name: %s\n", inContext->qnameStr ); @@ -6105,7 +6719,7 @@ static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext ) FPrintF( stdout, "Receive duration: " ); if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' ); else FPrintF( stdout, "∞\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); } //=========================================================================================================================== @@ -6115,16 +6729,16 @@ static void MDNSQueryPrintPrologue( const MDNSQueryContext *inContext ) static void MDNSQueryReadHandler( void *inContext ) { OSStatus err; - SocketContext * const sockContext = (SocketContext *) inContext; - MDNSQueryContext * const context = (MDNSQueryContext *) sockContext->context; + struct timeval now; + SocketContext * const sockCtx = (SocketContext *) inContext; + MDNSQueryContext * const context = (MDNSQueryContext *) sockCtx->userContext; size_t msgLen; sockaddr_ip fromAddr; - char time[ kTimestampBufLen ]; Boolean foundAnswer = false; - GetTimestampStr( time ); + gettimeofday( &now, NULL ); - err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr, + err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL ); require_noerr( err, exit ); @@ -6162,11 +6776,10 @@ static void MDNSQueryReadHandler( void *inContext ) if( context->allResponses || foundAnswer ) { FPrintF( stdout, "---\n" ); - FPrintF( stdout, "Receive time: %s\n", time ); - FPrintF( stdout, "Source: %##a\n", &fromAddr ); - FPrintF( stdout, "Message size: %zu\n\n", msgLen ); - - PrintMDNSMessage( context->msgBuf, msgLen, context->printRawRData ); + FPrintF( stdout, "Receive time: %{du:time}\n", &now ); + FPrintF( stdout, "Source: %##a\n", &fromAddr ); + FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}", + msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen ); } exit: @@ -6194,221 +6807,3657 @@ exit: } //=========================================================================================================================== -// SSDPDiscoverCmd +// DNSServerCmd //=========================================================================================================================== -#define kSSDPPort 1900 +typedef uint32_t DNSServerEventType; +#define kDNSServerEvent_Started 1 +#define kDNSServerEvent_Stopped 2 + +typedef struct DNSServerPrivate * DNSServerRef; typedef struct { - HTTPHeader header; // HTTP header object for sending and receiving. - dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket. - dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket. - int receiveSecs; // After send, the amount of time to spend receiving. - uint32_t ifindex; // Index of the interface over which to send the query. - Boolean useIPv4; // True if the query should be sent via IPv4 multicast. - Boolean useIPv6; // True if the query should be sent via IPv6 multicast. + DNSServerRef server; // Reference to the DNS server. + dispatch_source_t sigIntSource; // Dispatch SIGINT source. + dispatch_source_t sigTermSource; // Dispatch SIGTERM source. +#if( TARGET_OS_DARWIN ) + dispatch_source_t processMonitor; // Process monitor source for process being followed, if any. + pid_t followPID; // PID of process being followed (we exit when they exit), if any. + Boolean resolverRegistered; // True if system DNS settings contains a resolver entry for server. +#endif + Boolean loopbackOnly; // True if the server should be bound to the loopback interface. + Boolean serverStarted; // True if the server was successfully started. + Boolean calledStop; // True if the server was explicitly stopped. -} SSDPDiscoverContext; +} DNSServerCmdContext; -static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext ); -static void SSDPDiscoverReadHandler( void *inContext ); -static int SocketToPortNumber( SocketRef inSock ); -static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST ); +typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, void *inContext ); -static void SSDPDiscoverCmd( void ) +CFTypeID DNSServerGetTypeID( void ); +static OSStatus + DNSServerCreate( + dispatch_queue_t inQueue, + DNSServerEventHandler_f inEventHandler, + void * inEventContext, + int inResponseDelayMs, + Boolean inLoopbackOnly, + DNSServerRef * outServer ); +static void DNSServerStart( DNSServerRef inServer ); +static void DNSServerStop( DNSServerRef inServer ); + +static void DNSServerCmdContextFree( DNSServerCmdContext *inContext ); +static void DNSServerCmdEventHandler( DNSServerEventType inType, void *inContext ); +static void DNSServerCmdSigIntHandler( void *inContext ); +static void DNSServerCmdSigTermHandler( void *inContext ); +#if( TARGET_OS_DARWIN ) +static void DNSServerCmdFollowedProcessHandler( void *inContext ); +#endif + +ulog_define_ex( "com.apple.dnssdutil", DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL ); +#define ds_ulog( LEVEL, ... ) ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ ) + +static void DNSServerCmd( void ) { OSStatus err; - SSDPDiscoverContext * context; - dispatch_source_t signalSource = NULL; - SocketRef sockV4 = kInvalidSocketRef; - SocketRef sockV6 = kInvalidSocketRef; - ssize_t n; - int sendCount; - char time[ kTimestampBufLen ]; - - // Set up SIGINT handler. - - signal( SIGINT, SIG_IGN ); - err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); - require_noerr( err, exit ); - dispatch_resume( signalSource ); + DNSServerCmdContext * context; - // Check command parameters. - - if( gSSDPDiscover_ReceiveSecs < -1 ) - { - FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs ); - err = kParamErr; - goto exit; - } - - // Create context. - - context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) ); + context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) ); require_action( context, exit, err = kNoMemoryErr ); - context->receiveSecs = gSSDPDiscover_ReceiveSecs; - context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false; - context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false; - - err = InterfaceIndexFromArgString( gInterface, &context->ifindex ); - require_noerr_quiet( err, exit ); - - // Set up IPv4 socket. + context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false; - if( context->useIPv4 ) +#if( TARGET_OS_DARWIN ) + if( gDNSServer_FollowPID ) { - int port; - err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 ); - require_noerr( err, exit ); + long long value; - err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex ); - require_noerr( err, exit ); + err = StringToLongLong( gDNSServer_FollowPID, &value ); + if( !err && ( value < 0 ) ) err = kValueErr; + if( err ) + { + FPrintF( stderr, "Invalid followPID argument \"%s\".\n", gDNSServer_FollowPID ); + goto exit; + } + context->followPID = (pid_t) value; - err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) ); - err = map_socket_noerr_errno( sockV4, err ); + err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(), + DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor ); require_noerr( err, exit ); + dispatch_resume( context->processMonitor ); + } + else + { + context->followPID = -1; } +#endif - // Set up IPv6 socket. + signal( SIGINT, SIG_IGN ); + err = DispatchSignalSourceCreate( SIGINT, DNSServerCmdSigIntHandler, context, &context->sigIntSource ); + require_noerr( err, exit ); + dispatch_resume( context->sigIntSource ); - if( context->useIPv6 ) + signal( SIGTERM, SIG_IGN ); + err = DispatchSignalSourceCreate( SIGTERM, DNSServerCmdSigTermHandler, context, &context->sigTermSource ); + require_noerr( err, exit ); + dispatch_resume( context->sigTermSource ); + + if( gDNSServer_Foreground ) { - err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 ); - require_noerr( err, exit ); - - err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex ); - require_noerr( err, exit ); - - err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) ); - err = map_socket_noerr_errno( sockV6, err ); - require_noerr( err, exit ); + LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" ); } - // Print prologue. + if( ( gDNSServer_DefaultTTL < 0 ) || ( gDNSServer_DefaultTTL > INT32_MAX ) ) + { + ds_ulog( kLogLevelError, "The default TTL %d provided by user is out-of-range. Will use %d instead.\n", + gDNSServer_DefaultTTL, kDNSServerDefaultTTL ); + gDNSServer_DefaultTTL = kDNSServerDefaultTTL; + } - SSDPDiscoverPrintPrologue( context ); + err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context, gDNSServer_ResponseDelayMs, + context->loopbackOnly, &context->server ); + require_noerr( err, exit ); - // Send mDNS query message. + DNSServerStart( context->server ); + dispatch_main(); - sendCount = 0; - if( IsValidSocket( sockV4 ) ) +exit: + ds_ulog( kLogLevelError, "Failed to start DNS server: %#m\n", err ); + if( context ) DNSServerCmdContextFree( context ); + if( err ) exit( 1 ); +} + +//=========================================================================================================================== +// DNSServerCmdContextFree +//=========================================================================================================================== + +static void DNSServerCmdContextFree( DNSServerCmdContext *inContext ) +{ + ForgetCF( &inContext->server ); + dispatch_source_forget( &inContext->sigIntSource ); + dispatch_source_forget( &inContext->sigTermSource ); + dispatch_source_forget( &inContext->processMonitor ); + free( inContext ); +} + +//=========================================================================================================================== +// DNSServerCmdEventHandler +//=========================================================================================================================== + +#if( TARGET_OS_DARWIN ) +static OSStatus _DNSServerCmdRegisterResolver( void ); +static OSStatus _DNSServerCmdUnregisterResolver( void ); +#endif + +static void DNSServerCmdEventHandler( DNSServerEventType inType, void *inContext ) +{ + DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext; +#if( TARGET_OS_DARWIN ) + OSStatus err; +#endif + + if( inType == kDNSServerEvent_Started ) { - struct sockaddr_in mcastAddr4; - - memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) ); - SIN_LEN_SET( &mcastAddr4 ); - mcastAddr4.sin_family = AF_INET; - mcastAddr4.sin_port = htons( kSSDPPort ); - mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250 - - err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST ); - require_noerr( err, exit ); - - n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4, - (socklen_t) sizeof( mcastAddr4 ) ); - err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n ); + context->serverStarted = true; + #if( TARGET_OS_DARWIN ) + err = _DNSServerCmdRegisterResolver(); if( err ) { - FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err ); - ForgetSocket( &sockV4 ); + ds_ulog( kLogLevelError, "Failed to add resolver to DNS configuration for \"d.test.\" domain: %#m\n", err ); + if( context->loopbackOnly ) exit( 1 ); } else { - if( gSSDPDiscover_Verbose ) - { - GetTimestampStr( time ); - FPrintF( stdout, "---\n" ); - FPrintF( stdout, "Send time: %s\n", time ); - FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) ); - FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 ); - FPrintF( stdout, "Message size: %zu\n", context->header.len ); - FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len ); - } - ++sendCount; + context->resolverRegistered = true; } + #endif } - - if( IsValidSocket( sockV6 ) ) + else if( inType == kDNSServerEvent_Stopped ) { - struct sockaddr_in6 mcastAddr6; - - memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) ); - SIN6_LEN_SET( &mcastAddr6 ); - mcastAddr6.sin6_family = AF_INET6; - mcastAddr6.sin6_port = htons( kSSDPPort ); - mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C - mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02; - mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C; - - err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST ); - require_noerr( err, exit ); - - n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6, - (socklen_t) sizeof( mcastAddr6 ) ); - err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n ); - if( err ) - { - FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err ); - ForgetSocket( &sockV6 ); - } - else + #if( TARGET_OS_DARWIN ) + if( context->resolverRegistered ) { - if( gSSDPDiscover_Verbose ) + err = _DNSServerCmdUnregisterResolver(); + if( err ) { - GetTimestampStr( time ); - FPrintF( stdout, "---\n" ); - FPrintF( stdout, "Send time: %s\n", time ); - FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) ); - FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 ); - FPrintF( stdout, "Message size: %zu\n", context->header.len ); - FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len ); + ds_ulog( kLogLevelError, "Failed to remove resolver from DNS configuration: %#m\n", err ); + } + else + { + context->resolverRegistered = false; } - ++sendCount; } + + if( !context->calledStop ) + { + ds_ulog( kLogLevelError, "The server stopped unexpectedly.\n" ); + exit( 1 ); + } + #endif + DNSServerCmdContextFree( context ); } - require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr ); - - // If there's no wait period after the send, then exit. - +} + +#if( TARGET_OS_DARWIN ) +//=========================================================================================================================== +// _DNSServerCmdRegisterResolver +//=========================================================================================================================== + +static OSStatus _DNSServerCmdRegisterResolver( void ) +{ + OSStatus err; + SCDynamicStoreRef store; + CFPropertyListRef plist = NULL; + CFStringRef key = NULL; + const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK ); + Boolean success; + + store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL ); + err = map_scerror( store ); + require_noerr( err, exit ); + + err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist, + "{" + "%kO=" + "[" + "%s" + "]" + "%kO=" + "[" + "%.4a" + "%.16a" + "]" + "}", + kSCPropNetDNSSupplementalMatchDomains, "d.test.", + kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr ); + require_noerr( err, exit ); + + key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, + CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS ); + require_action( key, exit, err = kUnknownErr ); + + success = SCDynamicStoreSetValue( store, key, plist ); + require_action( success, exit, err = kUnknownErr ); + +exit: + CFReleaseNullSafe( store ); + CFReleaseNullSafe( plist ); + CFReleaseNullSafe( key ); + return( err ); +} + +//=========================================================================================================================== +// _DNSServerCmdUnregisterResolver +//=========================================================================================================================== + +static OSStatus _DNSServerCmdUnregisterResolver( void ) +{ + OSStatus err; + SCDynamicStoreRef store; + CFStringRef key = NULL; + Boolean success; + + store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL ); + err = map_scerror( store ); + require_noerr( err, exit ); + + key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, + CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS ); + require_action( key, exit, err = kUnknownErr ); + + success = SCDynamicStoreRemoveValue( store, key ); + require_action( success, exit, err = kUnknownErr ); + +exit: + CFReleaseNullSafe( store ); + CFReleaseNullSafe( key ); + return( err ); +} +#endif + +//=========================================================================================================================== +// DNSServerCmdSigIntHandler +//=========================================================================================================================== + +static void _DNSServerCmdExternalExit( DNSServerCmdContext *inContext, int inSignal ); + +static void DNSServerCmdSigIntHandler( void *inContext ) +{ + _DNSServerCmdExternalExit( (DNSServerCmdContext *) inContext, SIGINT ); +} + +//=========================================================================================================================== +// DNSServerCmdSigTermHandler +//=========================================================================================================================== + +static void DNSServerCmdSigTermHandler( void *inContext ) +{ + _DNSServerCmdExternalExit( (DNSServerCmdContext *) inContext, SIGTERM ); +} + +#if( TARGET_OS_DARWIN ) +//=========================================================================================================================== +// DNSServerCmdFollowedProcessHandler +//=========================================================================================================================== + +static void DNSServerCmdFollowedProcessHandler( void *inContext ) +{ + DNSServerCmdContext * const context = (DNSServerCmdContext *) inContext; + + if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT ) + { + _DNSServerCmdExternalExit( context, 0 ); + } +} +#endif + +//=========================================================================================================================== +// _DNSServerCmdExternalExit +//=========================================================================================================================== + +#define SignalNumberToString( X ) ( \ + ( (X) == SIGINT ) ? "SIGINT" : \ + ( (X) == SIGTERM ) ? "SIGTERM" : \ + "???" ) + +static void _DNSServerCmdExternalExit( DNSServerCmdContext *inContext, int inSignal ) +{ + OSStatus err; + +#if( TARGET_OS_DARWIN ) + if( inSignal == 0 ) + { + ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID ); + } + else +#endif + { + ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) ); + } + +#if( TARGET_OS_DARWIN ) + if( inContext->resolverRegistered ) + { + err = _DNSServerCmdUnregisterResolver(); + if( err ) + { + ds_ulog( kLogLevelError, "Failed to remove resolver from DNS configuration: %#m\n", err ); + goto exit; + } + inContext->resolverRegistered = false; + } +#endif + if( inContext->serverStarted ) + { + DNSServerStop( inContext->server ); + inContext->calledStop = true; + } + err = kNoErr; + +exit: + exit( err ? 1 : 0 ); +} + +//=========================================================================================================================== +// DNSServerCreate +//=========================================================================================================================== + +typedef struct DNSDelayedResponse DNSDelayedResponse; +struct DNSDelayedResponse +{ + DNSDelayedResponse * next; + sockaddr_ip clientAddr; + uint64_t targetTicks; + uint8_t * msgPtr; + size_t msgLen; +}; + +#define DNSScheduledResponseFree( X ) do { ForgetMem( &(X)->msgPtr ) ; free( X ); } while( 0 ) + +struct DNSServerPrivate +{ + CFRuntimeBase base; // CF object base. + dispatch_queue_t queue; // Queue for DNS server's events. + dispatch_source_t readSourceUDPv4; // Read source for IPv4 UDP socket. + dispatch_source_t readSourceUDPv6; // Read source for IPv6 UDP socket. + dispatch_source_t readSourceTCPv4; // Read source for IPv4 TCP socket. + dispatch_source_t readSourceTCPv6; // Read source for IPv6 TCP socket. + DNSServerEventHandler_f eventHandler; + void * eventContext; + DNSDelayedResponse * responseList; + int responseDelayMs; + dispatch_source_t responseTimer; + Boolean loopbackOnly; + Boolean stopped; +}; + +CF_CLASS_DEFINE( DNSServer ); + +static OSStatus + DNSServerCreate( + dispatch_queue_t inQueue, + DNSServerEventHandler_f inEventHandler, + void * inEventContext, + int inResponseDelayMs, + Boolean inLoopbackOnly, + DNSServerRef * outServer ) +{ + OSStatus err; + DNSServerRef obj = NULL; + + CF_OBJECT_CREATE( DNSServer, obj, err, exit ); + + ReplaceDispatchQueue( &obj->queue, inQueue ); + obj->eventHandler = inEventHandler; + obj->eventContext = inEventContext; + obj->responseDelayMs = inResponseDelayMs; + if( inLoopbackOnly ) obj->loopbackOnly = true; + + *outServer = obj; + obj = NULL; + err = kNoErr; + +exit: + CFReleaseNullSafe( obj ); + return( err ); +} + +//=========================================================================================================================== +// _DNSServerFinalize +//=========================================================================================================================== + +static void _DNSServerFinalize( CFTypeRef inObj ) +{ + DNSServerRef const me = (DNSServerRef) inObj; + + check( !me->readSourceUDPv4 ); + check( !me->readSourceUDPv6 ); + check( !me->readSourceTCPv4 ); + check( !me->readSourceTCPv6 ); + check( !me->responseTimer ); + dispatch_forget( &me->queue ); +} + +//=========================================================================================================================== +// DNSServerStart +//=========================================================================================================================== + +static void _DNSServerStart( void *inContext ); +static void _DNSServerUDPReadHandler( void *inContext ); +static void _DNSServerTCPReadHandler( void *inContext ); + +static void DNSServerStart( DNSServerRef me ) +{ + CFRetain( me ); + dispatch_async_f( me->queue, me, _DNSServerStart ); +} + +static void _DNSServerStart( void *inContext ) +{ + OSStatus err; + DNSServerRef const me = (DNSServerRef) inContext; + SocketRef sock = kInvalidSocketRef; + SocketContext * sockCtx = NULL; + const uint32_t loopbackV4 = htonl( INADDR_LOOPBACK ); + + // Create IPv4 UDP socket. + + err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL, + kDNSPort, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock ); + require_noerr( err, exit ); + + // Create read source for IPv4 UDP socket. + + err = SocketContextCreate( sock, me, &sockCtx ); + require_noerr( err, exit ); + sock = kInvalidSocketRef; + + err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx, + &me->readSourceUDPv4 ); + require_noerr( err, exit ); + dispatch_resume( me->readSourceUDPv4 ); + sockCtx = NULL; + + // Create IPv6 UDP socket. + + err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL, + kDNSPort, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock ); + require_noerr( err, exit ); + + // Create read source for IPv6 UDP socket. + + err = SocketContextCreate( sock, me, &sockCtx ); + require_noerr( err, exit ); + sock = kInvalidSocketRef; + + err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx, + &me->readSourceUDPv6 ); + require_noerr( err, exit ); + dispatch_resume( me->readSourceUDPv6 ); + sockCtx = NULL; + + // Create IPv4 TCP socket. + + err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL, + kDNSPort, NULL, kSocketBufferSize_DontSet, false, &sock ); + require_noerr( err, exit ); + + // Create read source for IPv4 TCP socket. + + err = SocketContextCreate( sock, me, &sockCtx ); + require_noerr( err, exit ); + sock = kInvalidSocketRef; + + err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx, + &me->readSourceTCPv4 ); + require_noerr( err, exit ); + dispatch_resume( me->readSourceTCPv4 ); + sockCtx = NULL; + + // Create IPv6 TCP socket. + + err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL, + kDNSPort, NULL, kSocketBufferSize_DontSet, false, &sock ); + require_noerr( err, exit ); + + // Create read source for IPv6 TCP socket. + + err = SocketContextCreate( sock, me, &sockCtx ); + require_noerr( err, exit ); + sock = kInvalidSocketRef; + + err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx, + &me->readSourceTCPv6 ); + require_noerr( err, exit ); + dispatch_resume( me->readSourceTCPv6 ); + sockCtx = NULL; + + CFRetain( me ); + if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, me->eventContext ); + +exit: + ForgetSocket( &sock ); + if( sockCtx ) SocketContextRelease( sockCtx ); + if( err ) DNSServerStop( me ); + CFRelease( me ); +} + +//=========================================================================================================================== +// DNSServerStop +//=========================================================================================================================== + +static void _DNSServerStop( void *inContext ); +static void _DNSServerStop2( void *inContext ); + +static void DNSServerStop( DNSServerRef me ) +{ + CFRetain( me ); + dispatch_async_f( me->queue, me, _DNSServerStop ); +} + +static void _DNSServerStop( void *inContext ) +{ + DNSServerRef const me = (DNSServerRef) inContext; + DNSDelayedResponse * resp; + + dispatch_source_forget( &me->readSourceUDPv4 ); + dispatch_source_forget( &me->readSourceUDPv6 ); + dispatch_source_forget( &me->readSourceTCPv4 ); + dispatch_source_forget( &me->readSourceTCPv6 ); + dispatch_source_forget( &me->responseTimer ); + + while( ( resp = me->responseList ) != NULL ) + { + me->responseList = resp->next; + DNSScheduledResponseFree( resp ); + } + + dispatch_async_f( me->queue, me, _DNSServerStop2 ); +} + +static void _DNSServerStop2( void *inContext ) +{ + DNSServerRef const me = (DNSServerRef) inContext; + + if( !me->stopped ) + { + me->stopped = true; + if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, me->eventContext ); + CFRelease( me ); + } + CFRelease( me ); +} + +//=========================================================================================================================== +// _DNSServerUDPReadHandler +//=========================================================================================================================== + +static OSStatus + _DNSServerAnswerQuery( + const uint8_t * inQueryPtr, + size_t inQueryLen, + Boolean inForTCP, + uint8_t ** outResponsePtr, + size_t * outResponseLen ); + +#define _DNSServerAnswerQueryForUDP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \ + _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) + +#define _DNSServerAnswerQueryForTCP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \ + _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) + +static void _DNSServerUDPDelayedSend( void *inContext ); + +static void _DNSServerUDPReadHandler( void *inContext ) +{ + OSStatus err; + SocketContext * const sockCtx = (SocketContext *) inContext; + DNSServerRef const me = (DNSServerRef) sockCtx->userContext; + struct timeval now; + ssize_t n; + sockaddr_ip clientAddr; + socklen_t clientAddrLen; + uint8_t * responsePtr = NULL; // malloc'd + size_t responseLen; + uint8_t msg[ 512 ]; + + gettimeofday( &now, NULL ); + + // Receive message. + + clientAddrLen = (socklen_t) sizeof( clientAddr ); + n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen ); + err = map_socket_value_errno( sockCtx->sock, n >= 0, n ); + require_noerr( err, exit ); + + ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now ); + + if( n < kDNSHeaderLength ) + { + ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength ); + goto exit; + } + + ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n ); + + // Create response. + + err = _DNSServerAnswerQueryForUDP( msg, (size_t) n, &responsePtr, &responseLen ); + require_noerr_quiet( err, exit ); + + // Schedule response. + + if( me->responseDelayMs > 0 ) + { + DNSDelayedResponse * resp; + DNSDelayedResponse ** ptr; + DNSDelayedResponse * newResp; + + newResp = (DNSDelayedResponse *) calloc( 1, sizeof( *newResp ) ); + require_action( newResp, exit, err = kNoMemoryErr ); + + SockAddrCopy( &clientAddr, &newResp->clientAddr ); + newResp->targetTicks = UpTicks() + MillisecondsToUpTicks( (uint64_t) me->responseDelayMs ); + newResp->msgLen = responseLen; + newResp->msgPtr = responsePtr; + responsePtr = NULL; + + for( ptr = &me->responseList; ( resp = *ptr ) != NULL; ptr = &resp->next ) + { + if( newResp->targetTicks < resp->targetTicks ) break; + } + + newResp->next = resp; + *ptr = newResp; + + if( me->responseList == newResp ) + { + dispatch_source_forget( &me->responseTimer ); + + err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER, + ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue, + _DNSServerUDPDelayedSend, NULL, sockCtx, &me->responseTimer ); + require_noerr( err, exit ); + dispatch_resume( me->responseTimer ); + } + } + else + { + ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen ); + + n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen ); + err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n ); + require_noerr( err, exit ); + } + +exit: + FreeNullSafe( responsePtr ); + return; +} + +static void _DNSServerUDPDelayedSend( void *inContext ) +{ + OSStatus err; + SocketContext * const sockCtx = (SocketContext *) inContext; + DNSServerRef const me = (DNSServerRef) sockCtx->userContext; + DNSDelayedResponse * resp; + ssize_t n; + uint64_t nowTicks; + DNSDelayedResponse * freeList = NULL; + + dispatch_source_forget( &me->responseTimer ); + + nowTicks = UpTicks(); + while( ( resp = me->responseList ) != NULL ) + { + if( resp->targetTicks > nowTicks ) break; + me->responseList = resp->next; + + ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}", + resp->msgLen, resp->msgPtr, resp->msgLen ); + + n = sendto( sockCtx->sock, (char *) resp->msgPtr, resp->msgLen, 0, &resp->clientAddr.sa, + SockAddrGetSize( &resp->clientAddr ) ); + err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) resp->msgLen, n ); + check_noerr( err ); + + resp->next = freeList; + freeList = resp; + nowTicks = UpTicks(); + } + + if( ( resp = me->responseList ) != NULL ) + { + uint64_t remainingNs; + + remainingNs = UpTicksToNanoseconds( resp->targetTicks - nowTicks ); + if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX; + + err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0, + me->queue, _DNSServerUDPDelayedSend, NULL, sockCtx, &me->responseTimer ); + require_noerr( err, exit ); + dispatch_resume( me->responseTimer ); + } + +exit: + while( ( resp = freeList ) != NULL ) + { + freeList = resp->next; + DNSScheduledResponseFree( resp ); + } +} + +//=========================================================================================================================== +// _DNSServerAnswerQuery +//=========================================================================================================================== + +#define kLabelPrefix_Alias "alias" +#define kLabelPrefix_AliasTTL "alias-ttl" +#define kLabelPrefix_Count "count" +#define kLabelPrefix_TTL "ttl" +#define kLabel_IPv4 "ipv4" +#define kLabel_IPv6 "ipv6" + +#define kMaxAliasTTLCount ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 ) + +static OSStatus + _DNSServerInitializeResponseMessage( + DataBuffer * inDB, + unsigned int inID, + unsigned int inFlags, + const uint8_t * inQName, + unsigned int inQType, + unsigned int inQClass ); +static OSStatus + _DNSServerAnswerQueryDynamically( + const uint8_t * inQName, + unsigned int inQType, + unsigned int inQClass, + Boolean inForTCP, + DataBuffer * inDB ); + +static OSStatus + _DNSServerAnswerQuery( + const uint8_t * const inQueryPtr, + const size_t inQueryLen, + Boolean inForTCP, + uint8_t ** outResponsePtr, + size_t * outResponseLen ) +{ + OSStatus err; + DataBuffer dataBuf; + const uint8_t * ptr; + const uint8_t * const queryEnd = &inQueryPtr[ inQueryLen ]; + const DNSHeader * qhdr; + unsigned int msgID, qflags, qtype, qclass, rflags; + uint8_t qname[ kDomainNameLengthMax ]; + + DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize ); + + require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr ); + + qhdr = (const DNSHeader *) inQueryPtr; + msgID = DNSHeaderGetID( qhdr ); + qflags = DNSHeaderGetFlags( qhdr ); + + // Minimal checking of the query message's header. + + if( ( qflags & kDNSHeaderFlag_Response ) || // The message must be a query, not a response. + ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) || // OPCODE must be QUERY (standard query). + ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) ) // There should be a single question. + { + err = kRequestErr; + goto exit; + } + + // Get QNAME. + + ptr = (const uint8_t *) &qhdr[ 1 ]; + err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr ); + require_noerr( err, exit ); + + // Get QTYPE and QCLASS. + + require_action_quiet( ( queryEnd - ptr ) >= 4, exit, err = kUnderrunErr ); + qtype = DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields *) ptr ); + qclass = DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields *) ptr ); + ptr += 4; + + // Create a tentative response message. + + rflags = kDNSHeaderFlag_Response; + if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired; + DNSFlagsSetOpCode( rflags, kDNSOpCode_Query ); + + err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass ); + require_noerr( err, exit ); + + err = _DNSServerAnswerQueryDynamically( qname, qtype, qclass, inForTCP, &dataBuf ); + if( err ) + { + DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure ); + err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass ); + require_noerr( err, exit ); + } + + err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen ); + require_noerr( err, exit ); + +exit: + DataBuffer_Free( &dataBuf ); + return( err ); +} + +static OSStatus + _DNSServerInitializeResponseMessage( + DataBuffer * inDB, + unsigned int inID, + unsigned int inFlags, + const uint8_t * inQName, + unsigned int inQType, + unsigned int inQClass ) +{ + OSStatus err; + DNSHeader header; + DNSQuestionFixedFields fields; + + DataBuffer_Reset( inDB ); + + memset( &header, 0, sizeof( header ) ); + DNSHeaderSetID( &header, inID ); + DNSHeaderSetFlags( &header, inFlags ); + DNSHeaderSetQuestionCount( &header, 1 ); + + err = DataBuffer_Append( inDB, &header, sizeof( header ) ); + require_noerr( err, exit ); + + err = DataBuffer_Append( inDB, inQName, DomainNameLength( inQName ) ); + require_noerr( err, exit ); + + DNSQuestionFixedFieldsInit( &fields, inQType, inQClass ); + err = DataBuffer_Append( inDB, &fields, sizeof( fields ) ); + require_noerr( err, exit ); + +exit: + return( err ); +} + +static OSStatus + _DNSServerAnswerQueryDynamically( + const uint8_t * const inQName, + const unsigned int inQType, + const unsigned int inQClass, + const Boolean inForTCP, + DataBuffer * const inDB ) +{ + OSStatus err; // General-purpose error variable. + const uint8_t * labelPtr; // QNAME label pointer. + size_t labelLen; // QNAME label length. + DNSHeader * hdr; // Response header pointer. + unsigned int flags; // Response header flags. + unsigned int rcode; // Response header response code. + unsigned int answerCount = 0; // Number of answers contained in response. + int32_t aliasCount = -1; // Arg from "alias" label. Valid values are in [2 .. 2^31 - 1]. + int count = -1; // First arg from "count" label. Valid values are in [1 .. 255]. + int randCount = -1; // Second arg from "count" label. Valid values are in [1 .. 255]. + int32_t ttl = -1; // Arg from "ttl" label. Valid values are in [0 .. 2^31 - 1]. + uint32_t aliasTTLs[ kMaxAliasTTLCount ]; // Args from "alias-ttl" label. Valid values are in [0 .. 2^31 - 1]. + int i; // General-purpose array index. + Boolean useAliasTTLs = false; // True if QNAME contained a valid "alias-ttl" label. + Boolean nameExists = false; // True if name specified by QNAME exists. + Boolean nameHasA = false; // True if name specified by QNAME has an A record. + Boolean nameHasAAAA = false; // True if name specified by QNAME has a AAAA record. + Boolean notImplemented = false; // True if the kind of the query is not supported. + Boolean truncated = false; // True if the response message is truncated. + uint8_t namePtr[ 2 ]; // Name compression pointer. + + if( inQClass != kDNSServiceClass_IN ) + { + notImplemented = true; + goto done; + } + + for( labelPtr = inQName; ( labelLen = *labelPtr ) != 0; labelPtr += ( 1 + labelLen ) ) + { + const char * const labelStr = (const char *) &labelPtr[ 1 ]; + const char * next; + long long arg; + int n; + + require_action( labelLen <= kDomainNameLengthMax, exit, err = kUnexpectedErr ); + + // Check if the first label is a valid alias TTL sequence label. + + if( ( labelPtr == inQName ) && ( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_AliasTTL ) == 0 ) ) + { + const char * src = &labelStr[ sizeof_string( kLabelPrefix_AliasTTL ) ]; + const char * const end = &labelStr[ labelLen ]; + int argCount = 0; + + while( src < end ) + { + n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next ); + if( n != 1 ) break; + if( ( arg < 0 ) || ( arg > INT32_MAX ) ) break; // TTL must be >= 0 and <= (2^31 - 1). + aliasTTLs[ argCount++ ] = (uint32_t) arg; + src = next; + } + if( ( argCount > 0 ) && ( src == end ) ) + { + aliasCount = argCount; + useAliasTTLs = true; + continue; + } + } + + // Check if the first label is a valid alias label. + + if( ( labelPtr == inQName ) && ( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_Alias ) == 0 ) ) + { + const char * src = &labelStr[ sizeof_string( kLabelPrefix_Alias ) ]; + const char * const end = &labelStr[ labelLen ]; + + if( src == end ) + { + aliasCount = 1; + continue; + } + + n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next ); + if( ( n == 1 ) && ( next == end ) ) + { + if( ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be >= 2 and <= (2^31 - 1). + aliasCount = (int32_t) arg; + continue; + } + } + + // Check if the label is a valid count label. + + if( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_Count ) == 0 ) + { + const char * src = &labelStr[ sizeof_string( kLabelPrefix_Count ) ]; + const char * const end = &labelStr[ labelLen ]; + + n = SNScanF( src, (size_t)( end - src ), "-%3lld%#n", &arg, &next ); + if( n == 1 ) + { + if( count > 0 ) break; // Count cannot be specified more than once. + if( ( arg < 1 ) || ( arg > 255 ) ) break; // Count must be >= 1 and <= 255. + count = (int) arg; + + src = next; + if( src < end ) + { + n = SNScanF( src, (size_t)( end - src ), "-%3lld%#n", &arg, &next ); + if( ( n != 1 ) || ( next != end ) ) break; + if( ( arg < count ) || ( arg > 255 ) ) break; // Rand count must be >= count and <= 255. + randCount = (int) arg; + } + continue; + } + } + + // Check if the label is a valid tag label. + + if( strnicmp_prefix( labelStr, labelLen, "tag-" ) == 0 ) continue; + + // Check if the label is a valid TTL label. + + if( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_TTL ) == 0 ) + { + const char * src = &labelStr[ sizeof_string( kLabelPrefix_TTL ) ]; + const char * const end = &labelStr[ labelLen ]; + + n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next ); + if( ( n == 1 ) && ( next == end ) ) + { + if( ttl >= 0 ) break; // TTL cannot be specified more than once. + if( ( arg < 0 ) || ( arg > INT32_MAX ) ) break; // TTL must be >= 0 and <= (2^31 - 1). + ttl = (int32_t) arg; + continue; + } + } + + // Check if the label is a valid IPv4 or IPv6 label. + + if( MemIEqual( labelStr, labelLen, kLabel_IPv4, sizeof_string( kLabel_IPv4 ) ) ) + { + if( nameHasA || nameHasAAAA ) break; // Valid names have at most one IPv4 or IPv6 label. + nameHasA = true; + continue; + } + if( MemIEqual( labelStr, labelLen, kLabel_IPv6, sizeof_string( kLabel_IPv6 ) ) ) + { + if( nameHasA || nameHasAAAA ) break; // Valid names have at most one IPv4 or IPv6 label. + nameHasAAAA = true; + continue; + } + + // If the remaining labels are equal to "d.test.", the name exists. + + if( DomainNameEqual( labelPtr, (const uint8_t *) "\x01" "d" "\x04" "test" ) ) nameExists = true; + break; + } + require_quiet( nameExists, done ); + + // Set default values for count and TTL, if those labels were present. + + if( count <= 0 ) count = 1; + check( ( gDNSServer_DefaultTTL >= 0 ) && ( gDNSServer_DefaultTTL <= INT32_MAX ) ); + if( ttl < 0 ) ttl = gDNSServer_DefaultTTL; + + // Names that don't specify v4 or v6 have both A and AAAA records. + + if( !nameHasA && !nameHasAAAA ) + { + nameHasA = true; + nameHasAAAA = true; + } + + check( ( count >= 1 ) && ( count <= 255 ) ); + check( ( randCount <= 0 ) || ( ( randCount >= count ) && ( randCount <= 255 ) ) ); + + if( aliasCount > 0 ) + { + size_t nameOffset; + uint8_t rdataLabel[ 1 + kDomainLabelLengthMax + 1 ]; + + // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-". superPtr is a name + // compression pointer to the second label of QNAME, i.e., the immediate superdomain name of QNAME. It's used for + // the RDATA of CNAME records whose canonical name ends with the superdomain name. It may also be used to construct + // CNAME record names, when the offset to the previous CNAME's RDATA doesn't fit in a compression pointer. + + const uint8_t superPtr[ 2 ] = { 0xC0, (uint8_t)( kDNSHeaderLength + 1 + inQName[ 0 ] ) }; + + // The name of the first CNAME record is equal to QNAME, so nameOffset is set to offset of QNAME. + + nameOffset = kDNSHeaderLength; + + for( i = aliasCount; i >= 1; --i ) + { + size_t nameLen; + size_t rdataLen; + int j; + uint32_t aliasTTL; + uint8_t nameLabel[ 1 + kDomainLabelLengthMax + 1 ]; + DNSRecordFixedFields fields; + + if( nameOffset <= kDNSCompressionOffsetMax ) + { + namePtr[ 0 ] = (uint8_t)( ( ( nameOffset >> 8 ) & 0x3F ) | 0xC0 ); + namePtr[ 1 ] = (uint8_t)( nameOffset & 0xFF ); + + nameLen = sizeof( namePtr ); + } + else + { + memcpy( nameLabel, rdataLabel, 1 + rdataLabel[ 0 ] ); + nameLen = 1 + nameLabel[ 0 ] + sizeof( superPtr ); + } + + if( i >= 2 ) + { + char * dst = (char *) &rdataLabel[ 1 ]; + char * const end = (char *) &rdataLabel[ countof( rdataLabel ) ]; + + if( useAliasTTLs ) + { + err = SNPrintF_Add( &dst, end, kLabelPrefix_AliasTTL ); + require_noerr( err, exit ); + + for( j = aliasCount - ( i - 1 ); j < aliasCount; ++j ) + { + err = SNPrintF_Add( &dst, end, "-%u", aliasTTLs[ j ] ); + require_noerr( err, exit ); + } + } + else + { + err = SNPrintF_Add( &dst, end, kLabelPrefix_Alias "%?{end}-%u", i == 2, i - 1 ); + require_noerr( err, exit ); + } + rdataLabel[ 0 ] = (uint8_t)( dst - (char *) &rdataLabel[ 1 ] ); + rdataLen = 1 + rdataLabel[ 0 ] + sizeof( superPtr ); + } + else + { + rdataLen = sizeof( superPtr ); + } + + if( !inForTCP ) + { + size_t recordLen = nameLen + sizeof( fields ) + rdataLen; + + if( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) + { + truncated = true; + goto done; + } + } + ++answerCount; + + // Set CNAME record's NAME. + + if( nameOffset <= kDNSCompressionOffsetMax ) + { + err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) ); + require_noerr( err, exit ); + } + else + { + err = DataBuffer_Append( inDB, nameLabel, 1 + nameLabel[ 0 ] ); + require_noerr( err, exit ); + + err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) ); + require_noerr( err, exit ); + } + + // Set CNAME record's TYPE, CLASS, TTL, and RDLENGTH. + + aliasTTL = useAliasTTLs ? aliasTTLs[ aliasCount - i ] : ( (uint32_t) gDNSServer_DefaultTTL ); + DNSRecordFixedFieldsInit( &fields, kDNSServiceType_CNAME, kDNSServiceClass_IN, aliasTTL, rdataLen ); + err = DataBuffer_Append( inDB, &fields, sizeof( fields ) ); + require_noerr( err, exit ); + + // Save offset of CNAME record's RDATA, which may be used for the name of the next CNAME record. + + nameOffset = DataBuffer_GetLen( inDB ); + + // Set CNAME record's RDATA. + + if( i >= 2 ) + { + err = DataBuffer_Append( inDB, rdataLabel, 1 + rdataLabel[ 0 ] ); + require_noerr( err, exit ); + } + err = DataBuffer_Append( inDB, superPtr, sizeof( superPtr ) ); + require_noerr( err, exit ); + } + + namePtr[ 0 ] = superPtr[ 0 ]; + namePtr[ 1 ] = superPtr[ 1 ]; + } + else + { + // There are no aliases, so initialize the name compression pointer to point to QNAME. + + namePtr[ 0 ] = 0xC0; + namePtr[ 1 ] = kDNSHeaderLength; + } + + if( ( ( inQType == kDNSServiceType_A ) && nameHasA ) || + ( ( inQType == kDNSServiceType_AAAA ) && nameHasAAAA ) ) + { + uint8_t * lsb; // Pointer to the least significant byte of record data. + size_t recordLen; // Length of the entire record. + size_t rdataLen; // Length of record's RDATA. + uint8_t rdata[ 16 ]; // A buffer that's big enough for either A or AAAA RDATA. + uint8_t randItegers[ 255 ]; // Array for random integers in [1 .. 255]. + DNSRecordFixedFields fields; + + if( inQType == kDNSServiceType_A ) + { + rdataLen = 4; + WriteBig32( rdata, kTestDNSServerBaseAddrV4 ); + lsb = &rdata[ 3 ]; + } + else + { + rdataLen = 16; + memcpy( rdata, kTestDNSServerBaseAddrV6, 16 ); + lsb = &rdata[ 15 ]; + } + + if( randCount > 0 ) + { + // Populate the array with all integers between 1 and , inclusive. + + for( i = 0; i < randCount; ++i ) randItegers[ i ] = (uint8_t)( i + 1 ); + + // Create a contiguous subarray starting at index 0 that contains randomly chosen integers between + // 1 and , inclusive. + // Loop invariant 1: Array elements with indexes in [0 .. i - 1] have been randomly chosen. + // Loop invariant 2: Array elements with indexes in [i .. randCount - 1] are candidates for being chosen. + + for( i = 0; i < count; ++i ) + { + uint8_t tmp; + int j; + + j = (int) RandomRange( i, randCount - 1 ); + if( i != j ) + { + tmp = randItegers[ i ]; + randItegers[ i ] = randItegers[ j ]; + randItegers[ j ] = tmp; + } + } + } + + recordLen = sizeof( namePtr ) + sizeof( fields ) + rdataLen; + for( i = 0; i < count; ++i ) + { + if( !inForTCP && ( ( DataBuffer_GetLen( inDB ) + recordLen ) > kDNSMaxUDPMessageSize ) ) + { + truncated = true; + goto done; + } + ++answerCount; + + // Set record NAME. + + err = DataBuffer_Append( inDB, namePtr, sizeof( namePtr ) ); + require_noerr( err, exit ); + + // Set record TYPE, CLASS, TTL, and RDLENGTH. + + DNSRecordFixedFieldsInit( &fields, inQType, kDNSServiceClass_IN, ttl, rdataLen ); + err = DataBuffer_Append( inDB, &fields, sizeof( fields ) ); + require_noerr( err, exit ); + + // Set record RDATA. + + *lsb = ( randCount > 0 ) ? randItegers[ i ] : ( *lsb + 1 ); + + err = DataBuffer_Append( inDB, rdata, rdataLen ); + require_noerr( err, exit ); + } + } + +done: + hdr = (DNSHeader *) DataBuffer_GetPtr( inDB ); + flags = DNSHeaderGetFlags( hdr ); + if( truncated ) flags |= kDNSHeaderFlag_Truncation; + if( notImplemented ) + { + rcode = kDNSRCode_NotImplemented; + } + else + { + flags |= kDNSHeaderFlag_AuthAnswer; + rcode = nameExists ? kDNSRCode_NoError : kDNSRCode_NXDomain; + } + DNSFlagsSetRCode( flags, rcode ); + DNSHeaderSetFlags( hdr, flags ); + DNSHeaderSetAnswerCount( hdr, answerCount ); + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// _DNSServerTCPReadHandler +//=========================================================================================================================== + +typedef struct +{ + sockaddr_ip clientAddr; // Client's address. + dispatch_source_t readSource; // Dispatch read source for client socket. + dispatch_source_t writeSource; // Dispatch write source for client socket. + size_t offset; // Offset into receive buffer. + void * msgPtr; // Pointer to dynamically allocated message buffer. + size_t msgLen; // Length of message buffer. + Boolean readSuspended; // True if the read source is currently suspended. + Boolean writeSuspended; // True if the write source is currently suspended. + Boolean receivedLength; // True if receiving DNS message as opposed to the message length. + uint8_t lenBuf[ 2 ]; // Buffer for two-octet message length field. + iovec_t iov[ 2 ]; // IO vector for writing response message. + iovec_t * iovPtr; // Vector pointer for SocketWriteData(). + int iovCount; // Vector count for SocketWriteData(). + +} TCPConnectionContext; + +static void TCPConnectionStop( TCPConnectionContext *inContext ); +static void TCPConnectionContextFree( TCPConnectionContext *inContext ); +static void TCPConnectionReadHandler( void *inContext ); +static void TCPConnectionWriteHandler( void *inContext ); + +#define TCPConnectionForget( X ) ForgetCustomEx( X, TCPConnectionStop, TCPConnectionContextFree ) + +static void _DNSServerTCPReadHandler( void *inContext ) +{ + OSStatus err; + SocketContext * const sockCtx = (SocketContext *) inContext; + TCPConnectionContext * connection; + socklen_t clientAddrLen; + SocketRef newSock = kInvalidSocketRef; + SocketContext * newSockCtx = NULL; + + connection = (TCPConnectionContext *) calloc( 1, sizeof( *connection ) ); + require_action( connection, exit, err = kNoMemoryErr ); + + clientAddrLen = (socklen_t) sizeof( connection->clientAddr ); + newSock = accept( sockCtx->sock, &connection->clientAddr.sa, &clientAddrLen ); + err = map_socket_creation_errno( newSock ); + require_noerr( err, exit ); + + err = SocketContextCreate( newSock, connection, &newSockCtx ); + require_noerr( err, exit ); + newSock = kInvalidSocketRef; + + err = DispatchReadSourceCreate( newSockCtx->sock, NULL, TCPConnectionReadHandler, SocketContextCancelHandler, + newSockCtx, &connection->readSource ); + require_noerr( err, exit ); + SocketContextRetain( newSockCtx ); + dispatch_resume( connection->readSource ); + + err = DispatchWriteSourceCreate( newSockCtx->sock, NULL, TCPConnectionWriteHandler, SocketContextCancelHandler, + newSockCtx, &connection->writeSource ); + require_noerr( err, exit ); + SocketContextRetain( newSockCtx ); + connection->writeSuspended = true; + connection = NULL; + +exit: + ForgetSocket( &newSock ); + SocketContextRelease( newSockCtx ); + TCPConnectionForget( &connection ); +} + +//=========================================================================================================================== +// TCPConnectionStop +//=========================================================================================================================== + +static void TCPConnectionStop( TCPConnectionContext *inContext ) +{ + dispatch_source_forget_ex( &inContext->readSource, &inContext->readSuspended ); + dispatch_source_forget_ex( &inContext->writeSource, &inContext->writeSuspended ); +} + +//=========================================================================================================================== +// TCPConnectionContextFree +//=========================================================================================================================== + +static void TCPConnectionContextFree( TCPConnectionContext *inContext ) +{ + check( !inContext->readSource ); + check( !inContext->writeSource ); + ForgetMem( &inContext->msgPtr ); + free( inContext ); +} + +//=========================================================================================================================== +// TCPConnectionReadHandler +//=========================================================================================================================== + +static void TCPConnectionReadHandler( void *inContext ) +{ + OSStatus err; + SocketContext * const sockCtx = (SocketContext *) inContext; + TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext; + struct timeval now; + uint8_t * responsePtr = NULL; // malloc'd + size_t responseLen; + + // Receive message length. + + if( !connection->receivedLength ) + { + err = SocketReadData( sockCtx->sock, connection->lenBuf, sizeof( connection->lenBuf ), &connection->offset ); + if( err == EWOULDBLOCK ) goto exit; + require_noerr( err, exit ); + + connection->offset = 0; + connection->msgLen = ReadBig16( connection->lenBuf ); + connection->msgPtr = malloc( connection->msgLen ); + require_action( connection->msgPtr, exit, err = kNoMemoryErr ); + connection->receivedLength = true; + } + + // Receive message. + + err = SocketReadData( sockCtx->sock, connection->msgPtr, connection->msgLen, &connection->offset ); + if( err == EWOULDBLOCK ) goto exit; + require_noerr( err, exit ); + + gettimeofday( &now, NULL ); + dispatch_suspend( connection->readSource ); + connection->readSuspended = true; + + ds_ulog( kLogLevelInfo, "TCP server received %zu bytes from %##a at %{du:time}.\n", + connection->msgLen, &connection->clientAddr, &now ); + + if( connection->msgLen < kDNSHeaderLength ) + { + ds_ulog( kLogLevelInfo, "TCP DNS message is too small (%zu < %d).\n", connection->msgLen, kDNSHeaderLength ); + goto exit; + } + + ds_ulog( kLogLevelInfo, "TCP received message:\n\n%1{du:dnsmsg}", connection->msgPtr, connection->msgLen ); + + // Create response. + + err = _DNSServerAnswerQueryForTCP( connection->msgPtr, connection->msgLen, &responsePtr, &responseLen ); + require_noerr_quiet( err, exit ); + + // Send response. + + ds_ulog( kLogLevelInfo, "TCP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen ); + + free( connection->msgPtr ); + connection->msgPtr = responsePtr; + connection->msgLen = responseLen; + responsePtr = NULL; + + check( connection->msgLen <= UINT16_MAX ); + WriteBig16( connection->lenBuf, connection->msgLen ); + connection->iov[ 0 ].iov_base = connection->lenBuf; + connection->iov[ 0 ].iov_len = sizeof( connection->lenBuf ); + connection->iov[ 1 ].iov_base = connection->msgPtr; + connection->iov[ 1 ].iov_len = connection->msgLen; + + connection->iovPtr = connection->iov; + connection->iovCount = 2; + + check( connection->writeSuspended ); + dispatch_resume( connection->writeSource ); + connection->writeSuspended = false; + +exit: + FreeNullSafe( responsePtr ); + if( err && ( err != EWOULDBLOCK ) ) TCPConnectionForget( &connection ); +} + +//=========================================================================================================================== +// TCPConnectionWriteHandler +//=========================================================================================================================== + +static void TCPConnectionWriteHandler( void *inContext ) +{ + OSStatus err; + SocketContext * const sockCtx = (SocketContext *) inContext; + TCPConnectionContext * connection = (TCPConnectionContext *) sockCtx->userContext; + + err = SocketWriteData( sockCtx->sock, &connection->iovPtr, &connection->iovCount ); + if( err == EWOULDBLOCK ) goto exit; + check_noerr( err ); + + TCPConnectionForget( &connection ); + +exit: + return; +} + +//=========================================================================================================================== +// GAIPerfCmd +//=========================================================================================================================== + +#define kGAIPerfStandardTTL ( 1 * kSecondsPerHour ) + +typedef struct GAITesterPrivate * GAITesterRef; +typedef struct GAITestCase GAITestCase; + +typedef uint32_t GAITesterEventType; +#define kGAITesterEvent_Started 1 +#define kGAITesterEvent_Stopped 2 + +typedef struct +{ + const char * name; // Domain name that was resolved. + int64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection. + int64_t firstTimeUs; // Time in microseconds that it took to get the first address result. + int64_t timeUs; // Time in microseconds that it took to get all expected address results. + +} GAITestItemResult; + +typedef void ( *GAITesterEventHandler_f )( GAITesterEventType inType, void *inContext ); +typedef void + ( *GAITesterResultsHandler_f )( + const char * inCaseTitle, + MicroTime64 inCaseStartTime, + MicroTime64 inCaseEndTime, + const GAITestItemResult * inResults, + size_t inResultCount, + size_t inItemCount, + void * inContext ); + +typedef unsigned int GAITestAddrType; +#define kGAITestAddrType_None 0 +#define kGAITestAddrType_IPv4 ( 1U << 0 ) +#define kGAITestAddrType_IPv6 ( 1U << 1 ) +#define kGAITestAddrType_Both ( kGAITestAddrType_IPv4 | kGAITestAddrType_IPv6 ) + +#define GAITestAddrTypeIsValid( X ) \ + ( ( (X) & kGAITestAddrType_Both ) && ( ( (X) & ~kGAITestAddrType_Both ) == 0 ) ) + +typedef enum +{ + kGAIPerfOutputFormat_JSON = 1, + kGAIPerfOutputFormat_XML = 2, + kGAIPerfOutputFormat_Binary = 3 + +} GAIPerfOutputFormatType; + +typedef struct +{ + GAITesterRef tester; // GAI tester object. + CFMutableArrayRef caseResults; // Array of test case results. + char * outputFilePath; // File to write test results to. If NULL, then write to stdout. + GAIPerfOutputFormatType outputFormat; // Format of test results output. + unsigned int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo(). + unsigned int serverDelayMs; // Amount of additional time to have server delay its responses. + unsigned int defaultIterCount; // Default test case iteration count. + dispatch_source_t sigIntSource; // Dispatch source for SIGINT. + dispatch_source_t sigTermSource; // Dispatch source for SIGTERM. + Boolean gotSignal; // True if SIGINT or SIGTERM was caught. + Boolean testerStarted; // True if the GAI tester was started. + Boolean appendNewLine; // True if a newline character should be appended to JSON output. + +} GAIPerfContext; + +static void GAIPerfContextFree( GAIPerfContext *inContext ); +static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext ); +static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext ); +static void GAIPerfEventHandler( GAITesterEventType inType, void *inContext ); +static void + GAIPerfResultsHandler( + const char * inCaseTitle, + MicroTime64 inCaseStartTime, + MicroTime64 inCaseEndTime, + const GAITestItemResult * inResults, + size_t inResultCount, + size_t inItemCount, + void * inContext ); +static void GAIPerfSignalHandler( void *inContext ); + +CFTypeID GAITesterGetTypeID( void ); +static OSStatus + GAITesterCreate( + dispatch_queue_t inQueue, + int inCallDelayMs, + int inServerDelayMs, + int inServerDefaultTTL, + GAITesterRef * outTester ); +static void GAITesterStart( GAITesterRef inTester ); +static void GAITesterStop( GAITesterRef inTester ); +static void GAITesterAddCase( GAITesterRef inTester, GAITestCase *inCase ); +static void + GAITesterSetEventHandler( + GAITesterRef inTester, + GAITesterEventHandler_f inEventHandler, + void * inEventContext ); +static void + GAITesterSetResultsHandler( + GAITesterRef inTester, + GAITesterResultsHandler_f inResultsHandler, + void * inResultsContext ); + +static OSStatus GAITestCaseCreate( const char *inTitle, unsigned int inTimeLimitMs, GAITestCase **outSet ); +static void GAITestCaseFree( GAITestCase *inCase ); +static OSStatus + GAITestCaseAddItem( + GAITestCase * inCase, + unsigned int inAliasCount, + unsigned int inAddressCount, + int inTTL, + GAITestAddrType inHasAddrs, + GAITestAddrType inWantAddrs, + unsigned int inItemCount ); +static OSStatus GAITestCaseAddLocalHostItem( GAITestCase *inCase, GAITestAddrType inWantAddrs, unsigned int inItemCount ); + +#define kGAIPerfTestSuite_Basic 1 +#define kGAIPerfTestSuite_Advanced 2 + +static void GAIPerfCmd( void ) +{ + OSStatus err; + GAIPerfContext * context; + int suiteValue; + + context = (GAIPerfContext *) calloc( 1, sizeof( *context ) ); + require_action( context, exit, err = kNoMemoryErr ); + + context->caseResults = CFArrayCreateMutable( NULL, 0, &kCFTypeArrayCallBacks ); + require_action( context->caseResults, exit, err = kNoMemoryErr ); + + context->outputFormat = (GAIPerfOutputFormatType) CLIArgToValue( "format", gGAIPerf_OutputFormat, &err, + "json", kGAIPerfOutputFormat_JSON, + "xml", kGAIPerfOutputFormat_XML, + "binary", kGAIPerfOutputFormat_Binary, + NULL ); + require_noerr_quiet( err, exit ); + + context->callDelayMs = ( gGAIPerf_CallDelayMs >= 0 ) ? (unsigned int) gGAIPerf_CallDelayMs : 0; + context->serverDelayMs = ( gGAIPerf_ServerDelayMs >= 0 ) ? (unsigned int) gGAIPerf_ServerDelayMs : 0; + context->defaultIterCount = ( gGAIPerf_DefaultIterCount >= 0 ) ? (unsigned int) gGAIPerf_DefaultIterCount : 0; + context->appendNewLine = gGAIPerf_OutputAppendNewLine ? true : false; + + if( gGAIPerf_OutputFilePath ) + { + context->outputFilePath = strdup( gGAIPerf_OutputFilePath ); + require_action( context->outputFilePath, exit, err = kNoMemoryErr ); + } + + err = GAITesterCreate( dispatch_get_main_queue(), (int) context->callDelayMs, (int) context->serverDelayMs, + kGAIPerfStandardTTL, &context->tester ); + require_noerr( err, exit ); + + check( gGAIPerf_TestSuite ); + suiteValue = CLIArgToValue( "suite", gGAIPerf_TestSuite, &err, + "basic", kGAIPerfTestSuite_Basic, + "advanced", kGAIPerfTestSuite_Advanced, + NULL ); + require_noerr_quiet( err, exit ); + + switch( suiteValue ) + { + case kGAIPerfTestSuite_Basic: + err = GAIPerfAddBasicTestCases( context ); + require_noerr( err, exit ); + break; + + case kGAIPerfTestSuite_Advanced: + err = GAIPerfAddAdvancedTestCases( context ); + require_noerr( err, exit ); + break; + + default: + err = kValueErr; + break; + } + + GAITesterSetEventHandler( context->tester, GAIPerfEventHandler, context ); + GAITesterSetResultsHandler( context->tester, GAIPerfResultsHandler, context ); + + signal( SIGINT, SIG_IGN ); + err = DispatchSignalSourceCreate( SIGINT, GAIPerfSignalHandler, context, &context->sigIntSource ); + require_noerr( err, exit ); + dispatch_resume( context->sigIntSource ); + + signal( SIGTERM, SIG_IGN ); + err = DispatchSignalSourceCreate( SIGTERM, GAIPerfSignalHandler, context, &context->sigTermSource ); + require_noerr( err, exit ); + dispatch_resume( context->sigTermSource ); + + GAITesterStart( context->tester ); + dispatch_main(); + +exit: + if( context ) GAIPerfContextFree( context ); + if( err ) exit( 1 ); +} + +//=========================================================================================================================== +// GAIPerfContextFree +//=========================================================================================================================== + +static void GAIPerfContextFree( GAIPerfContext *inContext ) +{ + ForgetCF( &inContext->tester ); + ForgetCF( &inContext->caseResults ); + ForgetMem( &inContext->outputFilePath ); + dispatch_source_forget( &inContext->sigIntSource ); + dispatch_source_forget( &inContext->sigTermSource ); + free( inContext ); +} + +//=========================================================================================================================== +// GAIPerfAddAdvancedTestCases +//=========================================================================================================================== + +#define kTestCaseTitleBufferSize 128 + +static void + _GAIPerfWriteTestCaseTitle( + char inBuffer[ kTestCaseTitleBufferSize ], + unsigned int inCNAMERecordCount, + unsigned int inARecordCount, + unsigned int inAAAARecordCount, + GAITestAddrType inRequested, + unsigned int inIterationCount, + Boolean inIterationsAreUnique ); +static void + _GAIPerfWriteLocalHostTestCaseTitle( + char inBuffer[ kTestCaseTitleBufferSize ], + GAITestAddrType inRequested, + unsigned int inIterationCount ); +static unsigned int + _GAIPerfTimeLimitMs( + unsigned int inCallDelayMs, + unsigned int inServerDelayMs, + unsigned int inIterationCount ); + +#define kGAIPerfAdvancedTestSuite_MaxAliasCount 4 +#define kGAIPerfAdvancedTestSuite_MaxAddrCount 8 + +static OSStatus GAIPerfAddAdvancedTestCases( GAIPerfContext *inContext ) +{ + OSStatus err; + unsigned int aliasCount, addressCount, timeLimitMs, i; + GAITestCase * testCase = NULL; + char title[ kTestCaseTitleBufferSize ]; + + aliasCount = 0; + while( aliasCount <= kGAIPerfAdvancedTestSuite_MaxAliasCount ) + { + for( addressCount = 1; addressCount <= kGAIPerfAdvancedTestSuite_MaxAddrCount; addressCount *= 2 ) + { + // Add a test case to resolve a domain name with + // + // CNAME records, A records, and AAAA records + // + // to its IPv4 and IPv6 addresses. Each iteration resolves a unique instance of such a domain name, which + // requires server queries. + + _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both, + inContext->defaultIterCount, true ); + + timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs, + inContext->defaultIterCount ); + err = GAITestCaseCreate( title, timeLimitMs, &testCase ); + require_noerr( err, exit ); + + for( i = 0; i < inContext->defaultIterCount; ++i ) + { + err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL, + kGAITestAddrType_Both, kGAITestAddrType_Both, 1 ); + require_noerr( err, exit ); + } + + GAITesterAddCase( inContext->tester, testCase ); + testCase = NULL; + + // Add a test case to resolve a domain name with + // + // CNAME records, A records, and AAAA records + // + // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique domain name, which requires a server + // query. The subsequent iterations resolve the same domain name as the preliminary iteration, which should + // ideally require no server queries, i.e., the results should come from the cache. + + _GAIPerfWriteTestCaseTitle( title, aliasCount, addressCount, addressCount, kGAITestAddrType_Both, + inContext->defaultIterCount, false ); + + timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs, 1 ) + + _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount ); + err = GAITestCaseCreate( title, timeLimitMs, &testCase ); + require_noerr( err, exit ); + + err = GAITestCaseAddItem( testCase, aliasCount, addressCount, kGAIPerfStandardTTL, + kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->defaultIterCount + 1 ); + require_noerr( err, exit ); + + GAITesterAddCase( inContext->tester, testCase ); + testCase = NULL; + } + + if( aliasCount == 0 ) aliasCount = 1; + else aliasCount *= 2; + } + + // Finally, add a test case to resolve localhost to its IPv4 and IPv6 addresses. + + _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount ); + + timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount ); + err = GAITestCaseCreate( title, timeLimitMs, &testCase ); + require_noerr( err, exit ); + + err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->defaultIterCount ); + require_noerr( err, exit ); + + GAITesterAddCase( inContext->tester, testCase ); + testCase = NULL; + +exit: + if( testCase ) GAITestCaseFree( testCase ); + return( err ); +} + +//=========================================================================================================================== +// _GAIPerfWriteTestCaseTitle +//=========================================================================================================================== + +#define GAITestAddrTypeToRequestKeyValue( X ) ( \ + ( (X) == kGAITestAddrType_Both ) ? "ipv4\\,ipv6" : \ + ( (X) == kGAITestAddrType_IPv4 ) ? "ipv4" : \ + ( (X) == kGAITestAddrType_IPv6 ) ? "ipv6" : \ + "" ) + +static void + _GAIPerfWriteTestCaseTitle( + char inBuffer[ kTestCaseTitleBufferSize ], + unsigned int inCNAMERecordCount, + unsigned int inARecordCount, + unsigned int inAAAARecordCount, + GAITestAddrType inRequested, + unsigned int inIterationCount, + Boolean inIterationsAreUnique ) +{ + SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=dynamic,cname=%u,a=%u,aaaa=%u,req=%s,iterations=%u%?s", + inCNAMERecordCount, inARecordCount, inAAAARecordCount, GAITestAddrTypeToRequestKeyValue( inRequested ), + inIterationCount, inIterationsAreUnique, ",unique" ); +} + +//=========================================================================================================================== +// _GAIPerfWriteLocalHostTestCaseTitle +//=========================================================================================================================== + +static void + _GAIPerfWriteLocalHostTestCaseTitle( + char inBuffer[ kTestCaseTitleBufferSize ], + GAITestAddrType inRequested, + unsigned int inIterationCount ) +{ + SNPrintF( inBuffer, kTestCaseTitleBufferSize, "name=localhost,req=%s,iterations=%u", + GAITestAddrTypeToRequestKeyValue( inRequested ), inIterationCount ); +} + +//=========================================================================================================================== +// _GAIPerfTimeLimitMs +//=========================================================================================================================== + +static unsigned int + _GAIPerfTimeLimitMs( + unsigned int inCallDelayMs, + unsigned int inServerDelayMs, + unsigned int inIterationCount ) +{ + // Allow each iteration 20 ms to complete (in addition to the call and server delay times). + + return( ( inCallDelayMs + inServerDelayMs + 20 ) * inIterationCount ); +} + +//=========================================================================================================================== +// GAIPerfAddBasicTestCases +//=========================================================================================================================== + +#define kGAIPerfBasicTestSuite_AliasCount 2 +#define kGAIPerfBasicTestSuite_AddrCount 4 + +static OSStatus GAIPerfAddBasicTestCases( GAIPerfContext *inContext ) +{ + OSStatus err; + GAITestCase * testCase = NULL; + char title[ kTestCaseTitleBufferSize ]; + unsigned int timeLimitMs, i; + + // Test Case #1: + // Resolve a domain name with + // + // 2 CNAME records, 4 A records, and 4 AAAA records + // + // to its IPv4 and IPv6 addresses. Each of the iterations resolves a unique domain name, which requires server + // queries. + + _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount, + kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both, + inContext->defaultIterCount, true ); + + timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs, inContext->defaultIterCount ); + err = GAITestCaseCreate( title, timeLimitMs, &testCase ); + require_noerr( err, exit ); + + for( i = 0; i < inContext->defaultIterCount; ++i ) + { + err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount, + kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, 1 ); + require_noerr( err, exit ); + } + + GAITesterAddCase( inContext->tester, testCase ); + testCase = NULL; + + // Test Case #2: + // Resolve a domain name with + // + // 2 CNAME records, 4 A records, and 4 AAAA records + // + // to its IPv4 and IPv6 addresses. A preliminary iteration resolves a unique instance of such a domain name, which + // requires server queries. Each of the subsequent iterations resolves the same domain name as the preliminary + // iteration, which should ideally require no additional server queries, i.e., the results should come from the cache. + + _GAIPerfWriteTestCaseTitle( title, kGAIPerfBasicTestSuite_AliasCount, + kGAIPerfBasicTestSuite_AddrCount, kGAIPerfBasicTestSuite_AddrCount, kGAITestAddrType_Both, + inContext->defaultIterCount, false ); + + timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, inContext->serverDelayMs, 1 ) + + _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount ); + err = GAITestCaseCreate( title, timeLimitMs, &testCase ); + require_noerr( err, exit ); + + err = GAITestCaseAddItem( testCase, kGAIPerfBasicTestSuite_AliasCount, kGAIPerfBasicTestSuite_AddrCount, + kGAIPerfStandardTTL, kGAITestAddrType_Both, kGAITestAddrType_Both, inContext->defaultIterCount + 1 ); + require_noerr( err, exit ); + + GAITesterAddCase( inContext->tester, testCase ); + testCase = NULL; + + // Test Case #3: + // Each iteration resolves localhost to its IPv4 and IPv6 addresses. + + _GAIPerfWriteLocalHostTestCaseTitle( title, kGAITestAddrType_Both, inContext->defaultIterCount ); + + timeLimitMs = _GAIPerfTimeLimitMs( inContext->callDelayMs, 0, inContext->defaultIterCount ); + err = GAITestCaseCreate( title, timeLimitMs, &testCase ); + require_noerr( err, exit ); + + err = GAITestCaseAddLocalHostItem( testCase, kGAITestAddrType_Both, inContext->defaultIterCount ); + require_noerr( err, exit ); + + GAITesterAddCase( inContext->tester, testCase ); + testCase = NULL; + +exit: + if( testCase ) GAITestCaseFree( testCase ); + return( err ); +} + +//=========================================================================================================================== +// GAIPerfEventHandler +//=========================================================================================================================== + +static void _GAIPerfOutputResultsAndExit( GAIPerfContext *inContext ) ATTRIBUTE_NORETURN; + +static void GAIPerfEventHandler( GAITesterEventType inType, void *inContext ) +{ + GAIPerfContext * const context = (GAIPerfContext *) inContext; + + if( inType == kGAITesterEvent_Started ) + { + context->testerStarted = true; + } + else if( inType == kGAITesterEvent_Stopped ) + { + if( context->gotSignal ) exit( 1 ); + _GAIPerfOutputResultsAndExit( context ); + } +} + +//=========================================================================================================================== +// _GAIPerfOutputResultsAndExit +//=========================================================================================================================== + +#define kGAIPerfResultsKey_TestCases CFSTR( "testCases" ) +#define kGAIPerfResultsKey_Info CFSTR( "info" ) + +#define kGAIPerfInfoKey_CallDelay CFSTR( "callDelayMs" ) +#define kGAIPerfInfoKey_ServerDelay CFSTR( "serverDelayMs" ) + +static void _GAIPerfOutputResultsAndExit( GAIPerfContext *inContext ) +{ + OSStatus err; + CFPropertyListRef plist = NULL; + CFDataRef results = NULL; + FILE * file = NULL; + + err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist, + "{" + "%kO=%O" + "%kO=" + "{" + "%kO=%lli" + "%kO=%lli" + "}" + "}", + kGAIPerfResultsKey_TestCases, inContext->caseResults, + kGAIPerfResultsKey_Info, + kGAIPerfInfoKey_CallDelay, (int64_t) inContext->callDelayMs, + kGAIPerfInfoKey_ServerDelay, (int64_t) inContext->serverDelayMs ); + require_noerr( err, exit ); + + // Convert results to a specific format. + + switch( inContext->outputFormat ) + { + case kGAIPerfOutputFormat_JSON: + results = CFCreateJSONData( plist, kJSONFlags_None, NULL ); + require_action( results, exit, err = kUnknownErr ); + break; + + case kGAIPerfOutputFormat_XML: + results = CFPropertyListCreateData( NULL, plist, kCFPropertyListXMLFormat_v1_0, 0, NULL ); + require_action( results, exit, err = kUnknownErr ); + break; + + case kGAIPerfOutputFormat_Binary: + results = CFPropertyListCreateData( NULL, plist, kCFPropertyListBinaryFormat_v1_0, 0, NULL ); + require_action( results, exit, err = kUnknownErr ); + break; + + default: + err = kValueErr; + goto exit; + } + + // Write formatted results to file or stdout. + + if( inContext->outputFilePath ) + { + file = fopen( inContext->outputFilePath, "wb" ); + err = map_global_value_errno( file, file ); + require_noerr( err, exit ); + } + else + { + file = stdout; + } + + err = WriteANSIFile( file, CFDataGetBytePtr( results ), (size_t) CFDataGetLength( results ) ); + require_noerr( err, exit ); + + // Write a trailing newline for JSON-formatted results if requested. + + if( ( inContext->outputFormat == kGAIPerfOutputFormat_JSON ) && inContext->appendNewLine ) + { + err = WriteANSIFile( file, "\n", 1 ); + require_noerr( err, exit ); + } + +exit: + CFReleaseNullSafe( plist ); + CFReleaseNullSafe( results ); + if( file && ( file != stdout ) ) fclose( file ); + GAIPerfContextFree( inContext ); + exit( err ? 1 : 0 ); +} + +//=========================================================================================================================== +// GAIPerfResultsHandler +//=========================================================================================================================== + +// Keys for test case dictionary + +#define kGAIPerfTestCaseKey_Title CFSTR( "title" ) +#define kGAIPerfTestCaseKey_StartTime CFSTR( "startTimeUs" ) +#define kGAIPerfTestCaseKey_EndTime CFSTR( "endTimeUs" ) +#define kGAIPerfTestCaseKey_Results CFSTR( "results" ) +#define kGAIPerfTestCaseKey_FirstStats CFSTR( "firstStats" ) +#define kGAIPerfTestCaseKey_ConnectionStats CFSTR( "connectionStats" ) +#define kGAIPerfTestCaseKey_Stats CFSTR( "stats" ) +#define kGAIPerfTestCaseKey_TimedOut CFSTR( "timedOut" ) + +// Keys for test case results array entry dictionaries + +#define kGAIPerfTestCaseResultKey_Name CFSTR( "name" ) +#define kGAIPerfTestCaseResultKey_ConnectionTime CFSTR( "connectionTimeUs" ) +#define kGAIPerfTestCaseResultKey_FirstTime CFSTR( "firstTimeUs" ) +#define kGAIPerfTestCaseResultKey_Time CFSTR( "timeUs" ) + +// Keys for test case stats dictionaries + +#define kGAIPerfTestCaseStatsKey_Count CFSTR( "count" ) +#define kGAIPerfTestCaseStatsKey_Min CFSTR( "min" ) +#define kGAIPerfTestCaseStatsKey_Max CFSTR( "max" ) +#define kGAIPerfTestCaseStatsKey_Mean CFSTR( "mean" ) +#define kGAIPerfTestCaseStatsKey_StdDev CFSTR( "sd" ) + +typedef struct +{ + double min; + double max; + double mean; + double stdDev; + +} GAIPerfStats; + +#define GAIPerfStatsInit( X ) \ + do { (X)->min = DBL_MAX; (X)->max = DBL_MIN; (X)->mean = 0.0; (X)->stdDev = 0.0; } while( 0 ) + +static void + GAIPerfResultsHandler( + const char * inCaseTitle, + MicroTime64 inCaseStartTime, + MicroTime64 inCaseEndTime, + const GAITestItemResult * inResults, + size_t inResultCount, + size_t inItemCount, + void * inContext ) +{ + OSStatus err; + GAIPerfContext * const context = (GAIPerfContext *) inContext; + int namesAreDynamic, namesAreUnique; + const char * ptr; + size_t count, startIndex; + CFMutableArrayRef results = NULL; + GAIPerfStats stats, firstStats, connStats; + double sum, firstSum, connSum, value, diff; + size_t keyValueLen, i; + char keyValue[ 16 ]; // Size must be at least strlen( "name=dynamic" ) + 1 bytes. + + // If this test case resolves the same "d.test." name in each iteration (title contains the "name=dynamic" key-value + // pair, but not the "unique" key), then don't count the first iteration, whose purpose is to populate the cache with the + // domain name's CNAME, A, and AAAA records. + + namesAreDynamic = false; + namesAreUnique = false; + ptr = inCaseTitle; + while( ParseQuotedEscapedString( ptr, NULL, ",", keyValue, sizeof( keyValue ), &keyValueLen, NULL, &ptr ) ) + { + if( strnicmpx( keyValue, keyValueLen, "name=dynamic" ) == 0 ) + { + namesAreDynamic = true; + } + else if( strnicmpx( keyValue, keyValueLen, "unique" ) == 0 ) + { + namesAreUnique = true; + } + if( namesAreDynamic && namesAreUnique ) break; + } + + if( namesAreDynamic && !namesAreUnique && ( inItemCount > 0 ) ) + { + count = ( inResultCount > 0 ) ? ( inResultCount - 1 ) : 0; + startIndex = 1; + } + else + { + count = inResultCount; + startIndex = 0; + } + + results = CFArrayCreateMutable( NULL, (CFIndex) count, &kCFTypeArrayCallBacks ); + require_action( results, exit, err = kNoMemoryErr ); + + GAIPerfStatsInit( &stats ); + GAIPerfStatsInit( &firstStats ); + GAIPerfStatsInit( &connStats ); + + sum = 0.0; + firstSum = 0.0; + connSum = 0.0; + for( i = startIndex; i < count; ++i ) + { + value = (double) inResults[ i ].timeUs; + if( value < stats.min ) stats.min = value; + if( value > stats.max ) stats.max = value; + sum += value; + + value = (double) inResults[ i ].firstTimeUs; + if( value < firstStats.min ) firstStats.min = value; + if( value > firstStats.max ) firstStats.max = value; + firstSum += value; + + value = (double) inResults[ i ].connectionTimeUs; + if( value < connStats.min ) connStats.min = value; + if( value > connStats.max ) connStats.max = value; + connSum += value; + + err = CFPropertyListAppendFormatted( kCFAllocatorDefault, results, + "{" + "%kO=%s" + "%kO=%lli" + "%kO=%lli" + "%kO=%lli" + "}", + kGAIPerfTestCaseResultKey_Name, inResults[ i ].name, + kGAIPerfTestCaseResultKey_ConnectionTime, inResults[ i ].connectionTimeUs, + kGAIPerfTestCaseResultKey_FirstTime, inResults[ i ].firstTimeUs, + kGAIPerfTestCaseResultKey_Time, inResults[ i ].timeUs ); + require_noerr( err, exit ); + } + + if( count > 0 ) + { + stats.mean = sum / count; + firstStats.mean = firstSum / count; + connStats.mean = connSum / count; + + sum = 0.0; + firstSum = 0.0; + connSum = 0.0; + for( i = startIndex; i < count; ++i ) + { + diff = stats.mean - (double) inResults[ i ].timeUs; + sum += ( diff * diff ); + + diff = firstStats.mean - (double) inResults[ i ].firstTimeUs; + firstSum += ( diff * diff ); + + diff = connStats.mean - (double) inResults[ i ].connectionTimeUs; + connSum += ( diff * diff ); + } + stats.stdDev = sqrt( sum / count ); + firstStats.stdDev = sqrt( firstSum / count ); + connStats.stdDev = sqrt( connSum / count ); + } + + err = CFPropertyListAppendFormatted( kCFAllocatorDefault, context->caseResults, + "{" + "%kO=%s" + "%kO=%lli" + "%kO=%lli" + "%kO=%O" + "%kO=" + "{" + "%kO=%lli" + "%kO=%f" + "%kO=%f" + "%kO=%f" + "%kO=%f" + "}" + "%kO=" + "{" + "%kO=%lli" + "%kO=%f" + "%kO=%f" + "%kO=%f" + "%kO=%f" + "}" + "%kO=" + "{" + "%kO=%lli" + "%kO=%f" + "%kO=%f" + "%kO=%f" + "%kO=%f" + "}" + "%kO=%b" + "}", + kGAIPerfTestCaseKey_Title, inCaseTitle, + kGAIPerfTestCaseKey_StartTime, (int64_t) inCaseStartTime, + kGAIPerfTestCaseKey_EndTime, (int64_t) inCaseEndTime, + kGAIPerfTestCaseKey_Results, results, + kGAIPerfTestCaseKey_Stats, + kGAIPerfTestCaseStatsKey_Count, (int64_t) count, + kGAIPerfTestCaseStatsKey_Min, stats.min, + kGAIPerfTestCaseStatsKey_Max, stats.max, + kGAIPerfTestCaseStatsKey_Mean, stats.mean, + kGAIPerfTestCaseStatsKey_StdDev, stats.stdDev, + kGAIPerfTestCaseKey_FirstStats, + kGAIPerfTestCaseStatsKey_Count, (int64_t) count, + kGAIPerfTestCaseStatsKey_Min, firstStats.min, + kGAIPerfTestCaseStatsKey_Max, firstStats.max, + kGAIPerfTestCaseStatsKey_Mean, firstStats.mean, + kGAIPerfTestCaseStatsKey_StdDev, firstStats.stdDev, + kGAIPerfTestCaseKey_ConnectionStats, + kGAIPerfTestCaseStatsKey_Count, (int64_t) count, + kGAIPerfTestCaseStatsKey_Min, connStats.min, + kGAIPerfTestCaseStatsKey_Max, connStats.max, + kGAIPerfTestCaseStatsKey_Mean, connStats.mean, + kGAIPerfTestCaseStatsKey_StdDev, connStats.stdDev, + kGAIPerfTestCaseKey_TimedOut, ( inResultCount < inItemCount ) ? true : false ); + require_noerr( err, exit ); + +exit: + CFReleaseNullSafe( results ); +} + +//=========================================================================================================================== +// GAIPerfSignalHandler +//=========================================================================================================================== + +static void GAIPerfSignalHandler( void *inContext ) +{ + GAIPerfContext * const context = (GAIPerfContext *) inContext; + + context->gotSignal = true; + if( context->tester && context->testerStarted ) + { + GAITesterStop( context->tester ); + } + else + { + exit( 1 ); + } +} + +//=========================================================================================================================== +// GAITesterCreate +//=========================================================================================================================== + +typedef enum +{ + kGAITestConnType_UseMainConnection = 1, + kGAITestConnType_OwnSharedConnection = 2 + +} GAITestConnType; + +typedef struct GAITestItem GAITestItem; +struct GAITestItem +{ + GAITestItem * next; // Next test item in list. + char * name; // Domain name to resolve. + int64_t connectionTimeUs; // Time in microseconds that it took to create a DNS-SD connection. + int64_t firstTimeUs; // Time in microseconds that it took to get the first address result. + int64_t timeUs; // Time in microseconds that it took to get all expected address results. + unsigned int addressCount; // Address count of the domain name, i.e., the Count label argument. + Boolean hasV4; // True if the domain name has one or more IPv4 addresses. + Boolean hasV6; // True if the domain name has one or more IPv6 addresses. + Boolean wantV4; // True if DNSServiceGetAddrInfo() should be called to get IPv4 addresses. + Boolean wantV6; // True if DNSServiceGetAddrInfo() should be called to get IPv6 addresses. +}; + +struct GAITestCase +{ + GAITestCase * next; // Next test case in list. + GAITestItem * itemList; // List of test items. + char * title; // Title of the test case. + unsigned int timeLimitMs; // Time limit in milliseconds for the test case's completion. +}; + +struct GAITesterPrivate +{ + CFRuntimeBase base; // CF object base. + dispatch_queue_t queue; // Serial work queue. + DNSServiceRef mainRef; // Reference to the main shared DNS-SD connection. + DNSServiceRef opRef; // Reference to the current DNSServiceGetAddrInfo operation. + GAITestCase * caseList; // List of test cases. + GAITestCase * currentCase; // Pointer to the current test case. + GAITestItem * currentItem; // Pointer to the current test item. + MicroTime64 caseStartTime; // Start time of current test case in Unix time as microseconds. + MicroTime64 caseEndTime; // End time of current test case in Unix time as microseconds. + Boolean started; // True if the tester has been successfully started. + Boolean stopped; // True if the tester has been stopped. + int callDelayMs; // Amount of time to wait before calling DNSServiceGetAddrInfo(). + dispatch_source_t caseTimer; // Timer for enforcing a test case time limits. + pcap_t * pcap; // Captures traffic between mDNSResponder and test DNS server. + pid_t serverPID; // PID of the test DNS server. + int serverDelayMs; // Additional time to have the server delay its responses by. + int serverDefaultTTL; // Default TTL for the server's records. + GAITesterEventHandler_f eventHandler; // User's event handler. + void * eventContext; // User's event handler context. + GAITesterResultsHandler_f resultsHandler; // User's results handler. + void * resultsContext; // User's results handler context. + + // Variables for current test item. + + uint64_t bitmapV4; // Bitmap of IPv4 results that have yet to be received. + uint64_t bitmapV6; // Bitmap of IPv6 results that have yet to be received. + uint64_t startTicks; // Start ticks of DNSServiceGetAddrInfo(). + uint64_t connTicks; // Ticks when the connection was created. + uint64_t firstTicks; // Ticks when the first DNSServiceGetAddrInfo result was received. + uint64_t endTicks; // Ticks when the last DNSServiceGetAddrInfo result was received. + Boolean gotFirstResult; // True if the first result has been received. +}; + +CF_CLASS_DEFINE( GAITester ); + +static void _GAITesterRun( void *inContext ); +static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap ); +static void _GAITesterTimeout( void *inContext ); +static void _GAITesterAdvanceCurrentItem( GAITesterRef inTester ); +static void _GAITesterAdvanceCurrentSet( GAITesterRef inTester ); +static void _GAITesterInitializeCurrentTest( GAITesterRef inTester ); +static void DNSSD_API + _GAITesterGetAddrInfoCallback( + DNSServiceRef inSDRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inError, + const char * inHostname, + const struct sockaddr * inSockAddr, + uint32_t inTTL, + void * inContext ); +static void _GAITesterCompleteCurrentTest( GAITesterRef inTester, Boolean inTimedOut ); + +#define ForgetPacketCapture( X ) ForgetCustom( X, pcap_close ) + +static OSStatus + GAITestItemCreate( + const char * inName, + unsigned int inAddressCount, + GAITestAddrType inHasAddrs, + GAITestAddrType inWantAddrs, + GAITestItem ** outItem ); +static OSStatus GAITestItemDuplicate( const GAITestItem *inItem, GAITestItem **outItem ); +static void GAITestItemFree( GAITestItem *inItem ); + +static OSStatus + GAITesterCreate( + dispatch_queue_t inQueue, + int inCallDelayMs, + int inServerDelayMs, + int inServerDefaultTTL, + GAITesterRef * outTester ) +{ + OSStatus err; + GAITesterRef obj = NULL; + + CF_OBJECT_CREATE( GAITester, obj, err, exit ); + + ReplaceDispatchQueue( &obj->queue, inQueue ); + obj->callDelayMs = inCallDelayMs; + obj->serverPID = -1; + obj->serverDelayMs = inServerDelayMs; + obj->serverDefaultTTL = inServerDefaultTTL; + + *outTester = obj; + obj = NULL; + err = kNoErr; + +exit: + CFReleaseNullSafe( obj ); + return( err ); +} + +//=========================================================================================================================== +// _GAITesterFinalize +//=========================================================================================================================== + +static void _GAITesterFinalize( CFTypeRef inObj ) +{ + GAITesterRef const me = (GAITesterRef) inObj; + GAITestCase * testCase; + + check( !me->opRef ); + check( !me->mainRef ); + check( !me->caseTimer ); + dispatch_forget( &me->queue ); + while( ( testCase = me->caseList ) != NULL ) + { + me->caseList = testCase->next; + GAITestCaseFree( testCase ); + } +} + +//=========================================================================================================================== +// GAITesterStart +//=========================================================================================================================== + +static void _GAITesterStart( void *inContext ); +static void _GAITesterStop( GAITesterRef me ); + +static void GAITesterStart( GAITesterRef me ) +{ + CFRetain( me ); + dispatch_async_f( me->queue, me, _GAITesterStart ); +} + +extern char ** environ; + +static void _GAITesterStart( void *inContext ) +{ + OSStatus err; + GAITesterRef const me = (GAITesterRef) inContext; + char * argv[ 4 ]; + char * ptr; + char * end; + char command[ 128 ]; + + ptr = &command[ 0 ]; + end = &command[ countof( command ) ]; + SNPrintF_Add( &ptr, end, "dnssdutil server --loopback --followPID %lld", (int64_t) getpid() ); + if( me->serverDefaultTTL >= 0 ) SNPrintF_Add( &ptr, end, " --defaultTTL %d", me->serverDefaultTTL ); + if( me->serverDelayMs >= 0 ) SNPrintF_Add( &ptr, end, " --responseDelay %d", me->serverDelayMs ); + + argv[ 0 ] = "/bin/sh"; + argv[ 1 ] = "-c"; + argv[ 2 ] = command; + argv[ 3 ] = NULL; + err = posix_spawn( &me->serverPID, argv[ 0 ], NULL, NULL, argv, environ ); + require_noerr( err, exit ); + + me->currentCase = me->caseList; + me->currentItem = me->currentCase ? me->currentCase->itemList : NULL; + _GAITesterInitializeCurrentTest( me ); + + // Hack: The first tester run is delayed for three seconds to allow the test DNS server to start up. + // A better way to handle this is to issue an asynchronous query for something in the d.test. domain. As soon as an + // expected response is received, the server can be considered to be up and running. + + CFRetain( me ); + dispatch_after_f( dispatch_time_seconds( 3 ), me->queue, me, _GAITesterRun ); + + CFRetain( me ); + me->started = true; + if( me->eventHandler ) me->eventHandler( kGAITesterEvent_Started, me->eventContext ); + +exit: + if( err ) _GAITesterStop( me ); + CFRelease( me ); +} + +//=========================================================================================================================== +// GAITesterStop +//=========================================================================================================================== + +static void _GAITesterUserStop( void *inContext ); + +static void GAITesterStop( GAITesterRef me ) +{ + CFRetain( me ); + dispatch_async_f( me->queue, me, _GAITesterUserStop ); +} + +static void _GAITesterUserStop( void *inContext ) +{ + GAITesterRef const me = (GAITesterRef) inContext; + + _GAITesterStop( me ); + CFRelease( me ); +} + +static void _GAITesterStop( GAITesterRef me ) +{ + OSStatus err; + + DNSServiceForget( &me->opRef ); + DNSServiceForget( &me->mainRef ); + ForgetPacketCapture( &me->pcap ); + dispatch_source_forget( &me->caseTimer ); + if( me->serverPID != -1 ) + { + err = kill( me->serverPID, SIGTERM ); + err = map_global_noerr_errno( err ); + check_noerr( err ); + } + + if( !me->stopped ) + { + me->stopped = true; + if( me->eventHandler ) me->eventHandler( kGAITesterEvent_Stopped, me->eventContext ); + if( me->started ) CFRelease( me ); + } +} + +//=========================================================================================================================== +// GAITesterAddCase +//=========================================================================================================================== + +static void GAITesterAddCase( GAITesterRef me, GAITestCase *inCase ) +{ + GAITestCase ** ptr; + + for( ptr = &me->caseList; *ptr != NULL; ptr = &( *ptr )->next ) {} + *ptr = inCase; +} + +//=========================================================================================================================== +// GAITesterSetEventHandler +//=========================================================================================================================== + +static void GAITesterSetEventHandler( GAITesterRef me, GAITesterEventHandler_f inEventHandler, void *inEventContext ) +{ + me->eventHandler = inEventHandler; + me->eventContext = inEventContext; +} + +//=========================================================================================================================== +// GAITesterSetResultsHandler +//=========================================================================================================================== + +static void GAITesterSetResultsHandler( GAITesterRef me, GAITesterResultsHandler_f inResultsHandler, void *inResultsContext ) +{ + me->resultsHandler = inResultsHandler; + me->resultsContext = inResultsContext; +} + +//=========================================================================================================================== +// _GAITesterRun +//=========================================================================================================================== + +static void _GAITesterRun( void *inContext ) +{ + OSStatus err; + GAITesterRef const me = (GAITesterRef) inContext; + GAITestItem * item; + GAITestItemResult * results = NULL; + + require_action_quiet( !me->stopped, exit, err = kNoErr ); + + for( ;; ) + { + item = me->currentItem; + if( item ) + { + DNSServiceProtocol protocols; + + check( !me->opRef ); + check( ( me->bitmapV4 != 0 ) || ( me->bitmapV6 != 0 ) ); + + // Perform preliminary tasks if this is the start of a new test case. + + if( item == me->currentCase->itemList ) + { + // Flush mDNSResponder's cache. + + err = systemf( NULL, "killall -HUP mDNSResponder" ); + require_noerr( err, exit ); + usleep( kMicrosecondsPerSecond ); + + // Start a packet capture. + + check( !me->pcap ); + err = _GAITesterCreatePacketCapture( &me->pcap ); + require_noerr( err, exit ); + + // Start the test case time limit timer. + + check( !me->caseTimer ); + if( me->currentCase->timeLimitMs > 0 ) + { + const int64_t timeLimitSecs = ( me->currentCase->timeLimitMs + 999 ) / 1000; + + err = DispatchTimerCreate( dispatch_time_seconds( timeLimitSecs ), DISPATCH_TIME_FOREVER, + ( (uint64_t) timeLimitSecs ) * kNanosecondsPerSecond / 10, + me->queue, _GAITesterTimeout, NULL, me, &me->caseTimer ); + require_noerr( err, exit ); + dispatch_resume( me->caseTimer ); + } + + me->caseStartTime = GetCurrentMicroTime(); + } + + // Call DNSServiceGetAddrInfo(). + + if( me->callDelayMs > 0 ) usleep( ( (useconds_t) me->callDelayMs ) * kMicrosecondsPerMillisecond ); + + protocols = 0; + if( item->wantV4 ) protocols |= kDNSServiceProtocol_IPv4; + if( item->wantV6 ) protocols |= kDNSServiceProtocol_IPv6; + + check( !me->mainRef ); + me->startTicks = UpTicks(); + + err = DNSServiceCreateConnection( &me->mainRef ); + require_noerr( err, exit ); + + err = DNSServiceSetDispatchQueue( me->mainRef, me->queue ); + require_noerr( err, exit ); + + me->connTicks = UpTicks(); + + me->opRef = me->mainRef; + err = DNSServiceGetAddrInfo( &me->opRef, kDNSServiceFlagsShareConnection | kDNSServiceFlagsReturnIntermediates, + kDNSServiceInterfaceIndexAny, protocols, item->name, _GAITesterGetAddrInfoCallback, me ); + require_noerr( err, exit ); + break; + } + else + { + // No more test items means that this test case has completed (or timed out). + + me->caseEndTime = GetCurrentMicroTime(); + dispatch_source_forget( &me->caseTimer ); + ForgetPacketCapture( &me->pcap ); + + if( me->resultsHandler ) + { + size_t resultCount, itemCount, i; + int timedOut; + + itemCount = 0; + resultCount = 0; + timedOut = false; + for( item = me->currentCase->itemList; item; item = item->next ) + { + if( !timedOut ) + { + if( item->timeUs < 0 ) + { + timedOut = true; + } + else + { + ++resultCount; + } + } + ++itemCount; + } + if( resultCount > 0 ) + { + results = (GAITestItemResult *) calloc( resultCount, sizeof( *results ) ); + require_action( results, exit, err = kNoMemoryErr ); + + item = me->currentCase->itemList; + for( i = 0; i < resultCount; ++i ) + { + results[ i ].name = item->name; + results[ i ].connectionTimeUs = item->connectionTimeUs; + results[ i ].firstTimeUs = item->firstTimeUs; + results[ i ].timeUs = item->timeUs; + item = item->next; + } + } + me->resultsHandler( me->currentCase->title, me->caseStartTime, me->caseEndTime, results, resultCount, + itemCount, me->resultsContext ); + ForgetMem( &results ); + } + + _GAITesterAdvanceCurrentSet( me ); + require_action_quiet( me->currentCase, exit, err = kEndingErr ); + } + } + +exit: + FreeNullSafe( results ); + if( err ) _GAITesterStop( me ); + CFRelease( me ); +} + +//=========================================================================================================================== +// _GAITesterCreatePacketCapture +//=========================================================================================================================== + +static OSStatus _GAITesterCreatePacketCapture( pcap_t **outPCap ) +{ + OSStatus err; + pcap_t * pcap; + struct bpf_program program; + char errBuf[ PCAP_ERRBUF_SIZE ]; + + pcap = pcap_create( "lo0", errBuf ); + require_action_string( pcap, exit, err = kUnknownErr, errBuf ); + + err = pcap_set_buffer_size( pcap, 512 * kBytesPerKiloByte ); + require_noerr_action( err, exit, err = kUnknownErr ); + + err = pcap_set_snaplen( pcap, 512 ); + require_noerr_action( err, exit, err = kUnknownErr ); + + err = pcap_set_immediate_mode( pcap, 0 ); + require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) ); + + err = pcap_activate( pcap ); + require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) ); + + err = pcap_setdirection( pcap, PCAP_D_INOUT ); + require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) ); + + err = pcap_setnonblock( pcap, 1, errBuf ); + require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) ); + + err = pcap_compile( pcap, &program, "udp port 53", 1, PCAP_NETMASK_UNKNOWN ); + require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) ); + + err = pcap_setfilter( pcap, &program ); + pcap_freecode( &program ); + require_noerr_action_string( err, exit, err = kUnknownErr, pcap_geterr( pcap ) ); + + *outPCap = pcap; + pcap = NULL; + +exit: + if( pcap ) pcap_close( pcap ); + return( err ); +} + +//=========================================================================================================================== +// _GAITesterTimeout +//=========================================================================================================================== + +static void _GAITesterTimeout( void *inContext ) +{ + GAITesterRef const me = (GAITesterRef) inContext; + + dispatch_source_forget( &me->caseTimer ); + + _GAITesterCompleteCurrentTest( me, true ); +} + +//=========================================================================================================================== +// _GAITesterAdvanceCurrentItem +//=========================================================================================================================== + +static void _GAITesterAdvanceCurrentItem( GAITesterRef me ) +{ + if( me->currentItem ) + { + me->currentItem = me->currentItem->next; + _GAITesterInitializeCurrentTest( me ); + } +} + +//=========================================================================================================================== +// _GAITesterAdvanceCurrentSet +//=========================================================================================================================== + +static void _GAITesterAdvanceCurrentSet( GAITesterRef me ) +{ + if( me->currentCase ) + { + me->caseStartTime = 0; + me->caseEndTime = 0; + me->currentCase = me->currentCase->next; + if( me->currentCase ) + { + me->currentItem = me->currentCase->itemList; + _GAITesterInitializeCurrentTest( me ); + } + } +} + +//=========================================================================================================================== +// _GAITesterInitializeCurrentTest +//=========================================================================================================================== + +static void _GAITesterInitializeCurrentTest( GAITesterRef me ) +{ + GAITestItem * const item = me->currentItem; + + if( item ) + { + check( item->addressCount > 0 ); + if( item->wantV4 ) + { + me->bitmapV4 = item->hasV4 ? ( ( UINT64_C( 1 ) << item->addressCount ) - 1 ) : 1; + } + else + { + me->bitmapV4 = 0; + } + + if( item->wantV6 ) + { + me->bitmapV6 = item->hasV6 ? ( ( UINT64_C( 1 ) << item->addressCount ) - 1 ) : 1; + } + else + { + me->bitmapV6 = 0; + } + me->gotFirstResult = false; + } +} + +//=========================================================================================================================== +// _GAITesterGetAddrInfoCallback +//=========================================================================================================================== + +static void DNSSD_API + _GAITesterGetAddrInfoCallback( + DNSServiceRef inSDRef, + DNSServiceFlags inFlags, + uint32_t inInterfaceIndex, + DNSServiceErrorType inError, + const char * inHostname, + const struct sockaddr * inSockAddr, + uint32_t inTTL, + void * inContext ) +{ + GAITesterRef const me = (GAITesterRef) inContext; + GAITestItem * const item = me->currentItem; + const sockaddr_ip * const sip = (const sockaddr_ip *) inSockAddr; + uint64_t nowTicks; + uint64_t * bitmapPtr; + uint64_t bitmask; + unsigned int addrOffset; + + Unused( inSDRef ); + Unused( inInterfaceIndex ); + Unused( inHostname ); + Unused( inTTL ); + + nowTicks = UpTicks(); + + require_quiet( inFlags & kDNSServiceFlagsAdd, exit ); + require_quiet( !inError || ( inError == kDNSServiceErr_NoSuchRecord ), exit ); + + bitmapPtr = NULL; + bitmask = 0; + if( ( sip->sa.sa_family == AF_INET ) && item->wantV4 ) + { + if( item->hasV4 ) + { + if( !inError ) + { + const uint32_t addrV4 = ntohl( sip->v4.sin_addr.s_addr ); + + if( strcasecmp( item->name, "localhost." ) == 0 ) + { + if( addrV4 == INADDR_LOOPBACK ) + { + bitmask = 1; + bitmapPtr = &me->bitmapV4; + } + } + else + { + addrOffset = addrV4 - kTestDNSServerBaseAddrV4; + if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) ) + { + bitmask = UINT64_C( 1 ) << ( addrOffset - 1 ); + bitmapPtr = &me->bitmapV4; + } + } + } + } + else if( inError == kDNSServiceErr_NoSuchRecord ) + { + bitmask = 1; + bitmapPtr = &me->bitmapV4; + } + } + else if( ( sip->sa.sa_family == AF_INET6 ) && item->wantV6 ) + { + if( item->hasV6 ) + { + if( !inError ) + { + const uint8_t * const addrV6 = sip->v6.sin6_addr.s6_addr; + + if( strcasecmp( item->name, "localhost." ) == 0 ) + { + if( memcmp( addrV6, in6addr_loopback.s6_addr, 16 ) == 0 ) + { + bitmask = 1; + bitmapPtr = &me->bitmapV6; + } + } + else if( memcmp( addrV6, kTestDNSServerBaseAddrV6, 15 ) == 0 ) + { + addrOffset = addrV6[ 15 ]; + if( ( addrOffset >= 1 ) && ( addrOffset <= item->addressCount ) ) + { + bitmask = UINT64_C( 1 ) << ( addrOffset - 1 ); + bitmapPtr = &me->bitmapV6; + } + } + } + } + else if( inError == kDNSServiceErr_NoSuchRecord ) + { + bitmask = 1; + bitmapPtr = &me->bitmapV6; + } + } + + if( bitmapPtr && ( *bitmapPtr & bitmask ) ) + { + *bitmapPtr &= ~bitmask; + if( !me->gotFirstResult ) + { + me->firstTicks = nowTicks; + me->gotFirstResult = true; + } + + if( ( me->bitmapV4 == 0 ) && ( me->bitmapV6 == 0 ) ) + { + me->endTicks = nowTicks; + _GAITesterCompleteCurrentTest( me, false ); + } + } + +exit: + return; +} + +//=========================================================================================================================== +// _GAITesterCompleteCurrentTest +//=========================================================================================================================== + +static OSStatus + _GAITesterGetDNSMessageFromPacket( + const uint8_t * inPacketPtr, + size_t inPacketLen, + const uint8_t ** outMsgPtr, + size_t * outMsgLen ); + +static void _GAITesterCompleteCurrentTest( GAITesterRef me, Boolean inTimedOut ) +{ + OSStatus err; + GAITestItem * item; + struct timeval * tsQA = NULL; + struct timeval * tsQAAAA = NULL; + struct timeval * tsRA = NULL; + struct timeval * tsRAAAA = NULL; + struct timeval timeStamps[ 4 ]; + struct timeval * tsPtr = &timeStamps[ 0 ]; + struct timeval * tsQ; + struct timeval * tsR; + int64_t idleTimeUs; + uint8_t name[ kDomainNameLengthMax ]; + + DNSServiceForget( &me->opRef ); + DNSServiceForget( &me->mainRef ); + + if( inTimedOut ) + { + for( item = me->currentItem; item; item = item->next ) + { + item->firstTimeUs = -1; + item->timeUs = -1; + } + me->currentItem = NULL; + + CFRetain( me ); + dispatch_async_f( me->queue, me, _GAITesterRun ); + return; + } + + item = me->currentItem; + err = DomainNameFromString( name, item->name, NULL ); + require_noerr( err, exit ); + + for( ;; ) + { + int status; + struct pcap_pkthdr * pktHdr; + const uint8_t * packet; + const uint8_t * msgPtr; + size_t msgLen; + const DNSHeader * hdr; + unsigned int flags; + const uint8_t * ptr; + const DNSQuestionFixedFields * qfields; + unsigned int qtype; + uint8_t qname[ kDomainNameLengthMax ]; + + status = pcap_next_ex( me->pcap, &pktHdr, &packet ); + if( status != 1 ) break; + if( _GAITesterGetDNSMessageFromPacket( packet, pktHdr->caplen, &msgPtr, &msgLen ) != kNoErr ) continue; + if( msgLen < kDNSHeaderLength ) continue; + + hdr = (const DNSHeader *) msgPtr; + flags = DNSHeaderGetFlags( hdr ); + if( DNSFlagsGetOpCode( flags ) != kDNSOpCode_Query ) continue; + if( DNSHeaderGetQuestionCount( hdr ) < 1 ) continue; + + ptr = (const uint8_t *) &hdr[ 1 ]; + if( DNSMessageExtractDomainName( msgPtr, msgLen, ptr, qname, &ptr ) != kNoErr ) continue; + if( !DomainNameEqual( qname, name ) ) continue; + + qfields = (const DNSQuestionFixedFields *) ptr; + if( DNSQuestionFixedFieldsGetClass( qfields ) != kDNSServiceClass_IN ) continue; + + qtype = DNSQuestionFixedFieldsGetType( qfields ); + if( item->wantV4 && ( qtype == kDNSServiceType_A ) ) + { + if( flags & kDNSHeaderFlag_Response ) + { + if( tsQA && !tsRA ) + { + tsRA = tsPtr++; + *tsRA = pktHdr->ts; + } + } + else if( !tsQA ) + { + tsQA = tsPtr++; + *tsQA = pktHdr->ts; + } + } + else if( item->wantV6 && ( qtype == kDNSServiceType_AAAA ) ) + { + if( flags & kDNSHeaderFlag_Response ) + { + if( tsQAAAA && !tsRAAAA ) + { + tsRAAAA = tsPtr++; + *tsRAAAA = pktHdr->ts; + } + } + else if( !tsQAAAA ) + { + tsQAAAA = tsPtr++; + *tsQAAAA = pktHdr->ts; + } + } + } + + if( tsQA && tsQAAAA ) tsQ = TIMEVAL_GT( *tsQA, *tsQAAAA ) ? tsQA : tsQAAAA; + else tsQ = tsQA ? tsQA : tsQAAAA; + + if( tsRA && tsRAAAA ) tsR = TIMEVAL_LT( *tsRA, *tsRAAAA ) ? tsRA : tsRAAAA; + else tsR = tsQA ? tsQA : tsQAAAA; + + if( tsQ && tsR ) + { + idleTimeUs = TIMEVAL_USEC64_DIFF( *tsR, *tsQ ); + if( idleTimeUs < 0 ) idleTimeUs = 0; + } + else + { + idleTimeUs = 0; + } + + item->connectionTimeUs = (int64_t) UpTicksToMicroseconds( me->connTicks - me->startTicks ); + item->firstTimeUs = (int64_t)( UpTicksToMicroseconds( me->firstTicks - me->connTicks ) - (uint64_t) idleTimeUs ); + item->timeUs = (int64_t)( UpTicksToMicroseconds( me->endTicks - me->connTicks ) - (uint64_t) idleTimeUs ); + + _GAITesterAdvanceCurrentItem( me ); + CFRetain( me ); + dispatch_async_f( me->queue, me, _GAITesterRun ); + +exit: + if( err ) _GAITesterStop( me ); +} + +//=========================================================================================================================== +// _GAITesterGetDNSMessageFromPacket +//=========================================================================================================================== + +#define kHeaderSizeNullLink 4 +#define kHeaderSizeIPv4Min 20 +#define kHeaderSizeIPv6 40 +#define kHeaderSizeUDP 8 + +#define kIPProtocolUDP 0x11 + +static OSStatus + _GAITesterGetDNSMessageFromPacket( + const uint8_t * inPacketPtr, + size_t inPacketLen, + const uint8_t ** outMsgPtr, + size_t * outMsgLen ) +{ + OSStatus err; + const uint8_t * nullLink; + uint32_t addressFamily; + const uint8_t * ip; + int ipHeaderLen; + int protocol; + const uint8_t * msg; + const uint8_t * const end = &inPacketPtr[ inPacketLen ]; + + nullLink = &inPacketPtr[ 0 ]; + require_action_quiet( ( end - nullLink ) >= kHeaderSizeNullLink, exit, err = kUnderrunErr ); + addressFamily = ReadHost32( &nullLink[ 0 ] ); + + ip = &nullLink[ kHeaderSizeNullLink ]; + if( addressFamily == AF_INET ) + { + require_action_quiet( ( end - ip ) >= kHeaderSizeIPv4Min, exit, err = kUnderrunErr ); + ipHeaderLen = ( ip[ 0 ] & 0x0F ) * 4; + protocol = ip[ 9 ]; + } + else if( addressFamily == AF_INET6 ) + { + require_action_quiet( ( end - ip ) >= kHeaderSizeIPv6, exit, err = kUnderrunErr ); + ipHeaderLen = kHeaderSizeIPv6; + protocol = ip[ 6 ]; + } + else + { + err = kTypeErr; + goto exit; + } + require_action_quiet( protocol == kIPProtocolUDP, exit, err = kTypeErr ); + require_action_quiet( ( end - ip ) >= ( ipHeaderLen + kHeaderSizeUDP ), exit, err = kUnderrunErr ); + + msg = &ip[ ipHeaderLen + kHeaderSizeUDP ]; + + *outMsgPtr = msg; + *outMsgLen = (size_t)( end - msg ); + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// GAITestCaseCreate +//=========================================================================================================================== + +static OSStatus GAITestCaseCreate( const char *inTitle, unsigned int inTimeLimitMs, GAITestCase **outSet ) +{ + OSStatus err; + GAITestCase * obj; + + obj = (GAITestCase *) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->title = strdup( inTitle ); + require_action( obj->title, exit, err = kNoMemoryErr ); + + obj->timeLimitMs = inTimeLimitMs; + + *outSet = obj; + obj = NULL; + err = kNoErr; + +exit: + if( obj ) GAITestCaseFree( obj ); + return( err ); +} + +//=========================================================================================================================== +// GAITestCaseFree +//=========================================================================================================================== + +static void GAITestCaseFree( GAITestCase *inCase ) +{ + GAITestItem * item; + + while( ( item = inCase->itemList ) != NULL ) + { + inCase->itemList = item->next; + GAITestItemFree( item ); + } + ForgetMem( &inCase->title ); + free( inCase ); +} + +//=========================================================================================================================== +// GAITestCaseAddItem +//=========================================================================================================================== + +// A character set of lower-case alphabet characters and digits and a string length of six allows for 36^6 = 2,176,782,336 +// possible strings to use in the Tag label. + +#define kUniqueStringCharSet "abcdefghijklmnopqrstuvwxyz0123456789" +#define kUniqueStringCharSetLen sizeof_string( kUniqueStringCharSet ) +#define kUniqueStringLen 6 + +static OSStatus + GAITestCaseAddItem( + GAITestCase * inCase, + unsigned int inAliasCount, + unsigned int inAddressCount, + int inTTL, + GAITestAddrType inHasAddrs, + GAITestAddrType inWantAddrs, + unsigned int inItemCount ) +{ + OSStatus err; + GAITestItem * item; + GAITestItem * item2; + GAITestItem * newItemList = NULL; + GAITestItem ** itemPtr; + char * ptr; + char * end; + unsigned int i; + char name[ 64 ]; + char uniqueStr[ kUniqueStringLen + 1 ]; + + require_action_quiet( inItemCount > 0, exit, err = kNoErr ); + + // Limit address count to 64 because we use 64-bit bitmaps for keeping track of addresses. + + require_action_quiet( ( inAddressCount >= 1 ) && ( inAddressCount <= 64 ), exit, err = kCountErr ); + require_action_quiet( ( inAliasCount >= 0 ) && ( inAliasCount <= INT32_MAX ), exit, err = kCountErr ); + require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr ); + + ptr = &name[ 0 ]; + end = &name[ countof( name ) ]; + + // Add Alias label. + + if( inAliasCount == 1 ) SNPrintF_Add( &ptr, end, "alias." ); + else if( inAliasCount >= 2 ) SNPrintF_Add( &ptr, end, "alias-%u.", inAliasCount ); + + // Add Count label. + + SNPrintF_Add( &ptr, end, "count-%u.", inAddressCount ); + + // Add TTL label. + + if( inTTL >= 0 ) SNPrintF_Add( &ptr, end, "ttl-%d.", inTTL ); + + // Add Tag label. + + RandomString( kUniqueStringCharSet, kUniqueStringCharSetLen, kUniqueStringLen, kUniqueStringLen, uniqueStr ); + SNPrintF_Add( &ptr, end, "tag-%s.", uniqueStr ); + + // Add IPv4 or IPv6 label if necessary. + + switch( inHasAddrs ) + { + case kGAITestAddrType_IPv4: + SNPrintF_Add( &ptr, end, "ipv4." ); + break; + + case kGAITestAddrType_IPv6: + SNPrintF_Add( &ptr, end, "ipv6." ); + break; + } + + // Add d.test. labels. + + SNPrintF_Add( &ptr, end, "d.test." ); + + // Create item. + + err = GAITestItemCreate( name, inAddressCount, inHasAddrs, inWantAddrs, &item ); + require_noerr( err, exit ); + + newItemList = item; + itemPtr = &item->next; + + // Create repeat items. + + for( i = 1; i < inItemCount; ++i ) + { + err = GAITestItemDuplicate( item, &item2 ); + require_noerr( err, exit ); + + *itemPtr = item2; + itemPtr = &item2->next; + } + + // Append to test case's item list. + + for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {} + *itemPtr = newItemList; + newItemList = NULL; + +exit: + while( ( item = newItemList ) != NULL ) + { + newItemList = item->next; + GAITestItemFree( item ); + } + return( err ); +} + +//=========================================================================================================================== +// GAITestCaseAddLocalHostItem +//=========================================================================================================================== + +static OSStatus GAITestCaseAddLocalHostItem( GAITestCase *inCase, GAITestAddrType inWantAddrs, unsigned int inItemCount ) +{ + OSStatus err; + GAITestItem * item; + GAITestItem * item2; + GAITestItem * newItemList = NULL; + GAITestItem ** itemPtr; + unsigned int i; + + require_action_quiet( inItemCount > 1, exit, err = kNoErr ); + + err = GAITestItemCreate( "localhost.", 1, kGAITestAddrType_Both, inWantAddrs, &item ); + require_noerr( err, exit ); + + newItemList = item; + itemPtr = &item->next; + + // Create repeat items. + + for( i = 1; i < inItemCount; ++i ) + { + err = GAITestItemDuplicate( item, &item2 ); + require_noerr( err, exit ); + + *itemPtr = item2; + itemPtr = &item2->next; + } + + for( itemPtr = &inCase->itemList; *itemPtr; itemPtr = &( *itemPtr )->next ) {} + *itemPtr = newItemList; + newItemList = NULL; + +exit: + while( ( item = newItemList ) != NULL ) + { + newItemList = item->next; + GAITestItemFree( item ); + } + return( err ); +} + +//=========================================================================================================================== +// GAITestItemCreate +//=========================================================================================================================== + +static OSStatus + GAITestItemCreate( + const char * inName, + unsigned int inAddressCount, + GAITestAddrType inHasAddrs, + GAITestAddrType inWantAddrs, + GAITestItem ** outItem ) +{ + OSStatus err; + GAITestItem * obj = NULL; + + require_action_quiet( inAddressCount >= 1, exit, err = kCountErr ); + require_action_quiet( GAITestAddrTypeIsValid( inHasAddrs ), exit, err = kValueErr ); + require_action_quiet( GAITestAddrTypeIsValid( inWantAddrs ), exit, err = kValueErr ); + + obj = (GAITestItem *) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + obj->name = strdup( inName ); + require_action( obj->name, exit, err = kNoMemoryErr ); + + obj->addressCount = inAddressCount; + obj->hasV4 = ( inHasAddrs & kGAITestAddrType_IPv4 ) ? true : false; + obj->hasV6 = ( inHasAddrs & kGAITestAddrType_IPv6 ) ? true : false; + obj->wantV4 = ( inWantAddrs & kGAITestAddrType_IPv4 ) ? true : false; + obj->wantV6 = ( inWantAddrs & kGAITestAddrType_IPv6 ) ? true : false; + + *outItem = obj; + obj = NULL; + err = kNoErr; + +exit: + if( obj ) GAITestItemFree( obj ); + return( err ); +} + +//=========================================================================================================================== +// GAITestItemDuplicate +//=========================================================================================================================== + +static OSStatus GAITestItemDuplicate( const GAITestItem *inItem, GAITestItem **outItem ) +{ + OSStatus err; + GAITestItem * obj; + + obj = (GAITestItem *) calloc( 1, sizeof( *obj ) ); + require_action( obj, exit, err = kNoMemoryErr ); + + *obj = *inItem; + obj->next = NULL; + if( inItem->name ) + { + obj->name = strdup( inItem->name ); + require_action( obj->name, exit, err = kNoMemoryErr ); + } + + *outItem = obj; + obj = NULL; + err = kNoErr; + +exit: + if( obj ) GAITestItemFree( obj ); + return( err ); +} + +//=========================================================================================================================== +// GAITestItemFree +//=========================================================================================================================== + +static void GAITestItemFree( GAITestItem *inItem ) +{ + ForgetMem( &inItem->name ); + free( inItem ); +} + +//=========================================================================================================================== +// SSDPDiscoverCmd +//=========================================================================================================================== + +#define kSSDPPort 1900 + +typedef struct +{ + HTTPHeader header; // HTTP header object for sending and receiving. + dispatch_source_t readSourceV4; // Read dispatch source for IPv4 socket. + dispatch_source_t readSourceV6; // Read dispatch source for IPv6 socket. + int receiveSecs; // After send, the amount of time to spend receiving. + uint32_t ifindex; // Index of the interface over which to send the query. + Boolean useIPv4; // True if the query should be sent via IPv4 multicast. + Boolean useIPv6; // True if the query should be sent via IPv6 multicast. + +} SSDPDiscoverContext; + +static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext ); +static void SSDPDiscoverReadHandler( void *inContext ); +static int SocketToPortNumber( SocketRef inSock ); +static OSStatus WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST ); + +static void SSDPDiscoverCmd( void ) +{ + OSStatus err; + struct timeval now; + SSDPDiscoverContext * context; + dispatch_source_t signalSource = NULL; + SocketRef sockV4 = kInvalidSocketRef; + SocketRef sockV6 = kInvalidSocketRef; + ssize_t n; + int sendCount; + + // Set up SIGINT handler. + + signal( SIGINT, SIG_IGN ); + err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource ); + require_noerr( err, exit ); + dispatch_resume( signalSource ); + + // Check command parameters. + + if( gSSDPDiscover_ReceiveSecs < -1 ) + { + FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs ); + err = kParamErr; + goto exit; + } + + // Create context. + + context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) ); + require_action( context, exit, err = kNoMemoryErr ); + + context->receiveSecs = gSSDPDiscover_ReceiveSecs; + context->useIPv4 = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false; + context->useIPv6 = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false; + + err = InterfaceIndexFromArgString( gInterface, &context->ifindex ); + require_noerr_quiet( err, exit ); + + // Set up IPv4 socket. + + if( context->useIPv4 ) + { + int port; + err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 ); + require_noerr( err, exit ); + + err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex ); + require_noerr( err, exit ); + + err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) ); + err = map_socket_noerr_errno( sockV4, err ); + require_noerr( err, exit ); + } + + // Set up IPv6 socket. + + if( context->useIPv6 ) + { + err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 ); + require_noerr( err, exit ); + + err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex ); + require_noerr( err, exit ); + + err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) ); + err = map_socket_noerr_errno( sockV6, err ); + require_noerr( err, exit ); + } + + // Print prologue. + + SSDPDiscoverPrintPrologue( context ); + + // Send mDNS query message. + + sendCount = 0; + if( IsValidSocket( sockV4 ) ) + { + struct sockaddr_in mcastAddr4; + + memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) ); + SIN_LEN_SET( &mcastAddr4 ); + mcastAddr4.sin_family = AF_INET; + mcastAddr4.sin_port = htons( kSSDPPort ); + mcastAddr4.sin_addr.s_addr = htonl( 0xEFFFFFFA ); // 239.255.255.250 + + err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST ); + require_noerr( err, exit ); + + n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4, + (socklen_t) sizeof( mcastAddr4 ) ); + err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n ); + if( err ) + { + FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err ); + ForgetSocket( &sockV4 ); + } + else + { + if( gSSDPDiscover_Verbose ) + { + gettimeofday( &now, NULL ); + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "Send time: %{du:time}\n", &now ); + FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV4 ) ); + FPrintF( stdout, "Destination: %##a\n", &mcastAddr4 ); + FPrintF( stdout, "Message size: %zu\n", context->header.len ); + FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len ); + } + ++sendCount; + } + } + + if( IsValidSocket( sockV6 ) ) + { + struct sockaddr_in6 mcastAddr6; + + memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) ); + SIN6_LEN_SET( &mcastAddr6 ); + mcastAddr6.sin6_family = AF_INET6; + mcastAddr6.sin6_port = htons( kSSDPPort ); + mcastAddr6.sin6_addr.s6_addr[ 0 ] = 0xFF; // SSDP IPv6 link-local multicast address FF02::C + mcastAddr6.sin6_addr.s6_addr[ 1 ] = 0x02; + mcastAddr6.sin6_addr.s6_addr[ 15 ] = 0x0C; + + err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST ); + require_noerr( err, exit ); + + n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6, + (socklen_t) sizeof( mcastAddr6 ) ); + err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n ); + if( err ) + { + FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err ); + ForgetSocket( &sockV6 ); + } + else + { + if( gSSDPDiscover_Verbose ) + { + gettimeofday( &now, NULL ); + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "Send time: %{du:time}\n", &now ); + FPrintF( stdout, "Source Port: %d\n", SocketToPortNumber( sockV6 ) ); + FPrintF( stdout, "Destination: %##a\n", &mcastAddr6 ); + FPrintF( stdout, "Message size: %zu\n", context->header.len ); + FPrintF( stdout, "HTTP header:\n%1{text}", context->header.buf, context->header.len ); + } + ++sendCount; + } + } + require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr ); + + // If there's no wait period after the send, then exit. + if( context->receiveSecs == 0 ) goto exit; // Create dispatch read sources for socket(s). if( IsValidSocket( sockV4 ) ) { - SocketContext * sockContext; + SocketContext * sockCtx; - sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); - require_action( sockContext, exit, err = kNoMemoryErr ); + err = SocketContextCreate( sockV4, context, &sockCtx ); + require_noerr( err, exit ); + sockV4 = kInvalidSocketRef; - err = DispatchReadSourceCreate( sockV4, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockContext, + err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx, &context->readSourceV4 ); - if( err ) ForgetMem( &sockContext ); + if( err ) ForgetSocketContext( &sockCtx ); require_noerr( err, exit ); - sockContext->context = context; - sockContext->sock = sockV4; - sockV4 = kInvalidSocketRef; dispatch_resume( context->readSourceV4 ); } if( IsValidSocket( sockV6 ) ) { - SocketContext * sockContext; + SocketContext * sockCtx; - sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) ); - require_action( sockContext, exit, err = kNoMemoryErr ); + err = SocketContextCreate( sockV6, context, &sockCtx ); + require_noerr( err, exit ); + sockV6 = kInvalidSocketRef; - err = DispatchReadSourceCreate( sockV6, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockContext, + err = DispatchReadSourceCreate( sockCtx->sock, NULL, SSDPDiscoverReadHandler, SocketContextCancelHandler, sockCtx, &context->readSourceV6 ); - if( err ) ForgetMem( &sockContext ); + if( err ) ForgetSocketContext( &sockCtx ); require_noerr( err, exit ); - sockContext->context = context; - sockContext->sock = sockV6; - sockV6 = kInvalidSocketRef; dispatch_resume( context->readSourceV6 ); } @@ -6475,7 +10524,6 @@ static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext ) const char * ifName; char ifNameBuf[ IF_NAMESIZE + 1 ]; NetTransportType ifType; - char time[ kTimestampBufLen ]; ifName = if_indextoname( inContext->ifindex, ifNameBuf ); @@ -6489,7 +10537,7 @@ static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext ) FPrintF( stdout, "Receive duration: " ); if( receiveSecs >= 0 ) FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' ); else FPrintF( stdout, "∞\n" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); } //=========================================================================================================================== @@ -6499,23 +10547,23 @@ static void SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext ) static void SSDPDiscoverReadHandler( void *inContext ) { OSStatus err; - SocketContext * const sockContext = (SocketContext *) inContext; - SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockContext->context; - HTTPHeader * const header = &context->header; + struct timeval now; + SocketContext * const sockCtx = (SocketContext *) inContext; + SSDPDiscoverContext * const context = (SSDPDiscoverContext *) sockCtx->userContext; + HTTPHeader * const header = &context->header; sockaddr_ip fromAddr; size_t msgLen; - char time[ kTimestampBufLen ]; - GetTimestampStr( time ); + gettimeofday( &now, NULL ); - err = SocketRecvFrom( sockContext->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ), + err = SocketRecvFrom( sockCtx->sock, header->buf, sizeof( header->buf ), &msgLen, &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL ); require_noerr( err, exit ); FPrintF( stdout, "---\n" ); - FPrintF( stdout, "Receive time: %s\n", time ); - FPrintF( stdout, "Source: %##a\n", &fromAddr ); - FPrintF( stdout, "Message size: %zu\n", msgLen ); + FPrintF( stdout, "Receive time: %{du:time}\n", &now ); + FPrintF( stdout, "Source: %##a\n", &fromAddr ); + FPrintF( stdout, "Message size: %zu\n", msgLen ); header->len = msgLen; if( HTTPHeader_Validate( header ) ) { @@ -6618,7 +10666,6 @@ static void ResQueryCmd( void ) res_query_f res_query_ptr; int n; uint16_t type, class; - char time[ kTimestampBufLen ]; uint8_t answer[ 1024 ]; // Get pointer to one of the res_query() functions. @@ -6663,10 +10710,10 @@ static void ResQueryCmd( void ) // Print prologue. - FPrintF( stdout, "Name: %s\n", gResQuery_Name ); - FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type ); - FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Name: %s\n", gResQuery_Name ); + FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type ); + FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); // Call res_query(). @@ -6681,8 +10728,7 @@ static void ResQueryCmd( void ) // Print result. - FPrintF( stdout, "Message size: %d\n\n", n ); - PrintUDNSMessage( answer, (size_t) n, false ); + FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n ); exit: if( err ) exit( 1 ); @@ -6719,7 +10765,6 @@ static void ResolvDNSQueryCmd( void ) uint16_t type, class; sockaddr_ip from; uint32_t fromLen; - char time[ kTimestampBufLen ]; uint8_t answer[ 1024 ]; // Make sure that the required symbols are available. @@ -6774,11 +10819,11 @@ static void ResolvDNSQueryCmd( void ) // Print prologue. - FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name ); - FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type ); - FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class ); - FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "" ); - FPrintF( stdout, "Start time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "Name: %s\n", gResolvDNSQuery_Name ); + FPrintF( stdout, "Type: %s (%u)\n", RecordTypeToString( type ), type ); + FPrintF( stdout, "Class: %s (%u)\n", ( class == kDNSServiceClass_IN ) ? "IN" : "???", class ); + FPrintF( stdout, "Path: %s\n", gResolvDNSQuery_Path ? gResolvDNSQuery_Name : "" ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); FPrintF( stdout, "---\n" ); // Call dns_query(). @@ -6797,13 +10842,258 @@ static void ResolvDNSQueryCmd( void ) // Print result. FPrintF( stdout, "From: %##a\n", &from ); - FPrintF( stdout, "Message size: %d\n\n", n ); - PrintUDNSMessage( answer, (size_t) n, false ); + FPrintF( stdout, "Message size: %d\n\n%{du:dnsmsg}", n, answer, (size_t) n ); exit: if( dns ) soft_dns_free( dns ); if( err ) exit( 1 ); } + +//=========================================================================================================================== +// CFHostCmd +//=========================================================================================================================== + +static void + _CFHostResolveCallback( + CFHostRef inHost, + CFHostInfoType inInfoType, + const CFStreamError * inError, + void * inInfo ); + +static void CFHostCmd( void ) +{ + OSStatus err; + CFStringRef name; + Boolean success; + CFHostRef host = NULL; + CFHostClientContext context; + CFStreamError streamErr; + + name = CFStringCreateWithCString( kCFAllocatorDefault, gCFHost_Name, kCFStringEncodingUTF8 ); + require_action( name, exit, err = kUnknownErr ); + + host = CFHostCreateWithName( kCFAllocatorDefault, name ); + ForgetCF( &name ); + require_action( host, exit, err = kUnknownErr ); + + memset( &context, 0, sizeof( context ) ); + success = CFHostSetClient( host, _CFHostResolveCallback, &context ); + require_action( success, exit, err = kUnknownErr ); + + CFHostScheduleWithRunLoop( host, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + + // Print prologue. + + FPrintF( stdout, "Hostname: %s\n", gCFHost_Name ); + FPrintF( stdout, "Start time: %{du:time}\n", NULL ); + FPrintF( stdout, "---\n" ); + + success = CFHostStartInfoResolution( host, kCFHostAddresses, &streamErr ); + require_action( success, exit, err = kUnknownErr ); + err = kNoErr; + + CFRunLoopRun(); + +exit: + CFReleaseNullSafe( host ); + if( err ) exit( 1 ); +} + +static void _CFHostResolveCallback( CFHostRef inHost, CFHostInfoType inInfoType, const CFStreamError *inError, void *inInfo ) +{ + OSStatus err; + struct timeval now; + + gettimeofday( &now, NULL ); + + Unused( inInfoType ); + Unused( inInfo ); + + if( inError && ( inError->domain != 0 ) && ( inError->error ) ) + { + err = inError->error; + if( inError->domain == kCFStreamErrorDomainNetDB ) + { + FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) ); + } + else + { + FPrintF( stderr, "Error %#m\n", err ); + } + } + else + { + CFArrayRef addresses; + CFIndex count, i; + CFDataRef addrData; + const struct sockaddr * sockAddr; + Boolean wasResolved = false; + + addresses = CFHostGetAddressing( inHost, &wasResolved ); + check( wasResolved ); + + if( addresses ) + { + count = CFArrayGetCount( addresses ); + for( i = 0; i < count; ++i ) + { + addrData = CFArrayGetCFDataAtIndex( addresses, i, &err ); + require_noerr( err, exit ); + + sockAddr = (const struct sockaddr *) CFDataGetBytePtr( addrData ); + FPrintF( stdout, "%##a\n", sockAddr ); + } + } + err = kNoErr; + } + + FPrintF( stdout, "---\n" ); + FPrintF( stdout, "End time: %{du:time}\n", &now ); + + if( gCFHost_WaitSecs > 0 ) sleep( (unsigned int) gCFHost_WaitSecs ); + +exit: + exit( err ? 1 : 0 ); +} + +//=========================================================================================================================== +// DNSConfigAddCmd +// +// Note: Based on ajn's supplemental test tool. +//=========================================================================================================================== + +static void DNSConfigAddCmd( void ) +{ + OSStatus err; + CFMutableDictionaryRef dict = NULL; + CFMutableArrayRef array = NULL; + size_t i; + SCDynamicStoreRef store = NULL; + CFStringRef key = NULL; + Boolean success; + + if( geteuid() != 0 ) + { + FPrintF( stderr, "error: This command must to be run as root.\n" ); + err = kIDErr; + goto exit; + } + + // Create dictionary. + + dict = CFDictionaryCreateMutable( NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks ); + require_action( dict, exit, err = kNoMemoryErr ); + + // Add DNS server IP addresses. + + array = CFArrayCreateMutable( NULL, (CFIndex) gDNSConfigAdd_IPAddrCount, &kCFTypeArrayCallBacks ); + require_action( array, exit, err = kNoMemoryErr ); + + for( i = 0; i < gDNSConfigAdd_IPAddrCount; ++i ) + { + CFStringRef addrStr; + + addrStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_IPAddrArray[ i ], kCFStringEncodingUTF8 ); + require_action( addrStr, exit, err = kUnknownErr ); + + CFArrayAppendValue( array, addrStr ); + CFRelease( addrStr ); + } + + CFDictionarySetValue( dict, kSCPropNetDNSServerAddresses, array ); + ForgetCF( &array ); + + // Add domains, if any. + + array = CFArrayCreateMutable( NULL, (CFIndex) Min( gDNSConfigAdd_DomainCount, 1 ), &kCFTypeArrayCallBacks ); + require_action( array, exit, err = kNoMemoryErr ); + + if( gDNSConfigAdd_DomainCount > 0 ) + { + for( i = 0; i < gDNSConfigAdd_DomainCount; ++i ) + { + CFStringRef domainStr; + + domainStr = CFStringCreateWithCString( NULL, gDNSConfigAdd_DomainArray[ i ], kCFStringEncodingUTF8 ); + require_action( domainStr, exit, err = kUnknownErr ); + + CFArrayAppendValue( array, domainStr ); + CFRelease( domainStr ); + } + } + else + { + // There are no domains, but the domain array needs to be non-empty, so add a zero-length string to the array. + + CFArrayAppendValue( array, CFSTR( "" ) ); + } + + CFDictionarySetValue( dict, kSCPropNetDNSSupplementalMatchDomains, array ); + ForgetCF( &array ); + + // Add interface, if any. + + if( gDNSConfigAdd_Interface ) + { + err = CFDictionarySetCString( dict, kSCPropInterfaceName, gDNSConfigAdd_Interface, kSizeCString ); + require_noerr( err, exit ); + + CFDictionarySetValue( dict, kSCPropNetDNSConfirmedServiceID, gDNSConfigAdd_ID ); + } + + // Set dictionary in dynamic store. + + store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL ); + err = map_scerror( store ); + require_noerr( err, exit ); + + key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigAdd_ID, kSCEntNetDNS ); + require_action( key, exit, err = kUnknownErr ); + + success = SCDynamicStoreSetValue( store, key, dict ); + require_action( success, exit, err = kUnknownErr ); + +exit: + CFReleaseNullSafe( dict ); + CFReleaseNullSafe( array ); + CFReleaseNullSafe( store ); + CFReleaseNullSafe( key ); + gExitCode = err ? 1 : 0; +} + +//=========================================================================================================================== +// DNSConfigRemoveCmd +//=========================================================================================================================== + +static void DNSConfigRemoveCmd( void ) +{ + OSStatus err; + SCDynamicStoreRef store = NULL; + CFStringRef key = NULL; + Boolean success; + + if( geteuid() != 0 ) + { + FPrintF( stderr, "error: This command must to be run as root.\n" ); + err = kIDErr; + goto exit; + } + + store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL ); + err = map_scerror( store ); + require_noerr( err, exit ); + + key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState, gDNSConfigRemove_ID, kSCEntNetDNS ); + require_action( key, exit, err = kUnknownErr ); + + success = SCDynamicStoreRemoveValue( store, key ); + require_action( success, exit, err = kUnknownErr ); + +exit: + CFReleaseNullSafe( store ); + CFReleaseNullSafe( key ); + gExitCode = err ? 1 : 0; +} #endif // TARGET_OS_DARWIN //=========================================================================================================================== @@ -6833,33 +11123,98 @@ exit: static void Exit( void *inContext ) { const char * const reason = (const char *) inContext; - char time[ kTimestampBufLen ]; FPrintF( stdout, "---\n" ); - FPrintF( stdout, "End time: %s\n", GetTimestampStr( time ) ); + FPrintF( stdout, "End time: %{du:time}\n", NULL ); if( reason ) FPrintF( stdout, "End reason: %s\n", reason ); exit( gExitCode ); } //=========================================================================================================================== -// GetTimestampStr +// PrintFTimestampHandler //=========================================================================================================================== -static char * GetTimestampStr( char inBuffer[ kTimestampBufLen ] ) +static int + PrintFTimestampHandler( + PrintFContext * inContext, + PrintFFormat * inFormat, + PrintFVAList * inArgs, + void * inUserContext ) { - struct timeval now; - struct tm * tm; - size_t len; + struct timeval now; + const struct timeval * tv; + struct tm * localTime; + size_t len; + int n; + char dateTimeStr[ 32 ]; - gettimeofday( &now, NULL ); - tm = localtime( &now.tv_sec ); - require_action( tm, exit, *inBuffer = '\0' ); + Unused( inUserContext ); + + tv = va_arg( inArgs->args, const struct timeval * ); + require_action_quiet( !inFormat->suppress, exit, n = 0 ); + + if( !tv ) + { + gettimeofday( &now, NULL ); + tv = &now; + } + localTime = localtime( &tv->tv_sec ); + len = strftime( dateTimeStr, sizeof( dateTimeStr ), "%Y-%m-%d %H:%M:%S", localTime ); + if( len == 0 ) dateTimeStr[ 0 ] = '\0'; - len = strftime( inBuffer, kTimestampBufLen, "%Y-%m-%d %H:%M:%S", tm ); - SNPrintF( &inBuffer[ len ], kTimestampBufLen - len, ".%06u", (unsigned int) now.tv_usec ); + n = PrintFCore( inContext, "%s.%06u", dateTimeStr, (unsigned int) tv->tv_usec ); exit: - return( inBuffer ); + return( n ); +} + +//=========================================================================================================================== +// PrintFDNSMessageHandler +//=========================================================================================================================== + +static int + PrintFDNSMessageHandler( + PrintFContext * inContext, + PrintFFormat * inFormat, + PrintFVAList * inArgs, + void * inUserContext ) +{ + OSStatus err; + const void * msgPtr; + size_t msgLen; + char * text; + int n; + Boolean isMDNS; + Boolean printRawRData; + + Unused( inUserContext ); + + msgPtr = va_arg( inArgs->args, const void * ); + msgLen = va_arg( inArgs->args, size_t ); + require_action_quiet( !inFormat->suppress, exit, n = 0 ); + + isMDNS = ( inFormat->altForm > 0 ) ? true : false; + if( inFormat->precision == 0 ) printRawRData = false; + else if( inFormat->precision == 1 ) printRawRData = true; + else + { + n = PrintFCore( inContext, "<< BAD %%{du:dnsmsg} PRECISION >>" ); + goto exit; + } + + err = DNSMessageToText( msgPtr, msgLen, isMDNS, printRawRData, &text ); + if( !err ) + { + n = PrintFCore( inContext, "%*{text}", inFormat->fieldWidth, text, kSizeCString ); + free( text ); + } + else + { + n = PrintFCore( inContext, "%*.1H", inFormat->fieldWidth, msgPtr, (int) msgLen, (int) msgLen ); + } + +exit: + return( n ); } //=========================================================================================================================== @@ -7043,17 +11398,9 @@ static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outData else if( stricmp_prefix( inString, kRDataArgPrefix_Domain ) == 0 ) { const char * const str = inString + sizeof_string( kRDataArgPrefix_Domain ); - uint8_t * end; - uint8_t dname[ kDomainNameLengthMax ]; - - err = DomainNameFromString( dname, str, &end ); - require_noerr( err, exit ); - - dataLen = (size_t)( end - dname ); - dataPtr = malloc( dataLen ); - require_action( dataPtr, exit, err = kNoMemoryErr ); - memcpy( dataPtr, dname, dataLen ); + err = StringToDomainName( str, &dataPtr, &dataLen ); + require_noerr_quiet( err, exit ); } // File path @@ -7083,18 +11430,9 @@ static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outData else if( stricmp_prefix( inString, kRDataArgPrefix_IPv4 ) == 0 ) { const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv4 ); - const char * end; - - dataLen = 4; - dataPtr = (uint8_t *) malloc( dataLen ); - require_action( dataPtr, exit, err = kNoMemoryErr ); - - err = StringToIPv4Address( str, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, - (uint32_t *) dataPtr, NULL, NULL, NULL, &end ); - if( !err && ( *end != '\0' ) ) err = kMalformedErr; - require_noerr( err, exit ); - *( (uint32_t *) dataPtr ) = HostToBig32( *( (uint32_t *) dataPtr ) ); + err = StringToARecordData( str, &dataPtr, &dataLen ); + require_noerr_quiet( err, exit ); } // IPv6 address string @@ -7102,17 +11440,9 @@ static OSStatus RecordDataFromArgString( const char *inString, uint8_t **outData else if( stricmp_prefix( inString, kRDataArgPrefix_IPv6 ) == 0 ) { const char * const str = inString + sizeof_string( kRDataArgPrefix_IPv6 ); - const char * end; - - dataLen = 16; - dataPtr = (uint8_t *) malloc( dataLen ); - require_action( dataPtr, exit, err = kNoMemoryErr ); - err = StringToIPv6Address( str, - kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope, - dataPtr, NULL, NULL, NULL, &end ); - if( !err && ( *end != '\0' ) ) err = kMalformedErr; - require_noerr( err, exit ); + err = StringToAAAARecordData( str, &dataPtr, &dataLen ); + require_noerr_quiet( err, exit ); } // SRV record @@ -7963,6 +12293,18 @@ static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 ) return( true ); } +//=========================================================================================================================== +// DomainNameLength +//=========================================================================================================================== + +static size_t DomainNameLength( const uint8_t * const inName ) +{ + const uint8_t * ptr; + + for( ptr = inName; *ptr != 0; ptr += ( 1 + *ptr ) ) {} + return( (size_t)( ptr - inName ) + 1 ); +} + //=========================================================================================================================== // DomainNameFromString //=========================================================================================================================== @@ -8039,7 +12381,7 @@ exit: } //=========================================================================================================================== -// PrintDNSMessage +// DNSMessageToText //=========================================================================================================================== #define DNSFlagsOpCodeToString( X ) ( \ @@ -8059,12 +12401,17 @@ exit: ( (X) == kDNSRCode_Refused ) ? "Refused" : \ "???" ) -#define DNSFlagsGetOpCode( X ) ( ( (X) >> 11 ) & 0x0F ) -#define DNSFlagsGetRCode( X ) ( (X) & 0x0F ) - -static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const Boolean inIsMDNS, const Boolean inPrintRaw ) +static OSStatus + DNSMessageToText( + const uint8_t * inMsgPtr, + size_t inMsgLen, + const Boolean inMDNS, + const Boolean inPrintRaw, + char ** outText ) { OSStatus err; + DataBuffer dataBuf; + size_t len; const DNSHeader * hdr; const uint8_t * const msgEnd = inMsgPtr + inMsgLen; const uint8_t * ptr; @@ -8072,6 +12419,9 @@ static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const unsigned int questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount; char nameStr[ kDNSServiceMaxDomainName ]; + DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX ); + #define _Append( ... ) do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 ) + require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr ); hdr = (DNSHeader *) inMsgPtr; @@ -8084,24 +12434,26 @@ static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const opcode = DNSFlagsGetOpCode( flags ); rcode = DNSFlagsGetRCode( flags ); - FPrintF( stdout, "ID: 0x%04X (%u)\n", id, id ); - FPrintF( stdout, "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA %s\n", + _Append( "ID: 0x%04X (%u)\n", id, id ); + _Append( "Flags: 0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n", flags, ( flags & kDNSHeaderFlag_Response ) ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ), ( flags & kDNSHeaderFlag_AuthAnswer ) ? ' ' : '!', ( flags & kDNSHeaderFlag_Truncation ) ? ' ' : '!', ( flags & kDNSHeaderFlag_RecursionDesired ) ? ' ' : '!', ( flags & kDNSHeaderFlag_RecursionAvailable ) ? ' ' : '!', + !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData ) ? " AD" : "!AD", + !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled ) ? " CD" : "!CD", DNSFlagsRCodeToString( rcode ) ); - FPrintF( stdout, "Question count: %u\n", questionCount ); - FPrintF( stdout, "Answer count: %u\n", answerCount ); - FPrintF( stdout, "Authority count: %u\n", authorityCount ); - FPrintF( stdout, "Additional count: %u\n", additionalCount ); + _Append( "Question count: %u\n", questionCount ); + _Append( "Answer count: %u\n", answerCount ); + _Append( "Authority count: %u\n", authorityCount ); + _Append( "Additional count: %u\n", additionalCount ); - ptr = (uint8_t *)( hdr + 1 ); + ptr = (const uint8_t *) &hdr[ 1 ]; for( i = 0; i < questionCount; ++i ) { - unsigned int qType, qClass; + unsigned int qtype, qclass; Boolean isQU; err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, nameStr, &ptr ); @@ -8113,20 +12465,18 @@ static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const goto exit; } - qType = ReadBig16( ptr ); - ptr += 2; - qClass = ReadBig16( ptr ); - ptr += 2; + qtype = DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields *) ptr ); + qclass = DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields *) ptr ); + ptr += 4; - isQU = ( inIsMDNS && ( qClass & kQClassUnicastResponseBit ) ) ? true : false; - if( inIsMDNS ) qClass &= ~kQClassUnicastResponseBit; + isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false; + if( inMDNS ) qclass &= ~kQClassUnicastResponseBit; - if( i == 0 ) FPrintF( stdout, "\nQUESTION SECTION\n" ); + if( i == 0 ) _Append( "\nQUESTION SECTION\n" ); - FPrintF( stdout, "%s %2s %?2s%?2u %-5s\n", - nameStr, inIsMDNS ? ( isQU ? "QU" : "QM" ) : "", - ( qClass == kDNSServiceClass_IN ), "IN", ( qClass != kDNSServiceClass_IN ), qClass, - RecordTypeToString( qType ) ); + _Append( "%s %2s %?2s%?2u %-5s\n", + nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "", + ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) ); } totalRRCount = answerCount + authorityCount + additionalCount; @@ -8147,8 +12497,8 @@ static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const err = DomainNameToString( name, NULL, nameStr, NULL ); require_noerr( err, exit ); - cacheFlush = ( inIsMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false; - if( inIsMDNS ) class &= ~kRRClassCacheFlushBit; + cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false; + if( inMDNS ) class &= ~kRRClassCacheFlushBit; rdataStr = NULL; if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr ); @@ -8158,20 +12508,26 @@ static OSStatus PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const require_action( rdataStr, exit, err = kNoMemoryErr ); } - if( answerCount && ( i == 0 ) ) FPrintF( stdout, "\nANSWER SECTION\n" ); - else if( authorityCount && ( i == answerCount ) ) FPrintF( stdout, "\nAUTHORITY SECTION\n" ); - else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) FPrintF( stdout, "\nADDITIONAL SECTION\n" ); + if( answerCount && ( i == 0 ) ) _Append( "\nANSWER SECTION\n" ); + else if( authorityCount && ( i == answerCount ) ) _Append( "\nAUTHORITY SECTION\n" ); + else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" ); - FPrintF( stdout, "%-42s %6u %2s %?2s%?2u %-5s %s\n", + _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n", nameStr, ttl, cacheFlush ? "CF" : "", ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class, RecordTypeToString( type ), rdataStr ); free( rdataStr ); } - FPrintF( stdout, "\n" ); - err = kNoErr; + _Append( "\n" ); + + err = DataBuffer_Append( &dataBuf, "", 1 ); + require_noerr( err, exit ); + + err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len ); + require_noerr( err, exit ); exit: + DataBuffer_Free( &dataBuf ); return( err ); } @@ -8194,21 +12550,18 @@ static OSStatus uint8_t * ptr; size_t msgLen; - WriteBig16( hdr->id, inMsgID ); - WriteBig16( hdr->flags, inFlags ); - WriteBig16( hdr->questionCount, 1 ); - WriteBig16( hdr->answerCount, 0 ); - WriteBig16( hdr->authorityCount, 0 ); - WriteBig16( hdr->additionalCount, 0 ); + memset( hdr, 0, sizeof( *hdr ) ); + DNSHeaderSetID( hdr, inMsgID ); + DNSHeaderSetFlags( hdr, inFlags ); + DNSHeaderSetQuestionCount( hdr, 1 ); ptr = (uint8_t *)( hdr + 1 ); err = DomainNameFromString( ptr, inQName, &ptr ); require_noerr_quiet( err, exit ); - WriteBig16( ptr, inQType ); - ptr += 2; - WriteBig16( ptr, inQClass ); - ptr += 2; + DNSQuestionFixedFieldsInit( (DNSQuestionFixedFields *) ptr, inQType, inQClass ); + ptr += 4; + msgLen = (size_t)( ptr - inMsg ); check( msgLen <= kDNSQueryMessageMaxLen ); @@ -8246,21 +12599,23 @@ exit: } //=========================================================================================================================== -// DispatchReadSourceCreate +// DispatchSocketSourceCreate //=========================================================================================================================== static OSStatus - DispatchReadSourceCreate( - SocketRef inSock, - DispatchHandler inEventHandler, - DispatchHandler inCancelHandler, - void * inContext, - dispatch_source_t * outSource ) + DispatchSocketSourceCreate( + SocketRef inSock, + dispatch_source_type_t inType, + dispatch_queue_t inQueue, + DispatchHandler inEventHandler, + DispatchHandler inCancelHandler, + void * inContext, + dispatch_source_t * outSource ) { OSStatus err; dispatch_source_t source; - source = dispatch_source_create( DISPATCH_SOURCE_TYPE_READ, (uintptr_t) inSock, 0, dispatch_get_main_queue() ); + source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() ); require_action( source, exit, err = kUnknownErr ); dispatch_set_context( source, inContext ); @@ -8283,6 +12638,7 @@ static OSStatus dispatch_time_t inStart, uint64_t inIntervalNs, uint64_t inLeewayNs, + dispatch_queue_t inQueue, DispatchHandler inEventHandler, DispatchHandler inCancelHandler, void * inContext, @@ -8291,7 +12647,7 @@ static OSStatus OSStatus err; dispatch_source_t timer; - timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() ); + timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() ); require_action( timer, exit, err = kUnknownErr ); dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs ); @@ -8306,6 +12662,38 @@ exit: return( err ); } +//=========================================================================================================================== +// DispatchProcessMonitorCreate +//=========================================================================================================================== + +static OSStatus + DispatchProcessMonitorCreate( + pid_t inPID, + unsigned long inFlags, + dispatch_queue_t inQueue, + DispatchHandler inEventHandler, + DispatchHandler inCancelHandler, + void * inContext, + dispatch_source_t * outMonitor ) +{ + OSStatus err; + dispatch_source_t monitor; + + monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags, + inQueue ? inQueue : dispatch_get_main_queue() ); + require_action( monitor, exit, err = kUnknownErr ); + + dispatch_set_context( monitor, inContext ); + dispatch_source_set_event_handler_f( monitor, inEventHandler ); + dispatch_source_set_cancel_handler_f( monitor, inCancelHandler ); + + *outMonitor = monitor; + err = kNoErr; + +exit: + return( err ); +} + //=========================================================================================================================== // ServiceTypeDescription //=========================================================================================================================== @@ -8368,16 +12756,59 @@ static const char * ServiceTypeDescription( const char *inName ) return( NULL ); } +//=========================================================================================================================== +// SocketContextCreate +//=========================================================================================================================== + +static OSStatus SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext ) +{ + OSStatus err; + SocketContext * context; + + context = (SocketContext *) calloc( 1, sizeof( *context ) ); + require_action( context, exit, err = kNoMemoryErr ); + + context->refCount = 1; + context->sock = inSock; + context->userContext = inUserContext; + + *outContext = context; + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// SocketContextRetain +//=========================================================================================================================== + +static SocketContext * SocketContextRetain( SocketContext *inContext ) +{ + ++inContext->refCount; + return( inContext ); +} + +//=========================================================================================================================== +// SocketContextRelease +//=========================================================================================================================== + +static void SocketContextRelease( SocketContext *inContext ) +{ + if( --inContext->refCount == 0 ) + { + ForgetSocket( &inContext->sock ); + free( inContext ); + } +} + //=========================================================================================================================== // SocketContextCancelHandler //=========================================================================================================================== static void SocketContextCancelHandler( void *inContext ) { - SocketContext * const context = (SocketContext *) inContext; - - ForgetSocket( &context->sock ); - free( context ); + SocketContextRelease( (SocketContext *) inContext ); } //=========================================================================================================================== @@ -8421,6 +12852,115 @@ exit: return( err ); } +//=========================================================================================================================== +// StringToLongLong +//=========================================================================================================================== + +static OSStatus StringToLongLong( const char *inString, long long *outValue ) +{ + OSStatus err; + long long value; + char * endPtr; + + set_errno_compat( 0 ); + value = strtol( inString, &endPtr, 0 ); + err = errno_compat(); + if( ( ( value == LLONG_MIN ) || ( value == LLONG_MAX ) ) && ( err == ERANGE ) ) goto exit; + require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr ); + + *outValue = value; + err = kNoErr; + +exit: + return( err ); +} + +//=========================================================================================================================== +// StringToARecordData +//=========================================================================================================================== + +static OSStatus StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen ) +{ + OSStatus err; + uint32_t * addrPtr; + const size_t addrLen = sizeof( *addrPtr ); + const char * end; + + addrPtr = (uint32_t *) malloc( addrLen ); + require_action( addrPtr, exit, err = kNoMemoryErr ); + + err = StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr, + NULL, NULL, NULL, &end ); + if( !err && ( *end != '\0' ) ) err = kMalformedErr; + require_noerr_quiet( err, exit ); + + *addrPtr = HostToBig32( *addrPtr ); + + *outPtr = (uint8_t *) addrPtr; + addrPtr = NULL; + *outLen = addrLen; + +exit: + FreeNullSafe( addrPtr ); + return( err ); +} + +//=========================================================================================================================== +// StringToAAAARecordData +//=========================================================================================================================== + +static OSStatus StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen ) +{ + OSStatus err; + uint8_t * addrPtr; + const size_t addrLen = 16; + const char * end; + + addrPtr = (uint8_t *) malloc( addrLen ); + require_action( addrPtr, exit, err = kNoMemoryErr ); + + err = StringToIPv6Address( inString, + kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope, + addrPtr, NULL, NULL, NULL, &end ); + if( !err && ( *end != '\0' ) ) err = kMalformedErr; + require_noerr_quiet( err, exit ); + + *outPtr = addrPtr; + addrPtr = NULL; + *outLen = addrLen; + +exit: + FreeNullSafe( addrPtr ); + return( err ); +} + +//=========================================================================================================================== +// StringToDomainName +//=========================================================================================================================== + +static OSStatus StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen ) +{ + OSStatus err; + uint8_t * namePtr; + size_t nameLen; + uint8_t * end; + uint8_t nameBuf[ kDomainNameLengthMax ]; + + err = DomainNameFromString( nameBuf, inString, &end ); + require_noerr_quiet( err, exit ); + + nameLen = (size_t)( end - nameBuf ); + namePtr = memdup( nameBuf, nameLen ); + require_action( namePtr, exit, err = kNoMemoryErr ); + + *outPtr = namePtr; + namePtr = NULL; + if( outLen ) *outLen = nameLen; + +exit: + return( err ); +} + #if( TARGET_OS_DARWIN ) //=========================================================================================================================== // GetDefaultDNSServer @@ -8446,7 +12986,7 @@ static OSStatus GetDefaultDNSServer( sockaddr_ip *outAddr ) addr = resolver->nameserver[ 0 ]; break; } - } + } require_action_quiet( addr, exit, err = kNotFoundErr ); SockAddrCopy( addr, outAddr ); @@ -8458,6 +12998,20 @@ exit: } #endif +//=========================================================================================================================== +// GetCurrentMicroTime +//=========================================================================================================================== + +static MicroTime64 GetCurrentMicroTime( void ) +{ + struct timeval now; + + TIMEVAL_ZERO( now ); + gettimeofday( &now, NULL ); + + return( (MicroTime64) TIMEVAL_USEC64( now ) ); +} + //=========================================================================================================================== // SocketWriteAll // @@ -8918,47 +13472,6 @@ exit: return( err ); } -//=========================================================================================================================== -// StringArray_Append -// -// Note: This was copied from CoreUtils because the StringArray_Append function is currently not exported in the framework. -//=========================================================================================================================== - -OSStatus StringArray_Append( char ***ioArray, size_t *ioCount, const char *inStr ) -{ - OSStatus err; - char * newStr; - size_t oldCount; - size_t newCount; - char ** oldArray; - char ** newArray; - - newStr = strdup( inStr ); - require_action( newStr, exit, err = kNoMemoryErr ); - - oldCount = *ioCount; - newCount = oldCount + 1; - newArray = (char **) malloc( newCount * sizeof( *newArray ) ); - require_action( newArray, exit, err = kNoMemoryErr ); - - if( oldCount > 0 ) - { - oldArray = *ioArray; - memcpy( newArray, oldArray, oldCount * sizeof( *oldArray ) ); - free( oldArray ); - } - newArray[ oldCount ] = newStr; - newStr = NULL; - - *ioArray = newArray; - *ioCount = newCount; - err = kNoErr; - -exit: - if( newStr ) free( newStr ); - return( err ); -} - //=========================================================================================================================== // StringArray_Free // @@ -9150,3 +13663,193 @@ Boolean if( outSrc ) *outSrc = (const char *) src; return( true ); } + +//=========================================================================================================================== +// _ServerSocketOpenEx2 +// +// Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT. +//=========================================================================================================================== + +static OSStatus + _ServerSocketOpenEx2( + int inFamily, + int inType, + int inProtocol, + const void * inAddr, + int inPort, + int * outPort, + int inRcvBufSize, + Boolean inNoPortReuse, + SocketRef * outSock ) +{ + OSStatus err; + int port; + SocketRef sock; + int name; + int option; + sockaddr_ip sip; + socklen_t len; + + port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic". + + sock = socket( inFamily, inType, inProtocol ); + err = map_socket_creation_errno( sock ); + require_noerr_quiet( err, exit ); + +#if( defined( SO_NOSIGPIPE ) ) + setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) ); +#endif + + err = SocketMakeNonBlocking( sock ); + require_noerr( err, exit ); + + // Set receive buffer size. This has to be done on the listening socket *before* listen is called because + // accept does not return until after the window scale option is exchanged during the 3-way handshake. + // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer + // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5. + + err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize ); + check_noerr( err ); + + // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port. + + if( ( inType != SOCK_DGRAM ) || !inNoPortReuse ) + { + option = 1; + name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR; + err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) ); + err = map_socket_noerr_errno( sock, err ); + require_noerr( err, exit ); + } + + if( inFamily == AF_INET ) + { + // Bind to the port. If it fails, retry with a dynamic port. + + memset( &sip.v4, 0, sizeof( sip.v4 ) ); + SIN_LEN_SET( &sip.v4 ); + sip.v4.sin_family = AF_INET; + sip.v4.sin_port = htons( (uint16_t) port ); + sip.v4.sin_addr.s_addr = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY ); + err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) ); + err = map_socket_noerr_errno( sock, err ); + if( err && ( inPort < 0 ) ) + { + sip.v4.sin_port = 0; + err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) ); + err = map_socket_noerr_errno( sock, err ); + } + require_noerr( err, exit ); + } +#if( defined( AF_INET6 ) ) + else if( inFamily == AF_INET6 ) + { + // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4. + + option = 1; + err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) ); + err = map_socket_noerr_errno( sock, err ); + require_noerr( err, exit ); + + // Bind to the port. If it fails, retry with a dynamic port. + + memset( &sip.v6, 0, sizeof( sip.v6 ) ); + SIN6_LEN_SET( &sip.v6 ); + sip.v6.sin6_family = AF_INET6; + sip.v6.sin6_port = htons( (uint16_t) port ); + sip.v6.sin6_addr = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any; + err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) ); + err = map_socket_noerr_errno( sock, err ); + if( err && ( inPort < 0 ) ) + { + sip.v6.sin6_port = 0; + err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) ); + err = map_socket_noerr_errno( sock, err ); + } + require_noerr( err, exit ); + } +#endif + else + { + dlogassert( "Unsupported family: %d", inFamily ); + err = kUnsupportedErr; + goto exit; + } + + if( inType == SOCK_STREAM ) + { + err = listen( sock, SOMAXCONN ); + err = map_socket_noerr_errno( sock, err ); + if( err ) + { + err = listen( sock, 5 ); + err = map_socket_noerr_errno( sock, err ); + require_noerr( err, exit ); + } + } + + if( outPort ) + { + len = (socklen_t) sizeof( sip ); + err = getsockname( sock, &sip.sa, &len ); + err = map_socket_noerr_errno( sock, err ); + require_noerr( err, exit ); + + *outPort = SockAddrGetPort( &sip ); + } + *outSock = sock; + sock = kInvalidSocketRef; + +exit: + ForgetSocket( &sock ); + return( err ); +} + +//=========================================================================================================================== +// memdup +// +// Note: This was copied from CoreUtils because it's currently not exported in the framework. +//=========================================================================================================================== + +void * memdup( const void *inPtr, size_t inLen ) +{ + void * mem; + + mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined. + require( mem, exit ); + if( inLen > 0 ) memcpy( mem, inPtr, inLen ); + +exit: + return( mem ); +} + +#if( !TARGET_OS_WINDOWS ) +//=========================================================================================================================== +// memicmp +// +// Note: This was copied from CoreUtils because it's currently not exported in the framework. +//=========================================================================================================================== + +int memicmp( const void *inP1, const void *inP2, size_t inLen ) +{ + const unsigned char * p1; + const unsigned char * e1; + const unsigned char * p2; + int c1; + int c2; + + p1 = (const unsigned char *) inP1; + e1 = p1 + inLen; + p2 = (const unsigned char *) inP2; + while( p1 < e1 ) + { + c1 = *p1++; + c2 = *p2++; + c1 = tolower( c1 ); + c2 = tolower( c2 ); + if( c1 < c2 ) return( -1 ); + if( c1 > c2 ) return( 1 ); + } + return( 0 ); +} +#endif diff --git a/mDNSResponder/Makefile b/mDNSResponder/Makefile index 978c8953..d50841c2 100644 --- a/mDNSResponder/Makefile +++ b/mDNSResponder/Makefile @@ -11,12 +11,13 @@ # install: # installsrc: # installhdrs: +# installapi: # clean: # include $(MAKEFILEPATH)/pb_makefiles/platform.make -MVERS = "mDNSResponder-878.70.2" +MVERS = "mDNSResponder-878.200.35" VER = ifneq ($(strip $(GCC_VERSION)),) @@ -44,6 +45,9 @@ 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) +installapi: + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installapi OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibrariesDynamic $(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/mDNSResponder/mDNSCore/DNSCommon.c b/mDNSResponder/mDNSCore/DNSCommon.c index 597c4cc0..a249b967 100644 --- a/mDNSResponder/mDNSCore/DNSCommon.c +++ b/mDNSResponder/mDNSCore/DNSCommon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2015 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -3450,7 +3450,6 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage rr->TimeRcvd = m ? m->timenow : 0; rr->DelayDelivery = 0; rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord() - rr->LastUsed = m ? m->timenow : 0; rr->CRActiveQuestion = mDNSNULL; rr->UnansweredQueries = 0; rr->LastUnansweredTime= 0; @@ -3630,25 +3629,6 @@ mDNSexport mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, cons return mDNSfalse; } -mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label) -{ - int i; - 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, - // but since it's only used for debugging (and probably only on OS X, not on - // 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) - LogInfo("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); - } - if (!ptr) - LogInfo("DumpRecords: ERROR: Premature end of packet data"); - return(ptr); -} - #define DNS_OP_Name(X) ( \ (X) == kDNSFlag0_OP_StdQuery ? "" : \ (X) == kDNSFlag0_OP_Iquery ? "Iquery " : \ @@ -3672,52 +3652,198 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, (X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \ (X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" ) -// Note: DumpPacket expects the packet header fields in host byte order, not network byte order -mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, - const mDNSAddr *srcaddr, mDNSIPPort srcport, - const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) +mDNSlocal void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, ...) { - mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update); - const mDNSu8 *ptr = msg->data; - int i; - DNSQuestion q; - char tbuffer[64], sbuffer[64], dbuffer[64] = ""; - if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0; - else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receive")] = 0; - if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0; - else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0; - if (dstaddr || !mDNSIPPortIsZero(dstport)) - dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; - - 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, + va_list args; + mDNSu32 buflen, n; + char *const dst = *ptr; + + buflen = (mDNSu32)(lim - dst); + if (buflen > 0) + { + va_start(args, fmt); + n = mDNS_vsnprintf(dst, buflen, fmt, args); + va_end(args); + *ptr = dst + n; + } +} + +#define DNSTypeString(X) (((X) == kDNSType_A) ? "A" : DNSTypeName(X)) + +#define ReadField16(PTR) ((mDNSu16)((((mDNSu16)((mDNSu8 *)(PTR))[0]) << 8) | ((mDNSu16)((mDNSu8 *)(PTR))[1]))) +#define ReadField32(PTR) \ + ((mDNSu32)( \ + (((mDNSu32)((mDNSu8 *)(PTR))[0]) << 24) | \ + (((mDNSu32)((mDNSu8 *)(PTR))[1]) << 16) | \ + (((mDNSu32)((mDNSu8 *)(PTR))[2]) << 8) | \ + ((mDNSu32)((mDNSu8 *)(PTR))[3]))) + +mDNSlocal void DNSMessageDump(const DNSMessage *const msg, const mDNSu8 *const end, char *buffer, mDNSu32 buflen) +{ + domainname *name; + const mDNSu8 *ptr; + domainname nameStorage[2]; + char *dst = buffer; + const char *const lim = &buffer[buflen]; + mDNSu32 i; + const mDNSu32 rrcount = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; + + mDNS_snprintf_add(&dst, lim, "DNS %s%s (%lu) (flags %02X%02X) RCODE: %s (%d)%s%s%s%s%s%s ID: %u:", DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), - msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", + (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "Response" : "Query", + (unsigned long)(end - (const mDNSu8 *)msg), msg->h.flags.b[0], msg->h.flags.b[1], DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask), msg->h.flags.b[1] & kDNSFlag1_RC_Mask, - msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "", - msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "", - msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "", - msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "", - msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "", - msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "", - mDNSVal16(msg->h.id), - end - msg->data, - sbuffer, mDNSVal16(srcport), dbuffer, - (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" - ); - - LogInfo("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); - for (i = 0; i < msg->h.numQuestions && ptr; i++) + (msg->h.flags.b[0] & kDNSFlag0_AA) ? " AA" : "", + (msg->h.flags.b[0] & kDNSFlag0_TC) ? " TC" : "", + (msg->h.flags.b[0] & kDNSFlag0_RD) ? " RD" : "", + (msg->h.flags.b[1] & kDNSFlag1_RA) ? " RA" : "", + (msg->h.flags.b[1] & kDNSFlag1_AD) ? " AD" : "", + (msg->h.flags.b[1] & kDNSFlag1_CD) ? " CD" : "", + mDNSVal16(msg->h.id)); + + name = mDNSNULL; + ptr = msg->data; + for (i = 0; i < msg->h.numQuestions; i++) { - ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); - if (ptr) LogInfo("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); + mDNSu16 qtype, qclass; + + name = &nameStorage[0]; + ptr = getDomainName(msg, ptr, end, name); + if (!ptr) goto exit; + + if ((end - ptr) < 4) goto exit; + qtype = ReadField16(&ptr[0]); + qclass = ReadField16(&ptr[2]); + ptr += 4; + + mDNS_snprintf_add(&dst, lim, " %##s %s", name->c, DNSTypeString(qtype)); + if (qclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", qclass); + mDNS_snprintf_add(&dst, lim, "?"); } - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers"); - ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities"); - DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals"); - LogInfo("--------------"); + + mDNS_snprintf_add(&dst, lim, " %u/%u/%u", msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals); + for (i = 0; i < rrcount; i++) + { + mDNSu16 rrtype, rrclass, rdlength; + mDNSu32 ttl; + int handled; + const mDNSu8 *rdata; + const domainname *const previousName = name; + + name = &nameStorage[(name == &nameStorage[0]) ? 1 : 0]; + ptr = getDomainName(msg, ptr, end, name); + if (!ptr) goto exit; + + if ((end - ptr) < 10) goto exit; + rrtype = ReadField16(&ptr[0]); + rrclass = ReadField16(&ptr[2]); + ttl = ReadField32(&ptr[4]); + rdlength = ReadField16(&ptr[8]); + ptr += 10; + + if ((end - ptr) < rdlength) goto exit; + rdata = ptr; + + if (i > 0) mDNS_snprintf_add(&dst, lim, ","); + if (!previousName || !SameDomainName(name, previousName)) mDNS_snprintf_add(&dst, lim, " %##s", name); + + mDNS_snprintf_add(&dst, lim, " %s", DNSTypeString(rrtype)); + if (rrclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", rrclass); + mDNS_snprintf_add(&dst, lim, " "); + + handled = mDNSfalse; + switch (rrtype) + { + case kDNSType_A: + if (rdlength == 4) + { + mDNS_snprintf_add(&dst, lim, "%.4a", rdata); + handled = mDNStrue; + } + break; + + case kDNSType_AAAA: + if (rdlength == 16) + { + mDNS_snprintf_add(&dst, lim, "%.16a", rdata); + handled = mDNStrue; + } + break; + + case kDNSType_CNAME: + ptr = getDomainName(msg, rdata, end, name); + if (!ptr) goto exit; + + mDNS_snprintf_add(&dst, lim, "%##s", name); + handled = mDNStrue; + break; + + case kDNSType_SOA: + { + mDNSu32 serial, refresh, retry, expire, minimum; + domainname *const mname = &nameStorage[0]; + domainname *const rname = &nameStorage[1]; + name = mDNSNULL; + + ptr = getDomainName(msg, rdata, end, mname); + if (!ptr) goto exit; + + ptr = getDomainName(msg, ptr, end, rname); + if (!ptr) goto exit; + + if ((end - ptr) < 20) goto exit; + serial = ReadField32(&ptr[0]); + refresh = ReadField32(&ptr[4]); + retry = ReadField32(&ptr[8]); + expire = ReadField32(&ptr[12]); + minimum = ReadField32(&ptr[16]); + + mDNS_snprintf_add(&dst, lim, "%##s %##s %lu %lu %lu %lu %lu", mname, rname, (unsigned long)serial, + (unsigned long)refresh, (unsigned long)retry, (unsigned long)expire, (unsigned long)minimum); + + handled = mDNStrue; + break; + } + + default: + break; + } + if (!handled) mDNS_snprintf_add(&dst, lim, "RDATA[%u]: %.*H", rdlength, rdlength, rdata); + mDNS_snprintf_add(&dst, lim, " (%lu)", (unsigned long)ttl); + ptr = rdata + rdlength; + } + +exit: + return; +} + +// Note: DumpPacket expects the packet header fields in host byte order, not network byte order +mDNSexport void DumpPacket(mStatus status, mDNSBool sent, char *transport, + const mDNSAddr *srcaddr, mDNSIPPort srcport, + const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end) +{ + char buffer[512]; + char *dst = buffer; + const char *const lim = &buffer[512]; + + buffer[0] = '\0'; + if (!status) mDNS_snprintf_add(&dst, lim, sent ? "Sent" : "Received"); + else mDNS_snprintf_add(&dst, lim, "ERROR %d %sing", status, sent ? "Send" : "Receiv"); + + mDNS_snprintf_add(&dst, lim, " %s DNS Message %u bytes from ", transport, (unsigned long)(end - (const mDNSu8 *)msg)); + + if (sent) mDNS_snprintf_add(&dst, lim, "port %d", mDNSVal16(srcport)); + else mDNS_snprintf_add(&dst, lim, "%#a:%d", srcaddr, mDNSVal16(srcport)); + + if (dstaddr || !mDNSIPPortIsZero(dstport)) mDNS_snprintf_add(&dst, lim, " to %#a:%d", dstaddr, mDNSVal16(dstport)); + + LogInfo("%s", buffer); + + buffer[0] = '\0'; + DNSMessageDump(msg, end, buffer, (mDNSu32)sizeof(buffer)); + LogInfo("%s", buffer); } // *************************************************************************** @@ -3824,7 +3950,7 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS // Dump the packet with the HINFO and TSIG if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id)) - DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); + DumpPacket(status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end); // put the number of additionals back the way it was msg->h.numAdditionals = numAdditionals; @@ -4053,6 +4179,9 @@ static const struct mDNSprintf_format unsigned int precision; } mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +#define kHexDigitsLowercase "0123456789abcdef" +#define kHexDigitsUppercase "0123456789ABCDEF"; + mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) { mDNSu32 nwritten = 0; @@ -4064,6 +4193,7 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt for (c = *fmt; c != 0; c = *++fmt) { unsigned long n; + int hexdump = mDNSfalse; if (c != '%') { *sbuffer++ = (char)c; @@ -4190,10 +4320,63 @@ decimal: if (!F.havePrecision) a[0], a[1], a[2], a[3]); break; case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5]); break; - case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), - "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", - a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7], - a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break; + case 16: { + // Print IPv6 addresses according to RFC 5952, A Recommendation for IPv6 Address Text + // Representation. See . + + int idx, runLen = 0, runStart = 0, maxRunLen = 0, maxRunStart = 0, maxRunEnd; + + // Find the leftmost longest run of consecutive zero hextets. + for (idx = 0; idx < 8; ++idx) + { + const unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1]; + if (hextet == 0) + { + if (runLen++ == 0) runStart = idx; + if (runLen > maxRunLen) + { + maxRunStart = runStart; + maxRunLen = runLen; + } + } + else + { + // If the number of remaining hextets is less than or equal to the length of the longest + // run so far, then we've found the leftmost longest run. + if ((8 - (idx + 1)) <= maxRunLen) break; + runLen = 0; + } + } + + // Compress the leftmost longest run of two or more consecutive zero hextets as "::". + // For each reminaing hextet, suppress zeros leading up to the least-significant nibble, which + // is always written, even if it's zero. Because of this requirement, it's easier to write the + // IPv6 address in reverse. Also, write a colon separator before each hextet except for the + // first one. + s = mDNS_VACB_Lim; + maxRunEnd = (maxRunLen >= 2) ? (maxRunStart + maxRunLen - 1) : -1; + for (idx = 7; idx >= 0; --idx) + { + if (idx == maxRunEnd) + { + if (idx == 7) *--s = ':'; + idx = maxRunStart; + *--s = ':'; + } + else + { + unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1]; + do { + *--s = kHexDigitsLowercase[hextet % 16]; + hextet /= 16; + } while (hextet); + if (idx > 0) *--s = ':'; + } + } + i = (unsigned int)(mDNS_VACB_Lim - s); + } + break; + default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify" " address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break; } @@ -4203,9 +4386,9 @@ decimal: if (!F.havePrecision) case 'p': F.havePrecision = F.lSize = 1; F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit - case 'X': digits = "0123456789ABCDEF"; + case 'X': digits = kHexDigitsUppercase; goto hexadecimal; - case 'x': digits = "0123456789abcdef"; + case 'x': digits = kHexDigitsLowercase; hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); else n = va_arg(arg, unsigned int); if (F.hSize) n = (unsigned short) n; @@ -4288,6 +4471,12 @@ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); { i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} break; + case 'H': { + s = va_arg(arg, char *); + hexdump = mDNStrue; + } + break; + case 'n': s = va_arg(arg, char *); if (F.hSize) *(short *) s = (short)nwritten; else if (F.lSize) *(long *) s = (long)nwritten; @@ -4308,14 +4497,34 @@ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); if (++nwritten >= buflen) goto exit; } while (i < --F.fieldWidth); - // Make sure we don't truncate in the middle of a UTF-8 character. - // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the - // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, - // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly - // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). - if (i > buflen - nwritten) - { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} - for (j=0; j 0) *dst++ = ' '; + if (dst < lim) *dst++ = kHexDigitsLowercase[(b >> 4) & 0xF]; + if (dst < lim) *dst++ = kHexDigitsLowercase[ b & 0xF]; + } + } + i = (unsigned int)(dst - sbuffer); + sbuffer = dst; + } + else + { + // Make sure we don't truncate in the middle of a UTF-8 character. + // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the + // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half, + // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly + // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated). + if (i > buflen - nwritten) + { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;} + for (j=0; j= buflen) goto exit; diff --git a/mDNSResponder/mDNSCore/DNSCommon.h b/mDNSResponder/mDNSCore/DNSCommon.h index e1ef261e..b100a400 100644 --- a/mDNSResponder/mDNSCore/DNSCommon.h +++ b/mDNSResponder/mDNSCore/DNSCommon.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -260,7 +260,7 @@ extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 extern const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize); extern const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end); extern mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, mDNSu32 *const lease); -extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport, +extern void DumpPacket(mStatus status, mDNSBool sent, char *transport, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end); extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type); diff --git a/mDNSResponder/mDNSCore/mDNS.c b/mDNSResponder/mDNSCore/mDNS.c index 0788ab67..8deada2e 100755 --- a/mDNSResponder/mDNSCore/mDNS.c +++ b/mDNSResponder/mDNSCore/mDNS.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2017 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -548,6 +548,7 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero q->CNAMEReferrals = c; #if AWD_METRICS + metrics.expiredAnswerState = q->metrics.expiredAnswerState; // We want the newly initialized state for this value q->metrics = metrics; #endif if (sock) @@ -785,6 +786,7 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define GoodbyeCount ((mDNSu8)3) #define WakeupCount ((mDNSu8)18) #define MAX_PROBE_RESTARTS ((mDNSu8)20) +#define MAX_GHOST_TIME ((mDNSs32)((60*60*24*7)*mDNSPlatformOneSecond)) // One week // Number of wakeups we send if WakeOnResolve is set in the question #define InitialWakeOnResolveCount ((mDNSu8)3) @@ -3220,21 +3222,25 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf // Depth 3: PTR "_services._dns-sd._udp.local." refers to "_example._tcp.local."; may be stale // Currently depths 4 and 5 are not expected to occur; if we did get to depth 5 we'd reconfim any records we // found referring to the given name, but not recursively descend any further reconfirm *their* antecedents. -mDNSlocal void ReconfirmAntecedents(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const int depth) +mDNSlocal void ReconfirmAntecedents(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSInterfaceID InterfaceID, const int depth) { mDNSu32 slot; - CacheGroup *cg; + const CacheGroup *cg; CacheRecord *cr; debugf("ReconfirmAntecedents (depth=%d) for %##s", depth, name->c); + if (!InterfaceID) return; // mDNS records have a non-zero InterfaceID. If InterfaceID is 0, then there's nothing to do. FORALL_CACHERECORDS(slot, cg, cr) { - domainname *crtarget = GetRRDomainNameTarget(&cr->resrec); - if (crtarget && cr->resrec.rdatahash == namehash && SameDomainName(crtarget, name)) + const domainname *crtarget; + if (cr->resrec.InterfaceID != InterfaceID) continue; // Skip non-mDNS records and mDNS records from other interfaces. + if (cr->resrec.rdatahash != namehash) continue; // Skip records whose rdata hash doesn't match the name hash. + crtarget = GetRRDomainNameTarget(&cr->resrec); + if (crtarget && SameDomainName(crtarget, name)) { - LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth, CRDisplayString(m, cr)); + LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d, InterfaceID=%p) %s", depth, InterfaceID, CRDisplayString(m, cr)); mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); if (depth < 5) - ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, depth+1); + ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, InterfaceID, depth+1); } } } @@ -3612,7 +3618,8 @@ mDNSlocal void SendQueries(mDNS *const m) { q->ThisQInterval = MaxQuestionInterval; } - else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && + else if (mDNSOpaque16IsZero(q->TargetQID) && q->InterfaceID && + q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash))) { // Generally don't need to log this. @@ -3623,7 +3630,7 @@ mDNSlocal void SendQueries(mDNS *const m) debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); // Sending third query, and no answers yet; time to begin doubting the source - ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); + ReconfirmAntecedents(m, &q->qname, q->qnamehash, q->InterfaceID, 0); } } @@ -4107,8 +4114,9 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco DNSQuestion *const q = m->CurrentQuestion; 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)); + verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s (%s) TTL %d %s", + q->CurrentAnswers, AddRecord ? "Add" : "Rmv", MortalityDisplayString(rr->resrec.mortality), + rr->resrec.rroriginalttl, CRDisplayString(m, rr)); // When the response for the question was validated, the entire rrset was validated. If we deliver // a RMV for a single record in the rrset, we invalidate the response. If we deliver another add @@ -4149,7 +4157,11 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0)) return; } - + + // Set the record to immortal if appropriate + if (AddRecord == QC_add && Question_uDNS(q) && rr->resrec.RecordType != kDNSRecordTypePacketNegative && + q->allowExpired != AllowExpired_None && rr->resrec.mortality == Mortality_Mortal ) rr->resrec.mortality = Mortality_Immortal; // Update a non-expired cache record to immortal if appropriate + #if AWD_METRICS if ((AddRecord == QC_add) && Question_uDNS(q) && !followcname) { @@ -4170,7 +4182,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco responseLatencyMs = 0; } - MetricsUpdateDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, responseLatencyMs, isForCellular); + MetricsUpdateDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, q->metrics.expiredAnswerState, responseLatencyMs, isForCellular); q->metrics.answered = mDNStrue; } if (q->metrics.querySendCount > 0) @@ -4183,8 +4195,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco // 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. - rr->LastUsed = m->timenow; - if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q) + if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q && rr->resrec.mortality != Mortality_Ghost) { if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d", @@ -4293,14 +4304,21 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco return; } - // Note: Proceed with caution here because client callback function is allowed to do anything, - // including starting/stopping queries, registering/deregistering records, etc. - // - // 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 ((m->CurrentQuestion == q) && followcname && !ValidatingQuestion(q)) - AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); + if ((m->CurrentQuestion == q) && !ValidatingQuestion(q)) + { + // 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 (followcname) AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); + + // If we are returning expired RRs, then remember the first expired qname we we can start the query again + if (rr->resrec.mortality == Mortality_Ghost && !q->firstExpiredQname.c[0] && (q->allowExpired == AllowExpired_AllowExpiredAnswers) && rr->resrec.RecordType != kDNSRecordTypePacketNegative) + { + debugf("AnswerCurrentQuestionWithResourceRecord: Keeping track of domain for expired RR %s for question %p", CRDisplayString(m,rr), q); + // Note: question->qname is already changed at this point if following a CNAME + AssignDomainName(&q->firstExpiredQname, rr->resrec.name); // Update firstExpiredQname + } + } } mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) @@ -4474,7 +4492,8 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) // response. A cache may be present that answers this question e.g., cache entry generated // before the question became suppressed. We need to skip the suppressed questions here as // the RMV event has already been generated. - if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q) && + (q->allowExpired == AllowExpired_None || rr->resrec.mortality == Mortality_Mortal)) { verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr)); q->FlappingInterface1 = mDNSNULL; @@ -4503,11 +4522,11 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) } if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results { - if (q->CurrentAnswers == 0) + if ((q->CurrentAnswers == 0) && mDNSOpaque16IsZero(q->TargetQID)) { LogInfo("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype)); - ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0); + ReconfirmAntecedents(m, &q->qname, q->qnamehash, rr->resrec.InterfaceID, 0); } AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv); } @@ -4631,16 +4650,15 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou while (*rp) { CacheRecord *const rr = *rp; + mDNSBool recordReleased = mDNSfalse; mDNSs32 event = RRExpireTime(rr); 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 { DNSQuestion *q = rr->CRActiveQuestion; + verbosedebugf("CheckCacheExpiration: Removing%7d %7d %p %s", + m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); // When a cache record is about to expire, we expect to do four queries at 80-82%, 85-87%, 90-92% and // then 95-97% of the TTL. If the DNS server does not respond, then we will remove the cache entry // before we pick a new DNS server. As the question interval is set to MaxQuestionInterval, we may @@ -4657,9 +4675,30 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou CacheRecordRmv(m, rr); m->rrcache_active--; } - ReleaseCacheRecord(m, rr); + + event += MAX_GHOST_TIME; // Adjust so we can check for a ghost expiration + if (rr->resrec.mortality == Mortality_Mortal || // Normal expired mortal record that needs released + (rr->resrec.mortality == Mortality_Ghost && m->timenow - event >= 0)) // A ghost record that expired more than MAX_GHOST_TIME ago + { // Release as normal + *rp = rr->next; // Cut it from the list before ReleaseCacheRecord + verbosedebugf("CheckCacheExpiration: Deleting (%s)%7d %7d %p %s", + MortalityDisplayString(rr->resrec.mortality), + m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); + ReleaseCacheRecord(m, rr); + recordReleased = mDNStrue; + } + else // An immortal record needs to become a ghost when it expires + { // Don't release this entry + if (rr->resrec.mortality == Mortality_Immortal) + { + rr->resrec.mortality = Mortality_Ghost; // Expired immortal records become ghosts + verbosedebugf("CheckCacheExpiration: NOT Deleting (%s)%7d %7d %p %s", + MortalityDisplayString(rr->resrec.mortality), + m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); + } + } } - else // else, not expired; see if we need to query + else // else, not expired; see if we need to query { // If waiting to delay delivery, do nothing until then if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0) @@ -4682,6 +4721,10 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou } } } + } + + if (!recordReleased) // Schedule if we did not release the record + { verbosedebugf("CheckCacheExpiration:%6d %5d %s", (event - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr)); if (m->rrcache_nextcheck[slot] - event > 0) @@ -4893,12 +4936,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) { // SecsSinceRcvd is whole number of elapsed seconds, rounded down mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond; - if (rr->resrec.rroriginalttl <= SecsSinceRcvd) - { - LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %s %d %d", - rr->resrec.rroriginalttl, SecsSinceRcvd, CRDisplayString(m, rr), m->timenow, rr->TimeRcvd); - continue; // Go to next one in loop - } + if (rr->resrec.rroriginalttl <= SecsSinceRcvd && q->allowExpired != AllowExpired_AllowExpiredAnswers) continue; // Go to next one in loop // If this record set is marked unique, then that means we can reasonably assume we have the whole set // -- we don't need to rush out on the network and query immediately to see if there are more answers out there @@ -4908,6 +4946,9 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; AnsweredFromCache = mDNStrue; +#if AWD_METRICS + if (q->metrics.expiredAnswerState == ExpiredAnswer_Allowed) q->metrics.expiredAnswerState = ExpiredAnswer_AnsweredWithExpired; +#endif AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } @@ -4930,6 +4971,21 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } + if (q->allowExpired == AllowExpired_AllowExpiredAnswers) + { + q->allowExpired = AllowExpired_MakeAnswersImmortal; // After looking through the cache for an answer, demote to make immortal + if (q->firstExpiredQname.c[0]) // If an original query name was saved on an expired answer, start it over in case it is updated + { + LogMsg("AnswerNewQuestion: Restarting original question %p firstExpiredQname %##s for allowExpiredAnswers question", q, &q->firstExpiredQname.c); + mDNS_StopQuery_internal(m, q); // Stop old query + AssignDomainName(&q->qname, &q->firstExpiredQname); // Update qname + q->qnamehash = DomainNameHashValue(&q->qname); // and namehash + mDNS_StartQuery_internal(m, q); // start new query + q->CNAMEReferrals = 0; // Reset referral count + q->firstExpiredQname.c[0] = 0; // Erase the domain name + } + } + // Note: When a query gets suppressed or retried with search domains, we de-activate the question. // Hence we don't execute the following block of code for those cases. if (ShouldQueryImmediately && ActiveQuestion(q)) @@ -7467,6 +7523,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con AuthRecord **nrp = &ResponseRecords; #if POOF_ENABLED + mDNSBool notD2D = !mDNSPlatformInterfaceIsD2D(InterfaceID); // We don't run the POOF algorithm on D2D interfaces. CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated CacheRecord **eap = &ExpectedAnswers; #endif // POOF_ENABLED @@ -7626,18 +7683,21 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con if (QuestionNeedsMulticastResponse && !(query->h.flags.b[0] & kDNSFlag0_TC)) { #if POOF_ENABLED - CacheGroup *cg = CacheGroupForName(m, pktq.qnamehash, &pktq.qname); - CacheRecord *cr; - - // Make a list indicating which of our own cache records we expect to see updated as a result of this query - // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated - for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) - if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit) - if (!cr->NextInKAList && eap != &cr->NextInKAList) - { - *eap = cr; - eap = &cr->NextInKAList; - } + if (notD2D) + { + CacheGroup *cg = CacheGroupForName(m, pktq.qnamehash, &pktq.qname); + CacheRecord *cr; + + // Make a list indicating which of our own cache records we expect to see updated as a result of this query + // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit) + if (!cr->NextInKAList && eap != &cr->NextInKAList) + { + *eap = cr; + eap = &cr->NextInKAList; + } + } #endif // POOF_ENABLED // Check if this question is the same as any of mine. @@ -7728,15 +7788,18 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec); #if POOF_ENABLED - // Having built our ExpectedAnswers list from the questions in this packet, we then remove - // any records that are suppressed by the Known Answer list in this packet. - eap = &ExpectedAnswers; - while (*eap) + if (notD2D) { - CacheRecord *cr = *eap; - if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec)) - { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; } - else eap = &cr->NextInKAList; + // Having built our ExpectedAnswers list from the questions in this packet, we then remove + // any records that are suppressed by the Known Answer list in this packet. + eap = &ExpectedAnswers; + while (*eap) + { + CacheRecord *cr = *eap; + if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec)) + { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; } + else eap = &cr->NextInKAList; + } } #endif // POOF_ENABLED @@ -7900,7 +7963,7 @@ exit: } #if POOF_ENABLED - while (ExpectedAnswers) + while (ExpectedAnswers && notD2D) { CacheRecord *cr = ExpectedAnswers; ExpectedAnswers = cr->NextInKAList; @@ -8106,10 +8169,11 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C if (!rr) NoCacheAnswer(m, &m->rec.r); else { - RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer - *rr = m->rec.r; // Block copy the CacheRecord object - rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment - rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer + *rr = m->rec.r; // Block copy the CacheRecord object + rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment + rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header + rr->resrec.mortality = Mortality_Mortal; // We need to add the anonymous info before we call CacheRecordAdd so that // if it finds a matching question with this record, it bumps up the counters like @@ -8176,6 +8240,7 @@ mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl) rr->TimeRcvd = m->timenow; rr->resrec.rroriginalttl = ttl; rr->UnansweredQueries = 0; + if (rr->resrec.mortality != Mortality_Mortal) rr->resrec.mortality = Mortality_Immortal; SetNextCacheCheckTimeForRecord(m, rr); } @@ -8246,7 +8311,7 @@ mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist if (target && cr->resrec.rdatahash == rr->namehash && SameDomainName(target, rr->name)) { - LogInfo("IsResponseAcceptable: Found a matching entry for %##s in the CacheFlushRecords %s", rr->name->c, CRDisplayString(m, cr)); + LogDebug("IsResponseAcceptable: Found a matching entry for %##s in the CacheFlushRecords %s", rr->name->c, CRDisplayString(m, cr)); return (mDNStrue); } } @@ -8744,6 +8809,12 @@ mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage DNSQuestion *q; m->mDNSStats.CacheRefreshed++; + + if (rr->resrec.mortality == Mortality_Ghost && unicastQuestion && (unicastQuestion->allowExpired != AllowExpired_AllowExpiredAnswers) && !rr->DelayDelivery) + { + rr->DelayDelivery = NonZeroTime(m->timenow); + debugf("mDNSCoreReceiveCacheCheck: Reset DelayDelivery for mortalityExpired EXP:%d RR %s", m->timenow - RRExpireTime(rr), CRDisplayString(m, rr)); + } if (rr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, rr)); RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); @@ -9513,6 +9584,12 @@ exit: r1->resrec.rrtype == r2->resrec.rrtype && r1->resrec.rrclass == r2->resrec.rrclass) { + if (r1->resrec.mortality == Mortality_Mortal && r2->resrec.mortality != Mortality_Mortal) + { + verbosedebugf("mDNSCoreReceiveResponse: R1(%p) is being immortalized by R2(%p)", r1, r2); + r1->resrec.mortality = Mortality_Immortal; // Immortalize the replacement record + } + // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics) // else, if record is old, mark it to be flushed if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond) @@ -9588,7 +9665,23 @@ exit: } else { +#if AWD_METRICS + if (r2->resrec.mortality == Mortality_Ghost) + { + DNSQuestion * q; + for (q = m->Questions; q; q=q->next) + { + if (!q->LongLived && ActiveQuestion(q) && + ResourceRecordAnswersQuestion(&r2->resrec, q) && + q->metrics.expiredAnswerState == ExpiredAnswer_AnsweredWithExpired) + { + q->metrics.expiredAnswerState = ExpiredAnswer_ExpiredAnswerChanged; + } + } + } +#endif // Old uDNS records are scheduled to be purged instead of given at most one second to live. + r2->resrec.mortality = Mortality_Mortal; // We want it purged, so remove any immortality mDNS_PurgeCacheResourceRecord(m, r2); purgedRecords = mDNStrue; } @@ -10207,7 +10300,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return; if (mDNS_PacketLoggingEnabled) - DumpPacket(m, mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + DumpPacket(mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space); if (ptr) @@ -10466,7 +10559,6 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, cr->TimeRcvd = m->timenow; cr->DelayDelivery = 0; cr->NextRequiredQuery = m->timenow; - cr->LastUsed = m->timenow; cr->CRActiveQuestion = mDNSNULL; cr->UnansweredQueries = 0; cr->LastUnansweredTime = 0; @@ -10561,7 +10653,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS { ifid = mDNSInterface_Any; if (mDNS_PacketLoggingEnabled) - DumpPacket(m, mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); + DumpPacket(mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end); uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport); // Note: mDNSCore also needs to get access to received unicast responses } @@ -11126,11 +11218,11 @@ mDNSlocal DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInter curmatch = GetBestServer(m, name, InterfaceID, ServiceID, allValid, mDNSNULL, mDNStrue); if (curmatch != mDNSNULL) - LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr, + LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) for %##s", &curmatch->addr, mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", InterfaceID, name); else - LogInfo("GetServerForName: no DNS server (Scope %s:%p) found for name %##s", ifname ? ifname : "None", InterfaceID, name); + LogInfo("GetServerForName: no DNS server (Scope %s:%p) for %##s", ifname ? ifname : "None", InterfaceID, name); return(curmatch); } @@ -11159,14 +11251,14 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) if (curmatch != mDNSNULL) { - LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) found for name %##s (%s)", + LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) for %##s (%s)", question, curmatch, &curmatch->addr, mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype)); } else { - LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) found for name %##s (%s)", + LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) for %##s (%s)", question, ifname ? ifname : "None", InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype)); } @@ -11218,14 +11310,14 @@ mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNS // Some callers don't check for the qtype if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA) { - LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); + LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); return mDNSfalse; } // Private domains are exempted irrespective of what the DNSServer says if (IsPrivateDomain(m, q)) { - LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype)); + LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype)); return mDNSfalse; } @@ -11238,20 +11330,20 @@ mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNS // Check if the DNS Configuration allows A/AAAA queries to be sent if ((q->qtype == kDNSType_A) && (d->req_A)) { - LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows A queries", q->qname.c, + LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows A queries", q->qname.c, DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port)); return mDNSfalse; } if ((q->qtype == kDNSType_AAAA) && (d->req_AAAA)) { - LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows AAAA queries", q->qname.c, + LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows AAAA queries", q->qname.c, DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port)); return mDNSfalse; } #if USE_DNS64 if (DNS64IsQueryingARecord(q->dns64.state)) { - LogInfo("ShouldSuppressUnicastQuery: DNS64 query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype)); + LogDebug("ShouldSuppressUnicastQuery: DNS64 query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype)); return mDNSfalse; } #endif @@ -11652,6 +11744,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) question->StopTime = (question->TimeoutQuestion) ? question->StopTime : 0; #if AWD_METRICS mDNSPlatformMemZero(&question->metrics, sizeof(question->metrics)); + question->metrics.expiredAnswerState = (question->allowExpired != AllowExpired_None) ? ExpiredAnswer_Allowed : ExpiredAnswer_None; #endif // Need not initialize the DNS Configuration for Local Only OR P2P Questions when timeout not specified @@ -11672,7 +11765,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) } question->qDNSServer = GetServerForQuestion(m, question); - LogInfo("InitDNSConfig: question %p %##s (%s) Timeout %d, DNS Server %#a:%d", + LogDebug("InitDNSConfig: question %p %##s (%s) Timeout %d, DNS Server %#a:%d", question, question->qname.c, DNSTypeName(question->qtype), timeout, question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); @@ -12113,7 +12206,7 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname; isForCell = (question->qDNSServer && question->qDNSServer->cellIntf); durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; - MetricsUpdateDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, durationMs, isForCell); + MetricsUpdateDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, question->metrics.expiredAnswerState, durationMs, isForCell); } #endif // Take care to cut question from list *before* calling UpdateQuestionDuplicates @@ -12333,7 +12426,7 @@ mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const cr) mStatus status; mDNS_Lock(m); status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); + if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, cr->resrec.InterfaceID, 0); mDNS_Unlock(m); return(status); } @@ -12346,7 +12439,7 @@ mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr cr = FindIdenticalRecordInCache(m, rr); debugf("mDNS_ReconfirmByValue: %p %s", cr, RRDisplayString(m, rr)); if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); - if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0); + if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, cr->resrec.InterfaceID, 0); mDNS_Unlock(m); return(status); } @@ -13152,6 +13245,7 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se } else { + rr->resrec.mortality = Mortality_Mortal; mDNS_PurgeCacheResourceRecord(m, rr); } } @@ -13349,7 +13443,7 @@ mDNSexport mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType) // If the optional target host parameter is set, then the storage it points to must remain valid for the lifetime of the service registration mDNSexport 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, + const domainname *const host, mDNSIPPort port, RData *const txtrdata, const mDNSu8 txtinfo[], mDNSu16 txtlen, AuthRecord *SubTypes, mDNSu32 NumSubTypes, mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags) { @@ -13386,7 +13480,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, hostTTL = kHostNameTTL; mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr); - mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, recordType, artype, ServiceCallback, sr); + mDNS_SetupResourceRecord(&sr->RR_TXT, txtrdata, InterfaceID, kDNSType_TXT, kStandardTTL, recordType, artype, ServiceCallback, sr); // If port number is zero, that means the client is really trying to do a RegisterNoSuchService if (mDNSIPPortIsZero(port)) @@ -13596,7 +13690,9 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS else debugf("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c); err = mDNS_RegisterService(m, sr, newname, &type, &domain, - host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, + host, sr->RR_SRV.resrec.rdata->u.srv.port, + (sr->RR_TXT.resrec.rdata != &sr->RR_TXT.rdatastorage) ? sr->RR_TXT.resrec.rdata : mDNSNULL, + sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength, sr->SubTypes, sr->NumSubTypes, sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, sr->flags); @@ -14235,6 +14331,7 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s mDNS_RegisterService(m, srs, &name, &SleepProxyServiceType, &localdomain, mDNSNULL, m->SPSSocket->port, // Host, port + mDNSNULL, (mDNSu8 *)"", 1, // TXT data, length mDNSNULL, 0, // Subtypes (none) mDNSInterface_Any, // Interface ID @@ -14953,6 +15050,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) { LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s, New DNS server %#a , Old DNS server %#a", CRDisplayString(m, cr), &ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL)); + cr->resrec.mortality = Mortality_Mortal; mDNS_PurgeCacheResourceRecord(m, cr); } else @@ -15021,6 +15119,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) cr->resrec.rDNSServer = mDNSNULL; } + cr->resrec.mortality = Mortality_Mortal; PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue); } } diff --git a/mDNSResponder/mDNSCore/mDNSDebug.h b/mDNSResponder/mDNSCore/mDNSDebug.h index 68a696ef..d690fd2b 100755 --- a/mDNSResponder/mDNSCore/mDNSDebug.h +++ b/mDNSResponder/mDNSCore/mDNSDebug.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2015 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,12 +99,14 @@ extern "C" { #define LogOperation(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__);} while (0) #define LogSPS(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__);} while (0) #define LogInfo(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__);} while (0) + #define LogDebug(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_DEBUG, __VA_ARGS__);} while (0) #elif (MDNS_GNU_VA_ARGS) #define debug_noop( ARGS... ) ((void)0) #define LogMsg( ARGS... ) LogMsgWithLevel(MDNS_LOG_MSG, ARGS) #define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS);} while (0) #define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS);} while (0) #define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS);} while (0) + #define LogDebug( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_DEBUG, ARGS);} while (0) #else #error Unknown variadic macros #endif @@ -116,10 +118,12 @@ extern "C" { #define LogOperation (mDNS_LoggingEnabled == 0) ? ((void)0) : LogOperation_ #define LogSPS (mDNS_LoggingEnabled == 0) ? ((void)0) : LogSPS_ #define LogInfo (mDNS_LoggingEnabled == 0) ? ((void)0) : LogInfo_ + #define LogDebug (mDNS_LoggingEnabled == 0) ? ((void)0) : LogDebug_ extern void LogMsg_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); extern void LogSPS_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); +extern void LogDebug_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2); #endif #if MDNS_DEBUGMSGS diff --git a/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h b/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h index 1962bb19..511aa3b6 100755 --- a/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2017 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,6 +101,10 @@ extern "C" { #define MaximumRDSize 264 #endif +#if !defined(MDNSRESPONDER_BTMM_SUPPORT) +#define MDNSRESPONDER_BTMM_SUPPORT 0 +#endif + // *************************************************************************** // Function scope indicators @@ -1335,6 +1339,13 @@ typedef struct McastResolver mDNSu32 timeout; // timeout value for questions } McastResolver; +enum { + Mortality_Mortal = 0, // This cache record can expire and get purged + Mortality_Immortal = 1, // Allow this record to remain in the cache indefinitely + Mortality_Ghost = 2 // An immortal record that has expired and can linger in the cache +}; +typedef mDNSu8 MortalityState; + // scoped values for DNSServer matching enum { @@ -1386,6 +1397,7 @@ typedef struct struct ResourceRecord_struct { mDNSu8 RecordType; // See kDNSRecordTypes enum. + MortalityState mortality; // Mortality of this resource record (See MortalityState enum) mDNSu16 rrtype; // See DNS_TypeValues enum. mDNSu16 rrclass; // See DNS_ClassValues enum. mDNSu32 rroriginalttl; // In seconds @@ -1399,7 +1411,6 @@ struct ResourceRecord_struct // ReconfirmAntecedents(), etc., use rdatahash as a pre-flight check to see // whether it's worth doing a full SameDomainName() call. If the rdatahash // is not a correct case-insensitive name hash, they'll get false negatives. - // Grouping pointers together at the end of the structure improves the memory layout efficiency mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface // For records received off the wire, InterfaceID is *always* set to the receiving interface @@ -1638,7 +1649,7 @@ struct CacheRecord_struct mDNSs32 TimeRcvd; // In platform time units mDNSs32 DelayDelivery; // Set if we want to defer delivery of this answer to local clients mDNSs32 NextRequiredQuery; // In platform time units - mDNSs32 LastUsed; // In platform time units + // Extra four bytes here (on 64bit) DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer. Can never point to a NewQuestion. mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries mDNSu8 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer @@ -1812,7 +1823,12 @@ typedef enum { DNSPUSH_ESTABLISHED = 4 } DNSPush_State; - +enum { + AllowExpired_None = 0, // Don't allow expired answers or mark answers immortal (behave normally) + AllowExpired_MakeAnswersImmortal = 1, // Any answers to this question get marked as immortal + AllowExpired_AllowExpiredAnswers = 2 // Allow already expired answers from the cache +}; +typedef mDNSu8 AllowExpiredState; #define HMAC_LEN 64 #define HMAC_IPAD 0x36 @@ -1899,13 +1915,26 @@ typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, #define AWD_METRICS (USE_AWD && TARGET_OS_IOS) #if AWD_METRICS -typedef struct + +enum { - 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? + ExpiredAnswer_None = 0, // No expired answers used + ExpiredAnswer_Allowed = 1, // An expired answer is allowed by this request + ExpiredAnswer_AnsweredWithExpired = 2, // Question was answered with an expired answer + ExpiredAnswer_ExpiredAnswerChanged = 3, // Expired answer changed on refresh + + ExpiredAnswer_EnumCount +}; +typedef mDNSu8 ExpiredAnswerMetric; +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? + ExpiredAnswerMetric expiredAnswerState; // Expired answer state (see ExpiredAnswerMetric above) + } uDNSMetrics; #endif @@ -1977,6 +2006,7 @@ struct DNSQuestion_struct mDNSu16 noServerResponse; // At least one server did not respond. mDNSu16 triedAllServersOnce; // Tried all DNS servers once mDNSu8 unansweredQueries; // The number of unanswered queries to this server + AllowExpiredState allowExpired; // Allow expired answers state (see enum AllowExpired_None, etc. above) ZoneData *nta; // Used for getting zone data for private or LLQ query mDNSAddr servAddr; // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query @@ -2016,6 +2046,7 @@ struct DNSQuestion_struct mDNSIPPort TargetPort; // Must be set if Target is set mDNSOpaque16 TargetQID; // Must be set if Target is set domainname qname; + domainname firstExpiredQname; // first expired qname in request chain mDNSu16 qtype; mDNSu16 qclass; mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer. @@ -2778,7 +2809,7 @@ extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDN 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, + const domainname *const host, mDNSIPPort port, RData *txtrdata, const mDNSu8 txtinfo[], mDNSu16 txtlen, AuthRecord *SubTypes, mDNSu32 NumSubTypes, mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags); extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 flags); @@ -2939,6 +2970,7 @@ extern char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataB #define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer) #define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) #define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) +#define MortalityDisplayString(M) (M == Mortality_Mortal ? "mortal" : (M == Mortality_Immortal ? "immortal" : "ghost")) extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); extern mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr); // returns true for RFC1918 private addresses @@ -3601,17 +3633,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) <= 912) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 1168) ? 1 : -1]; - char sizecheck_ZoneData [(sizeof(ZoneData) <= 1744) ? 1 : -1]; + char sizecheck_ZoneData [(sizeof(ZoneData) <= 2000) ? 1 : -1]; char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 200) ? 1 : -1]; char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; char sizecheck_DNSServer [(sizeof(DNSServer) <= 330) ? 1 : -1]; - char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 7376) ? 1 : -1]; + char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 8400) ? 1 : -1]; char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5540) ? 1 : -1]; char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1]; #if APPLE_OSX_mDNSResponder - char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1256) ? 1 : -1]; + char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1512) ? 1 : -1]; #endif }; diff --git a/mDNSResponder/mDNSCore/uDNS.c b/mDNSResponder/mDNSCore/uDNS.c index 64dae891..cd91f4da 100755 --- a/mDNSResponder/mDNSCore/uDNS.c +++ b/mDNSResponder/mDNSCore/uDNS.c @@ -5807,7 +5807,7 @@ struct CompileTimeAssertionChecks_uDNS // 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_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1]; - char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; + char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 6136) ? 1 : -1]; }; #if COMPILER_LIKES_PRAGMA_MARK diff --git a/mDNSResponder/mDNSMacOSX/D2D.c b/mDNSResponder/mDNSMacOSX/D2D.c index 2848cdab..6ad8e945 100644 --- a/mDNSResponder/mDNSMacOSX/D2D.c +++ b/mDNSResponder/mDNSMacOSX/D2D.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2016 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,7 +97,6 @@ static mDNSu8 *const compression_limit = (mDNSu8 *) &compression_base_msg + size static mDNSu8 *const compression_lhs = (mDNSu8 *const) compression_base_msg.data + 27; mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result); -mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len); typedef struct D2DRecordListElem { @@ -167,44 +166,15 @@ mDNSlocal mDNSu8 * DNSNameCompressionBuildRHS(mDNSu8 *start, const ResourceRecor return putRData(&compression_base_msg, start, compression_limit, resourceRecord); } -#define PRINT_DEBUG_BYTES_LIMIT 64 // set limit on number of record bytes printed for debugging - -mDNSlocal void PrintHex(mDNSu8 *data, mDNSu16 len) +mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len) { - mDNSu8 *end; - char buffer[49] = {0}; - char *bufend = buffer + sizeof(buffer); - - if (len > PRINT_DEBUG_BYTES_LIMIT) - { - LogInfo(" (limiting debug output to %d bytes)", PRINT_DEBUG_BYTES_LIMIT); - len = PRINT_DEBUG_BYTES_LIMIT; - } - end = data + len; - - while(data < end) + if (mDNS_LoggingEnabled) { - char *ptr = buffer; - for(; data < end && ptr < bufend-1; ptr+=3,data++) - mDNS_snprintf(ptr, bufend - ptr, "%02X ", *data); - LogInfo(" %s", buffer); + LogDebug("%s: LHS: (%d bytes) %.*H", tag, lhs_len, lhs_len, lhs); + if (rhs) LogDebug("%s: RHS: (%d bytes) %.*H", tag, rhs_len, rhs_len, rhs); } } -mDNSlocal void PrintHelper(const char *const tag, mDNSu8 *lhs, mDNSu16 lhs_len, mDNSu8 *rhs, mDNSu16 rhs_len) -{ - if (!mDNS_LoggingEnabled) return; - - LogInfo("%s:", tag); - LogInfo(" LHS: (%d bytes)", lhs_len); - PrintHex(lhs, lhs_len); - - if (!rhs) return; - - LogInfo(" RHS: (%d bytes)", rhs_len); - PrintHex(rhs, rhs_len); -} - mDNSlocal void FreeD2DARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result) { (void)m; // unused @@ -333,9 +303,8 @@ mDNSlocal mStatus xD2DParse(const mDNSu8 * const lhs, const mDNSu16 lhs_len, con 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); + const int len = (int)(compression_lhs - (mDNSu8*)&compression_base_msg); + LogInfo("xD2DParse: Static Bytes: (%d bytes) %.*H", len, len, &compression_base_msg); } mDNSu8 *ptr = compression_lhs; // pointer to the end of our fake packet @@ -366,8 +335,8 @@ mDNSlocal mStatus xD2DParse(const mDNSu8 * const lhs, const mDNSu16 lhs_len, con if (mDNS_LoggingEnabled) { - LogInfo(" Our Bytes (%d bytes): ", ptr - compression_lhs); - PrintHex(compression_lhs, ptr - compression_lhs); + const int len = (int)(ptr - compression_lhs); + LogInfo("xD2DParse: Our Bytes (%d bytes): %.*H", len, len, compression_lhs); } ptr = (mDNSu8 *) GetLargeResourceRecord(m, &compression_base_msg, compression_lhs, ptr, mDNSInterface_Any, kDNSRecordTypePacketAns, &m->rec); diff --git a/mDNSResponder/mDNSMacOSX/DNS64.c b/mDNSResponder/mDNSMacOSX/DNS64.c index 1f4e0ec2..6fef38f8 100644 --- a/mDNSResponder/mDNSMacOSX/DNS64.c +++ b/mDNSResponder/mDNSMacOSX/DNS64.c @@ -22,7 +22,13 @@ #include "DNS64.h" #include -#include + +#if __has_include() + #include +#else + #include +#endif + #include #include diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c index dd670ab1..2b89bc24 100644 --- a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c +++ b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2012 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,14 +15,6 @@ * limitations under the License. */ -// Suppress "warning: 'DNSServiceDiscoveryMachPort' is deprecated" messages -- we already know this code is building the deprecated API -// Since we compile with all warnings treated as errors, we have to turn off the warnings here or the project won't compile -#include -#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED -#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED -#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 -#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3 - #include "../mDNSMacOSX/DNSServiceDiscovery.h" #include "DNSServiceDiscoveryDefines.h" diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h index 004d3259..3f683a83 100644 --- a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h +++ b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002, 2004, 2006, 2011 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,9 @@ #include #include +#include -#include +#define kDNSServiceDiscoveryDeprecatedMsg "This API was deprecated in Mac OS X 10.3 and replaced by the portable cross-platform /usr/include/dns_sd.h API" __BEGIN_DECLS @@ -90,7 +91,7 @@ typedef uint32_t DNSRecordReference; call to the specified callout function. @param replyMsg The Mach message. */ -void DNSServiceDiscovery_handleReply(void *replyMsg); +void DNSServiceDiscovery_handleReply(void *replyMsg) API_DEPRECATED(kDNSServiceDiscoveryDeprecatedMsg, macos(10.2, 10.3)); /***************************************************************************/ /* DNS Service Browser */ @@ -125,7 +126,7 @@ dns_service_discovery_ref DNSServiceBrowserCreate const char *domain, DNSServiceBrowserReply callBack, void *context -) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; +) API_DEPRECATED(kDNSServiceDiscoveryDeprecatedMsg, macos(10.2, 10.3)); /***************************************************************************/ /* Resolver requests */ @@ -158,7 +159,7 @@ dns_service_discovery_ref DNSServiceResolverResolve const char *domain, DNSServiceResolverReply callBack, void *context -) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; +) API_DEPRECATED(kDNSServiceDiscoveryDeprecatedMsg, macos(10.2, 10.3)); /***************************************************************************/ /* Mach port accessor and deallocation */ @@ -173,7 +174,7 @@ dns_service_discovery_ref DNSServiceResolverResolve specified or some other error occurred which prevented the resolution from being started. */ -mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; +mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDiscovery) API_DEPRECATED(kDNSServiceDiscoveryDeprecatedMsg, macos(10.2, 10.3)); /*! @function DNSServiceDiscoveryDeallocate @@ -181,7 +182,7 @@ mach_port_t DNSServiceDiscoveryMachPort(dns_service_discovery_ref dnsServiceDisc @param dnsServiceDiscovery A dns_service_discovery_ref as returned from a creation or enumeration call @result void */ -void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery) AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3; +void DNSServiceDiscoveryDeallocate(dns_service_discovery_ref dnsServiceDiscovery) API_DEPRECATED(kDNSServiceDiscoveryDeprecatedMsg, macos(10.2, 10.3)); __END_DECLS diff --git a/mDNSResponder/mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist b/mDNSResponder/mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist new file mode 100644 index 00000000..72352ceb --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist @@ -0,0 +1,14 @@ + + + + + DEFAULT-OPTIONS + + Level + + Persist + Info + + + + diff --git a/mDNSResponder/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist b/mDNSResponder/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist new file mode 100644 index 00000000..1eba185d --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist @@ -0,0 +1,16 @@ + + + + + DEFAULT-OPTIONS + + Level + + Persist + Inherit + Enable + Inherit + + + + diff --git a/mDNSResponder/mDNSMacOSX/Metrics.h b/mDNSResponder/mDNSMacOSX/Metrics.h index ff419fd8..e3772299 100644 --- a/mDNSResponder/mDNSMacOSX/Metrics.h +++ b/mDNSResponder/mDNSMacOSX/Metrics.h @@ -26,7 +26,7 @@ extern "C" { #if TARGET_OS_IOS mStatus MetricsInit(void); -void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, mDNSu32 inLatencyMs, mDNSBool inForCell); +void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, ExpiredAnswerMetric inExpiredAnswerState, mDNSu32 inLatencyMs, mDNSBool inForCell); void MetricsUpdateDNSResolveStats(const domainname *inQueryName, const ResourceRecord *inRR, mDNSBool inForCell); void MetricsUpdateDNSQuerySize(mDNSu32 inSize); void MetricsUpdateDNSResponseSize(mDNSu32 inSize); diff --git a/mDNSResponder/mDNSMacOSX/Metrics.m b/mDNSResponder/mDNSMacOSX/Metrics.m index e540f3be..dd20bc27 100644 --- a/mDNSResponder/mDNSMacOSX/Metrics.m +++ b/mDNSResponder/mDNSMacOSX/Metrics.m @@ -93,10 +93,11 @@ SOFT_LINK_CLASS(WirelessDiagnostics, AWDMDNSResponderDNSMessageSizeStats) // Constants //=========================================================================================================================== -#define kQueryStatsMaxQuerySendCount 10 -#define kQueryStatsSendCountBinCount (kQueryStatsMaxQuerySendCount + 1) -#define kQueryStatsLatencyBinCount 55 -#define kResolveStatsMaxObjCount 2000 +#define kQueryStatsMaxQuerySendCount 10 +#define kQueryStatsSendCountBinCount (kQueryStatsMaxQuerySendCount + 1) +#define kQueryStatsLatencyBinCount 55 +#define kQueryStatsExpiredAnswerStateCount (ExpiredAnswer_EnumCount) +#define kResolveStatsMaxObjCount 2000 //=========================================================================================================================== // Data structures @@ -152,6 +153,7 @@ typedef struct uint16_t responseLatencyBins[kQueryStatsLatencyBinCount]; uint16_t negAnsweredQuerySendCountBins[kQueryStatsSendCountBinCount]; uint16_t negResponseLatencyBins[kQueryStatsLatencyBinCount]; + uint16_t expiredAnswerStateBins[kQueryStatsExpiredAnswerStateCount]; } DNSHist; @@ -159,6 +161,7 @@ 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)); +check_compile_time(countof_field(DNSHist, expiredAnswerStateBins) == (kQueryStatsExpiredAnswerStateCount)); // 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. @@ -344,7 +347,7 @@ check_compile_time(sizeof(DNSMessageSizeStats) <= 132); mDNSlocal mStatus QueryStatsCreate(const char *inDomainStr, const char *inAltDomainStr, QueryNameTest_f inTest, mDNSBool inTerminal, QueryStats **outStats); mDNSlocal void QueryStatsFree(QueryStats *inStats); mDNSlocal void QueryStatsFreeList(QueryStats *inList); -mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs, mDNSBool inForCell); +mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, mDNSu32 inLatencyMs, mDNSBool inForCell); mDNSlocal const char * QueryStatsGetDomainString(const QueryStats *inStats); mDNSlocal mDNSBool QueryStatsDomainTest(const QueryStats *inStats, const domainname *inQueryName); mDNSlocal mDNSBool QueryStatsHostnameTest(const QueryStats *inStats, const domainname *inQueryName); @@ -492,7 +495,7 @@ mStatus MetricsInit(void) // MetricsUpdateDNSQueryStats //=========================================================================================================================== -mDNSexport void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, mDNSu32 inLatencyMs, mDNSBool inForCell) +mDNSexport void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu16 inType, const ResourceRecord *inRR, mDNSu32 inSendCount, ExpiredAnswerMetric inExpiredAnswerState, mDNSu32 inLatencyMs, mDNSBool inForCell) { QueryStats * stats; mDNSBool match; @@ -505,7 +508,7 @@ mDNSexport void MetricsUpdateDNSQueryStats(const domainname *inQueryName, mDNSu1 match = stats->test(stats, inQueryName); if (match) { - QueryStatsUpdate(stats, inType, inRR, inSendCount, inLatencyMs, inForCell); + QueryStatsUpdate(stats, inType, inRR, inSendCount, inExpiredAnswerState, inLatencyMs, inForCell); if (stats->terminal) break; } } @@ -839,7 +842,7 @@ mDNSlocal void QueryStatsFreeList(QueryStats *inList) // QueryStatsUpdate //=========================================================================================================================== -mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, mDNSu32 inLatencyMs, mDNSBool inForCell) +mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const ResourceRecord *inRR, mDNSu32 inQuerySendCount, ExpiredAnswerMetric inExpiredAnswerState, mDNSu32 inLatencyMs, mDNSBool inForCell) { mStatus err; DNSHistSet * set; @@ -892,6 +895,7 @@ mDNSlocal mStatus QueryStatsUpdate(QueryStats *inStats, int inType, const Resour for (i = 0; (i < (int)countof(kResponseLatencyMsLimits)) && (inLatencyMs >= kResponseLatencyMsLimits[i]); ++i) {} increment_saturate(hist->unansweredQueryDurationBins[i], UINT16_MAX); } + increment_saturate(hist->expiredAnswerStateBins[Min(inExpiredAnswerState, (kQueryStatsExpiredAnswerStateCount-1))], UINT16_MAX); err = mStatus_NoError; exit: @@ -2061,6 +2065,7 @@ mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, size_t binCount; uint32_t sendCountBins[kQueryStatsSendCountBinCount]; uint32_t latencyBins[kQueryStatsLatencyBinCount]; + uint32_t expiredAnswerBins[kQueryStatsExpiredAnswerStateCount]; awdStats = [[AWDDNSDomainStatsSoft alloc] init]; require_action_quiet(awdStats, exit, err = mStatus_UnknownErr); @@ -2107,6 +2112,11 @@ mDNSlocal mStatus CreateAWDDNSDomainStats(DNSHist *inHist, const char *inDomain, binCount = CopyHistogramBins(latencyBins, inHist->unansweredQueryDurationBins, kQueryStatsLatencyBinCount); [awdStats setUnansweredQueryDurationMs:latencyBins count:(NSUInteger)binCount]; } + + // Expired answers states + + binCount = CopyHistogramBins(expiredAnswerBins, inHist->expiredAnswerStateBins, kQueryStatsExpiredAnswerStateCount); + [awdStats setExpiredAnswerStates:expiredAnswerBins count:(NSUInteger)binCount]; *outStats = awdStats; awdStats = nil; @@ -2166,6 +2176,9 @@ mDNSlocal void LogDNSHist(const DNSHist *inHist, const char *inDomain, mDNSBool LogMsgNoIdent("Answered questions %4u", totalAnswered); LogMsgNoIdent("Negatively answered questions %4u", totalNegAnswered); LogMsgNoIdent("Unanswered questions %4u", totalUnanswered); + LogMsgNoIdent("Expired - no cached answer %4u", inHist->expiredAnswerStateBins[ExpiredAnswer_Allowed]); + LogMsgNoIdent("Expired - answered from cache %4u", inHist->expiredAnswerStateBins[ExpiredAnswer_AnsweredWithExpired]); + LogMsgNoIdent("Expired - cache changed %4u", inHist->expiredAnswerStateBins[ExpiredAnswer_ExpiredAnswerChanged]); LogMsgNoIdent("-- Query send counts ---------"); LogDNSHistSendCounts(inHist->answeredQuerySendCountBins); LogMsgNoIdent("-- Query send counts (NAQs) --"); diff --git a/mDNSResponder/mDNSMacOSX/Private/com.apple.mDNSResponder.plist b/mDNSResponder/mDNSMacOSX/Private/com.apple.mDNSResponder.plist deleted file mode 100644 index 6d403b52..00000000 --- a/mDNSResponder/mDNSMacOSX/Private/com.apple.mDNSResponder.plist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - DEFAULT-OPTIONS - - Default-Privacy-Setting - Public - Level - - Persist - Inherit - Enable - Inherit - - - - - diff --git a/mDNSResponder/mDNSMacOSX/Scripts/bonjour-mcast-diagnose b/mDNSResponder/mDNSMacOSX/Scripts/bonjour-mcast-diagnose new file mode 100755 index 00000000..bda53ba8 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Scripts/bonjour-mcast-diagnose @@ -0,0 +1,380 @@ +#! /bin/bash +# +# Copyright (c) 2017-2018 Apple Inc. All rights reserved. +# +# This script is currently for Apple Internal use only. +# + +version=1.4 +script=${BASH_SOURCE[0]} +dnssdutil=${dnssdutil:-dnssdutil} + +#============================================================================================================================ +# PrintUsage +#============================================================================================================================ + +PrintUsage() +{ + echo "" + echo "Usage: $( basename "${script}" ) [options]" + echo "" + echo "Options:" + echo " -V Display version of this script and exit." + echo "" +} + +#============================================================================================================================ +# LogOut +#============================================================================================================================ + +LogOut() +{ + echo "$( date '+%Y-%m-%d %H:%M:%S%z' ): $*" +} + +#============================================================================================================================ +# LogMsg +#============================================================================================================================ + +LogMsg() +{ + echo "$*" + if [ -d "${workPath}" ]; then + LogOut "$*" >> "${workPath}/log.txt" + fi +} + +#============================================================================================================================ +# ErrQuit +#============================================================================================================================ + +ErrQuit() +{ + echo "error: $*" + exit 1 +} + +#============================================================================================================================ +# SignalHandler +#============================================================================================================================ + +SignalHandler() +{ + LogMsg "Exiting due to signal." + trap '' SIGINT SIGTERM + pkill -TERM -P $$ + wait + exit 2 +} + +#============================================================================================================================ +# ExitHandler +#============================================================================================================================ + +ExitHandler() +{ + if [ -d "${tempPath}" ]; then + rm -fr "${tempPath}" + fi +} + +#============================================================================================================================ +# RunNetStat +#============================================================================================================================ + +RunNetStat() +{ + LogMsg "Running netstat -g -n -s" + netstat -g -n -s &> "${workPath}/netstat-g-n-s.txt" +} + +#============================================================================================================================ +# StartPacketCapture +#============================================================================================================================ + +StartPacketCapture() +{ + LogMsg "Starting tcpdump." + tcpdump -n -w "${workPath}/tcpdump.pcapng" &> "${workPath}/tcpdump.txt" & + tcpdumpPID=$! +} + +#============================================================================================================================ +# SaveExistingPacketCaptures +#============================================================================================================================ + +SaveExistingPacketCaptures() +{ + LogMsg "Saving existing mDNS packet captures." + mkdir "${workPath}/pcaps" + for file in /tmp/mdns-tcpdump.pcapng*; do + [ -e "${file}" ] || continue + baseName=$( sed -E 's/^mdns-tcpdump.pcapng([0-9]+)$/mdns-tcpdump-\1.pcapng/' <<< "$( basename ${file} )" ) + gzip < ${file} > "${workPath}/pcaps/${baseName}.gz" + done +} + +#============================================================================================================================ +# StopPacketCapture +#============================================================================================================================ + +StopPacketCapture() +{ + LogMsg "Stopping tcpdump." + kill -TERM ${tcpdumpPID} +} + +#============================================================================================================================ +# RunInterfaceMulticastTests +#============================================================================================================================ + +RunInterfaceMulticastTests() +{ + local ifname="$1" + local allHostsV4=224.0.0.1 + local allHostsV6=ff02::1 + local mDNSV4=224.0.0.251 + local mDNSV6=ff02::fb + local serviceList=( $( "${dnssdutil}" queryrecord -i "${ifname}" -A -t ptr -n _services._dns-sd._udp.local -l 6 | sed -E -n 's/.*(_.*_(tcp|udp)\.local\.)$/\1/p' | sort -u ) ) + local log="${workPath}/mcast-test-log-${ifname}.txt" + + LogOut "List of services: ${serviceList[*]}" >> "${log}" + # Ping All Hosts IPv4 multicast address. + + local routeOutput=$( route -n get -ifscope ${ifname} "${allHostsV4}" 2> /dev/null ) + if [ -n "${routeOutput}" ]; then + LogOut "Pinging "${allHostsV4}" on interface ${ifname}." >> "${log}" + ping -t 5 -b ${ifname} "${allHostsV4}" &> "${workPath}/ping-all-hosts-${ifname}.txt" + else + LogOut "No route to "${allHostsV4}" on interface ${ifname}." >> "${log}" + fi + + # Ping mDNS IPv4 multicast address. + + routeOutput=$( route -n get -ifscope ${ifname} "${mDNSV4}" 2> /dev/null ) + if [ -n "${routeOutput}" ]; then + LogOut "Pinging "${mDNSV4}" on interface ${ifname}." >> "${log}" + ping -t 5 -b ${ifname} "${mDNSV4}" &> "${workPath}/ping-mDNS-${ifname}.txt" + else + LogOut "No route to "${mDNSV4}" on interface ${ifname}." >> "${log}" + fi + + # Ping All Hosts IPv6 multicast address. + + routeOutput=$( route -n get -ifscope ${ifname} -inet6 "${allHostsV6}" 2> /dev/null ) + if [ -n "${routeOutput}" ]; then + LogOut "Pinging "${allHostsV6}" on interface ${ifname}." >> "${log}" + ping6 -c 6 -I ${ifname} "${allHostsV6}" &> "${workPath}/ping6-all-hosts-${ifname}.txt" + else + LogOut "No route to "${allHostsV6}" on interface ${ifname}." >> "${log}" + fi + + # Ping mDNS IPv6 multicast address. + + routeOutput=$( route -n get -ifscope ${ifname} -inet6 "${mDNSV6}" 2> /dev/null ) + if [ -n "${routeOutput}" ]; then + LogOut "Pinging "${mDNSV6}" on interface ${ifname}." >> "${log}" + ping6 -c 6 -I ${ifname} "${mDNSV6}" &> "${workPath}/ping6-mDNS-${ifname}.txt" + else + LogOut "No route to "${mDNSV6}" on interface ${ifname}." >> "${log}" + fi + + # Send mDNS queries for services. + + for service in "${serviceList[@]}"; do + LogOut "Sending mDNS queries for "${service}" on interface ${ifname}." >> "${log}" + for(( i = 1; i <= 3; ++i )); do + printf "\n" + "${dnssdutil}" mdnsquery -i "${ifname}" -n "${service}" -t ptr -r 2 + printf "\n" + "${dnssdutil}" mdnsquery -i "${ifname}" -n "${service}" -t ptr -r 1 --QU -p 5353 + printf "\n" + done >> "${workPath}/mdnsquery-${ifname}.txt" 2>&1 + done +} + +#============================================================================================================================ +# RunMulticastTests +#============================================================================================================================ + +RunMulticastTests() +{ + local interfaces=( $( ifconfig -l -u ) ) + local skipPrefixes=( ap awdl bridge ipsec lo p2p pdp_ip pktap UDC utun ) + local ifname="" + local pid="" + local pids=() + + LogMsg "List of interfaces: ${interfaces[*]}" + for ifname in "${interfaces[@]}"; do + local skip=false + for prefix in ${skipPrefixes[@]}; do + if [[ ${ifname} =~ ^${prefix}[0-9]*$ ]]; then + skip=true + break + fi + done + + if [ "${skip}" != "true" ]; then + ifconfig ${ifname} | grep -q inet + if [ $? -ne 0 ]; then + skip=true + fi + fi + + if [ "${skip}" == "true" ]; then + continue + fi + + LogMsg "Starting interface multicast tests for ${ifname}." + RunInterfaceMulticastTests "${ifname}" & pids+=($!) + done + + LogMsg "Waiting for interface multicast tests to complete..." + for pid in "${pids[@]}"; do + wait "${pid}" + done + LogMsg "All interface multicast tests completed." +} + +#============================================================================================================================ +# RunBrowseTest +#============================================================================================================================ + +RunBrowseTest() +{ + LogMsg "Running dnssdutil browseAll command." + "${dnssdutil}" browseAll -A -d local -b 10 -c 10 &> "${workPath}/browseAll.txt" +} + +#============================================================================================================================ +# IsMacOS +#============================================================================================================================ + +IsMacOS() +{ + [[ $( sw_vers -productName ) =~ ^Mac\ OS ]] +} + +#============================================================================================================================ +# ArchiveLogs +#============================================================================================================================ + +ArchiveLogs() +{ + local workdir=$( basename "${workPath}" ) + local archivePath="${dstPath}/${workdir}.tar.gz" + + LogMsg "Archiving logs." + echo "---" + tar -C "${tempPath}" -czf "${archivePath}" "${workdir}" + if [ -e "${archivePath}" ]; then + echo "Created log archive at ${archivePath}" + echo "*** Please run sysdiagnose NOW. ***" + echo "Attach both the log archive and the sysdiagnose archive to the radar." + if IsMacOS; then + open "${dstPath}" + fi + else + echo "Failed to create archive at ${archivePath}." + fi + echo "---" +} + +#============================================================================================================================ +# CreateWorkDirName +#============================================================================================================================ + +CreateWorkDirName() +{ + local suffix="" + local productName=$( sw_vers -productName ) + if [ -n "${productName}" ]; then + suffix+="_${productName}" + fi + + local model="" + if IsMacOS; then + model=$( sysctl -n hw.model ) + model=${model//,/-} + else + model=$( gestalt_query -undecorated DeviceName ) + fi + if [ -n "${model}" ]; then + suffix+="_${model}" + fi + + local buildVersion=$( sw_vers -buildVersion ) + if [ -n "${buildVersion}" ]; then + suffix+="_${buildVersion}" + fi + + suffix=${suffix//[^A-Za-z0-9._-]/_} + + printf "bonjour-mcast-diags_$( date '+%Y.%m.%d_%H-%M-%S%z' )${suffix}" +} + +#============================================================================================================================ +# main +#============================================================================================================================ + +main() +{ + while getopts ":hV" option; do + case "${option}" in + h) + PrintUsage + exit 0 + ;; + V) + echo "$( basename "${script}" ) version ${version}" + exit 0 + ;; + :) + ErrQuit "option '${OPTARG}' requires an argument." + ;; + *) + ErrQuit "unknown option '${OPTARG}'." + ;; + esac + done + + [ "${OPTIND}" -gt "$#" ] || ErrQuit "unexpected argument \""${!OPTIND}"\"." + + if IsMacOS; then + if [ "${EUID}" -ne 0 ]; then + echo "Re-launching with sudo" + exec sudo ${script} + fi + dstPath=/var/tmp + else + [ "${EUID}" -eq 0 ] || ErrQuit "$( basename "${script}" ) needs to be run as root." + dstPath=/var/mobile/Library/Logs/CrashReporter + fi + + tempPath=$( mktemp -d -q ) || ErrQuit "Failed to make temp directory." + workPath="${tempPath}/$( CreateWorkDirName )" + mkdir "${workPath}" || ErrQuit "Failed to make work directory." + + trap SignalHandler SIGINT SIGTERM + trap ExitHandler EXIT + + LogMsg "About: $( basename "${script}" ) version ${version} ($( md5 -q ${script} ))." + if [ "${dnssdutil}" != "dnssdutil" ]; then + if [ -x "$( which "${dnssdutil}" )" ]; then + LogMsg "Using $( "${dnssdutil}" -V ) at $( which "${dnssdutil}" )." + else + LogMsg "WARNING: dnssdutil (${dnssdutil}) isn't an executable." + fi + fi + + RunNetStat + StartPacketCapture + SaveExistingPacketCaptures + RunBrowseTest + RunMulticastTests + StopPacketCapture + ArchiveLogs +} + +main "$@" diff --git a/mDNSResponder/mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump b/mDNSResponder/mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump new file mode 100755 index 00000000..2a81b5f3 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump @@ -0,0 +1,56 @@ +#! /bin/bash +# +# Copyright (c) 2018 Apple Inc. All rights reserved. +# +# This script is currently for Apple Internal use only. +# + +version=1.0 +script=${BASH_SOURCE[0]} + +#============================================================================================================================ +# PrintUsage +#============================================================================================================================ + +PrintUsage() +{ + echo "" + echo "Usage: $( basename "${script}" ) [options]" + echo "" + echo "Options:" + echo " -V Display version of this script and exit." + echo "" +} + +#============================================================================================================================ +# main +#============================================================================================================================ + +main() +{ + while getopts ":hV" option; do + case "${option}" in + h) + PrintUsage + exit 0 + ;; + V) + echo "$( basename "${script}" ) version ${version}" + exit 0 + ;; + :) + ErrQuit "option '${OPTARG}' requires an argument." + ;; + *) + ErrQuit "unknown option '${OPTARG}'." + ;; + esac + done + + [ "${OPTIND}" -gt "$#" ] || ErrQuit "unexpected argument \""${!OPTIND}"\"." + + launchctl load /Library/LaunchDaemons/com.apple.mDNSResponder.mdns-tcpdump.plist + launchctl start com.apple.mDNSResponder.mdns-tcpdump +} + +main "$@" diff --git a/mDNSResponder/mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist b/mDNSResponder/mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist new file mode 100644 index 00000000..0e48c217 --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist @@ -0,0 +1,21 @@ + + + + + Label + com.apple.mDNSResponder.mdns-tcpdump + ProgramArguments + + /usr/sbin/tcpdump + -w + /tmp/mdns-tcpdump.pcapng + -C + 10 + -W + 16 + ( udp port 5353 ) or arp or icmp or icmp6 + + KeepAlive + + + diff --git a/mDNSResponder/mDNSMacOSX/daemon.c b/mDNSResponder/mDNSMacOSX/daemon.c index fa192b6a..e16f1dbf 100644 --- a/mDNSResponder/mDNSMacOSX/daemon.c +++ b/mDNSResponder/mDNSMacOSX/daemon.c @@ -62,6 +62,7 @@ static os_log_t log_general = NULL; #define kPreferencesKey_DebugLogging CFSTR("DebugLogging") #define kPreferencesKey_UnicastPacketLogging CFSTR("UnicastPacketLogging") #define kPreferencesKey_AlwaysAppendSearchDomains CFSTR("AlwaysAppendSearchDomains") +#define kPreferencesKey_EnableAllowExpired CFSTR("EnableAllowExpired") #define kPreferencesKey_NoMulticastAdvertisements CFSTR("NoMulticastAdvertisements") #define kPreferencesKey_StrictUnicastOrdering CFSTR("StrictUnicastOrdering") #define kPreferencesKey_OfferSleepProxyService CFSTR("OfferSleepProxyService") @@ -84,13 +85,13 @@ static os_log_t log_general = NULL; static mDNS_PlatformSupport PlatformStorage; -// 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 * 232 = 32712 bytes. -// This fits in eight 4kB pages, with 56 bytes spare for memory block headers and similar overhead +// Start off with a default cache of 32K (136 records of 240 bytes each) +// Each time we grow the cache we add another 136 records +// 136 * 240 = 32640 bytes. +// This fits in eight 4kB pages, with 128 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]; }; +struct CompileTimeAssertionChecks_RR_CACHE_SIZE { char a[(RR_CACHE_SIZE >= 136) ? 1 : -1]; }; #define kRRCacheGrowSize (sizeof(CacheEntity) * RR_CACHE_SIZE) @@ -107,6 +108,7 @@ static mDNSBool NoMulticastAdvertisements = mDNSfalse; // By default, advertise extern mDNSBool StrictUnicastOrdering; extern mDNSBool AlwaysAppendSearchDomains; +extern mDNSBool EnableAllowExpired; #if ENABLE_BLE_TRIGGERED_BONJOUR extern mDNSBool EnableBLEBasedDiscovery; @@ -571,7 +573,7 @@ mDNSexport void mDNSPlatformLogToFile(int log_level, const char *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); + os_log_with_type(log_general, log_level, "%{private}s", buffer); } @@ -652,6 +654,7 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void mDNS_Lock(m); FORALL_CACHERECORDS(slot, cg, rr) { + rr->resrec.mortality = Mortality_Mortal; mDNS_PurgeCacheResourceRecord(m, rr); } // Restart unicast and multicast queries @@ -763,7 +766,8 @@ mDNSlocal void SignalDispatch(dispatch_source_t source) mDNS_Lock(m); FORALL_CACHERECORDS(slot, cg, rr) { - mDNS_PurgeCacheResourceRecord(m, rr); + rr->resrec.mortality = Mortality_Mortal; + mDNS_PurgeCacheResourceRecord(m, rr); } // Restart unicast and multicast queries mDNSCoreRestartQueries(m); @@ -1528,6 +1532,7 @@ mDNSexport int main(int argc, char **argv) UseInternalSleepProxy = (i+1attr[1].length >= sizeof(dnsprefix)-1 && 0 == strncasecmp(attributes->attr[1].data, dnsprefix, sizeof(dnsprefix)-1)) format = formatDnsPrefixedServiceItem; +#if MDNSRESPONDER_BTMM_SUPPORT else if (attributes->attr[1].length >= sizeof(btmmprefix)-1 && 0 == strncasecmp(attributes->attr[1].data, btmmprefix, sizeof(btmmprefix)-1)) format = formatBtmmPrefixedServiceItem; +#endif 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)) @@ -821,7 +830,9 @@ static CFPropertyListRef copyKeychainItemInfo(SecKeychainItemRef item, SecKeycha data = CFDataCreate(kCFAllocatorDefault, attributes->attr[1].data, attributes->attr[1].length); break; case formatDnsPrefixedServiceItem: +#if MDNSRESPONDER_BTMM_SUPPORT case formatBtmmPrefixedServiceItem: +#endif data = CFDataCreate(kCFAllocatorDefault, attributes->attr[1].data, attributes->attr[1].length); break; default: diff --git a/mDNSResponder/mDNSMacOSX/mDNSMacOSX.c b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.c index 3bb4ec69..f64e28af 100644 --- a/mDNSResponder/mDNSMacOSX/mDNSMacOSX.c +++ b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2016 Apple Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,22 +86,22 @@ #include #if TARGET_OS_IPHONE -// For WiFiManagerClientRef etc, declarations. -#include -#include +#include // For WiFiManagerClientRef etc, declarations. #include +#include // For os_variant_has_internal_diagnostics(). #endif // TARGET_OS_IPHONE // Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic. #include #include // for nwi_state -#if APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_BTMM_SUPPORT #include +#endif + +#if APPLE_OSX_mDNSResponder #include // for ne_session_set_socket_attributes() -#else -#define NO_AWACS 1 -#endif // APPLE_OSX_mDNSResponder +#endif #if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED #include @@ -153,9 +153,11 @@ static CFStringRef NetworkChangedKey_Computername; static CFStringRef NetworkChangedKey_DNS; static CFStringRef NetworkChangedKey_StateInterfacePrefix; static CFStringRef NetworkChangedKey_DynamicDNS = CFSTR("Setup:/Network/DynamicDNS"); +static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings"); +#if MDNSRESPONDER_BTMM_SUPPORT static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac"); static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity"); -static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings"); +#endif static char HINFO_HWstring_buffer[32]; static char *HINFO_HWstring = "Device"; @@ -794,8 +796,8 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, sendto_errno, strerror(sendto_errno), (mDNSu32)(m->timenow)); if (!mDNSAddressIsAllDNSLinkGroup(dst)) { - if (sendto_errno == EHOSTUNREACH) return(mStatus_HostUnreachErr); - if (sendto_errno == EHOSTDOWN || sendto_errno == ENETDOWN || sendto_errno == ENETUNREACH) return(mStatus_TransientErr); + if ((sendto_errno == EHOSTUNREACH) || (sendto_errno == ENETUNREACH)) return(mStatus_HostUnreachErr); + if ((sendto_errno == EHOSTDOWN) || (sendto_errno == ENETDOWN)) 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 ) @@ -3310,6 +3312,40 @@ mDNSlocal u_int64_t getExtendedFlags(char * ifa_name) return ifr.ifr_eflags; } +#if TARGET_OS_OSX +// IFRTYPE_FUNCTIONAL_INTCOPROC type interfaces on macOS do not support Bonjour discovery. +mDNSlocal mDNSBool isCoprocessorInterface(int sockFD, char * ifa_name) +{ + struct ifreq ifr; + + if (sockFD < 0) + { + LogMsg("isCoprocessorInterface: invalid socket FD passed: %d", sockFD); + return mDNSfalse; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + strlcpy(ifr.ifr_name, ifa_name, sizeof(ifr.ifr_name)); + + if (ioctl(sockFD, SIOCGIFFUNCTIONALTYPE, (caddr_t)&ifr) == -1) + { + LogMsg("isCoprocessorInterface: SIOCGIFFUNCTIONALTYPE failed, errno = %d (%s)", errno, strerror(errno)); + return mDNSfalse; + } + + if (ifr.ifr_functional_type == IFRTYPE_FUNCTIONAL_INTCOPROC) + { + LogMsg("isCoprocessorInterface: %s marked as coprocessor interface", ifa_name); + return mDNStrue; + } + else + return mDNSfalse; +} + +#else // TARGET_OS_OSX +#define isCoprocessorInterface(A, B) mDNSfalse +#endif // TARGET_OS_OSX + #if TARGET_OS_IPHONE // Function pointers for the routines we use in the MobileWiFi framework. @@ -3499,7 +3535,17 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(struct ifaddrs *ifa, mDNSs // we get the corresponding name for the interface index on which the packet was received and check against // the InterfaceList for a matching name. So, keep the name in sync strlcpy((*p)->ifinfo.ifname, ifa->ifa_name, sizeof((*p)->ifinfo.ifname)); - (*p)->Exists = mDNStrue; + + // Determine if multicast state has changed. + const mDNSBool txrx = MulticastInterface(*p); + if ((*p)->ifinfo.McastTxRx != txrx) + { + (*p)->ifinfo.McastTxRx = txrx; + (*p)->Exists = MulticastStateChanged; // State change; need to deregister and reregister this interface + } + else + (*p)->Exists = mDNStrue; + // If interface was not in getifaddrs list last time we looked, but it is now, update 'AppearanceTime' for this record if ((*p)->LastSeen != utc) (*p)->AppearanceTime = utc; @@ -3539,7 +3585,6 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(struct ifaddrs *ifa, mDNSs // We can be configured to disable multicast advertisement, but we want to to support // local-only services, which need a loopback address record. i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses; - i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse; i->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue; @@ -3566,6 +3611,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(struct ifaddrs *ifa, mDNSs LogInfo("AddInterfaceToList: D2DInterface set for %s", ifa->ifa_name); i->isExpensive = (eflags & IFEF_EXPENSIVE) ? mDNStrue: mDNSfalse; + i->isAWDL = (eflags & IFEF_AWDL) ? mDNStrue: mDNSfalse; if (eflags & IFEF_AWDL) { // Set SupportsUnicastMDNSResponse false for the AWDL interface since unicast reserves @@ -3590,6 +3636,8 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(struct ifaddrs *ifa, mDNSs i->BPF_len = 0; i->Registered = mDNSNULL; + // MulticastInterface() depends on the "m" and "ifa_flags" values being initialized above. + i->ifinfo.McastTxRx = MulticastInterface(i); // Do this AFTER i->BSSID has been set up i->ifinfo.NetWake = (eflags & IFEF_EXPENSIVE)? mDNSfalse : NetWakeInterface(i); GetMAC(&i->ifinfo.MAC, scope_id); @@ -4827,14 +4875,14 @@ mDNSlocal mStatus UpdateInterfaceList(mDNSs32 utc) mDNSPlatformMemCopy(m->PrimaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6); } - if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr) + if (ifa->ifa_flags & IFF_UP && ifa->ifa_addr && !isCoprocessorInterface(InfoSocket, ifa->ifa_name)) if (ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { if (!ifa->ifa_netmask) { mDNSAddr ip; SetupAddr(&ip, ifa->ifa_addr); - LogMsg("getifaddrs: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a", + LogMsg("UpdateInterfaceList: ifa_netmask is NULL for %5s(%d) Flags %04X Family %2d %#a", ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip); } // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be zero, so we don't complain about that @@ -4843,7 +4891,7 @@ mDNSlocal mStatus UpdateInterfaceList(mDNSs32 utc) { mDNSAddr ip; SetupAddr(&ip, ifa->ifa_addr); - LogMsg("getifaddrs ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d", + LogMsg("UpdateInterfaceList: ifa_netmask for %5s(%d) Flags %04X Family %2d %#a has different family: %d", ifa->ifa_name, if_nametoindex(ifa->ifa_name), ifa->ifa_flags, ifa->ifa_addr->sa_family, &ip, ifa->ifa_netmask->sa_family); } // Currently we use a few internal ones like mDNSInterfaceID_LocalOnly etc. that are negative values (0, -1, -2). @@ -4900,19 +4948,6 @@ mDNSlocal mStatus UpdateInterfaceList(mDNSs32 utc) if (!foundav4 && v4Loopback) AddInterfaceToList(v4Loopback, utc); if (!foundav6 && v6Loopback) AddInterfaceToList(v6Loopback, utc); - // Now the list is complete, set the McastTxRx setting for each interface. - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists) - { - mDNSBool txrx = MulticastInterface(i); - if (i->ifinfo.McastTxRx != txrx) - { - i->ifinfo.McastTxRx = txrx; - i->Exists = MulticastStateChanged; // State change; need to deregister and reregister this interface - } - } - if (InfoSocket >= 0) close(InfoSocket); @@ -5234,7 +5269,7 @@ mDNSlocal int ClearInactiveInterfaces(mDNSs32 utc) if (!i->Exists) { if (i->LastSeen == utc) i->LastSeen = utc - 1; - mDNSBool delete = (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0) && (utc - i->LastSeen >= 60); + const mDNSBool delete = (i->isAWDL || (NumCacheRecordsForInterfaceID(m, i->ifinfo.InterfaceID) == 0)) && (utc - i->LastSeen >= 60); LogInfo("ClearInactiveInterfaces: %-13s %5s(%lu) %.6a InterfaceID %p(%p) %#a/%d Age %d%s", delete ? "Deleting" : "Holding", i->ifinfo.ifname, i->scope_id, &i->BSSID, i->ifinfo.InterfaceID, i, &i->ifinfo.ip, CountMaskBits(&i->ifinfo.mask), utc - i->LastSeen, @@ -5818,6 +5853,7 @@ mDNSlocal void SetupDDNSDomains(domainname *const fqdn, DNameListElem **RegDomai } CFRelease(ddnsdict); } +#if MDNSRESPONDER_BTMM_SUPPORT if (RegDomains) { CFDictionaryRef btmm = SCDynamicStoreCopyValue(NULL, NetworkChangedKey_BackToMyMac); @@ -5847,7 +5883,7 @@ mDNSlocal void SetupDDNSDomains(domainname *const fqdn, DNameListElem **RegDomai CFRelease(btmm); } } - +#endif } // Returns mDNSfalse, if it does not set the configuration i.e., if the DNS configuration did not change @@ -6141,7 +6177,7 @@ mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const } } -#if APPLE_OSX_mDNSResponder +#if MDNSRESPONDER_BTMM_SUPPORT #if !NO_AWACS // checks whether a domain is present in Setup:/Network/BackToMyMac. Just because there is a key in the @@ -6283,7 +6319,7 @@ mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) else LogInfo("UpdateBTMMRelayConnection: Not calling AWS_Disconnect"); } } -#elif !TARGET_OS_EMBEDDED +#else mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) { (void) m; // Unused @@ -6291,11 +6327,9 @@ mDNSlocal void UpdateBTMMRelayConnection(mDNS *const m) } #endif // ! NO_AWACS -#if !TARGET_OS_EMBEDDED mDNSlocal void ProcessConndConfigChanges(void); -#endif -#endif // APPLE_OSX_mDNSResponder +#endif // MDNSRESPONDER_BTMM_SUPPORT // MUST be called holding the lock mDNSlocal void SetDomainSecrets_internal(mDNS *m) @@ -6369,11 +6403,13 @@ mDNSlocal void SetDomainSecrets_internal(mDNS *m) offset = 0; if (!strncmp(stringbuf, dnsprefix, strlen(dnsprefix))) offset = strlen(dnsprefix); +#if MDNSRESPONDER_BTMM_SUPPORT else if (!strncmp(stringbuf, btmmprefix, strlen(btmmprefix))) { AutoTunnel = mDNStrue; offset = strlen(btmmprefix); } +#endif domainname domain; if (!MakeDomainNameFromDNSNameString(&domain, stringbuf + offset)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; } @@ -6555,7 +6591,9 @@ mDNSlocal void SetDomainSecrets_internal(mDNS *m) } UpdateAnonymousRacoonConfig(m); // Determine whether we need racoon to accept incoming connections +#if MDNSRESPONDER_BTMM_SUPPORT ProcessConndConfigChanges(); // Update AutoTunnelInnerAddress values and default ipsec policies as necessary +#endif } #endif // APPLE_OSX_mDNSResponder @@ -7236,8 +7274,9 @@ mDNSexport void RemoveAutoTunnel6Record(mDNS *const m) if (info->AutoTunnel) UpdateAutoTunnel6Record(m, info); } +#endif /* APPLE_OSX_mDNSResponder */ -#if !TARGET_OS_EMBEDDED +#if MDNSRESPONDER_BTMM_SUPPORT mDNSlocal mDNSBool IPv6AddressIsOnInterface(mDNSv6Addr ipv6Addr, char *ifname) { struct ifaddrs *ifa; @@ -7474,8 +7513,7 @@ mDNSlocal void ProcessConndConfigChanges(void) // If awacsd crashes or exits for some reason, restart it UpdateBTMMRelayConnection(m); } -#endif // !TARGET_OS_EMBEDDED -#endif /* APPLE_OSX_mDNSResponder */ +#endif // MDNSRESPONDER_BTMM_SUPPORT mDNSlocal mDNSBool IsAppleNetwork(mDNS *const m) { @@ -7580,9 +7618,11 @@ mDNSexport void mDNSMacOSXNetworkChanged(void) #if APPLE_OSX_mDNSResponder #if !TARGET_OS_EMBEDDED +#if MDNSRESPONDER_BTMM_SUPPORT mDNS_Lock(m); ProcessConndConfigChanges(); mDNS_Unlock(m); +#endif // Scan to find client tunnels whose questions have completed, // but whose local inner/outer addresses have changed since the tunnel was set up @@ -7806,14 +7846,18 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v //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 + const int c = CFArrayGetCount(changedKeys); // Count changes CFRange range = { 0, c }; - 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); + const int c_host = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Hostnames ) != 0); + const int c_comp = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0); + const int c_udns = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0); + const int c_ddns = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); +#if MDNSRESPONDER_BTMM_SUPPORT + const int c_btmm = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac ) != 0); +#else + const int c_btmm = 0; +#endif + const int c_v4ll = ChangedKeysHaveIPv4LL(changedKeys); int c_fast = 0; // Do immediate network changed processing for "p2p*" interfaces and @@ -7971,9 +8015,11 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) CFArrayAppendValue(keys, NetworkChangedKey_Computername); CFArrayAppendValue(keys, NetworkChangedKey_DNS); CFArrayAppendValue(keys, NetworkChangedKey_DynamicDNS); - CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); +#if MDNSRESPONDER_BTMM_SUPPORT + CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity); +#endif CFArrayAppendValue(patterns, pattern1); CFArrayAppendValue(patterns, pattern2); CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); @@ -8177,13 +8223,9 @@ mDNSlocal void removeCachedPeerRecords(mDNSu32 ifindex, mDNSAddr *ap, bool purge // Handle KEV_DL_NODE_PRESENCE event. mDNSlocal void nodePresence(struct kev_dl_node_presence * p) { - char buf[INET6_ADDRSTRLEN]; struct opaque_presence_indication *op = (struct opaque_presence_indication *) p->node_service_info; - if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf))) - LogInfo("nodePresence: IPv6 address: %s, SUI %d", buf, op->SUI); - else - LogInfo("nodePresence: inet_ntop() error"); + LogInfo("nodePresence: IPv6 address: %.16a, SUI %d", p->sin6_node_address.sin6_addr.s6_addr, op->SUI); // AWDL will generate a KEV_DL_NODE_PRESENCE event with SSTH field of // all zeroes when a node is present and has no services registered. @@ -8203,17 +8245,11 @@ mDNSlocal void nodePresence(struct kev_dl_node_presence * p) mDNSlocal void nodeAbsence(struct kev_dl_node_absence * p) { mDNSAddr peerAddr; - char buf[INET6_ADDRSTRLEN]; - - if (inet_ntop(AF_INET6, & p->sin6_node_address.sin6_addr, buf, sizeof(buf))) - LogInfo("nodeAbsence: IPv6 address: %s", buf); - else - LogInfo("nodeAbsence: inet_ntop() error"); peerAddr.type = mDNSAddrType_IPv6; peerAddr.ip.v6 = *(mDNSv6Addr*)&p->sin6_node_address.sin6_addr; - LogInfo("nodeAbsence: immediately purge cached records from this peer"); + LogInfo("nodeAbsence: immediately purge cached records from %.16a", p->sin6_node_address.sin6_addr.s6_addr); removeCachedPeerRecords(p->sdl_node_address.sdl_index, & peerAddr, true); } @@ -8367,8 +8403,13 @@ mDNSlocal OSStatus KeychainChanged(SecKeychainEvent keychainEvent, SecKeychainCa if (!err) { relevant = ((a->attr[0].length == 4 && (!strncasecmp(a->attr[0].data, "ddns", 4) || !strncasecmp(a->attr[0].data, "sndd", 4))) || - (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix)))) || - (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix) && (!strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix))))); + (a->attr[1].length >= mDNSPlatformStrLen(dnsprefix) && (!strncasecmp(a->attr[1].data, dnsprefix, mDNSPlatformStrLen(dnsprefix))))); +#if MDNSRESPONDER_BTMM_SUPPORT + if (!relevant && (a->attr[1].length >= mDNSPlatformStrLen(btmmprefix)) && !strncasecmp(a->attr[1].data, btmmprefix, mDNSPlatformStrLen(btmmprefix))) + { + relevant = mDNStrue; + } +#endif SecKeychainItemFreeAttributesAndData(a, NULL); } } @@ -9419,36 +9460,13 @@ mDNSlocal void CreatePTRRecord(const domainname *domain) // intentionally to avoid adding to the complexity of code handling /etc/hosts. mDNSlocal void SetupLocalHostRecords(void) { - char buffer[MAX_REVERSE_MAPPING_NAME]; domainname name; - int i; - struct in6_addr addr; - mDNSu8 *ptr = addr.__u6_addr.__u6_addr8; - if (inet_pton(AF_INET, "127.0.0.1", &addr) == 1) - { - mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", - ptr[3], ptr[2], ptr[1], ptr[0]); - MakeDomainNameFromDNSNameString(&name, buffer); - CreatePTRRecord(&name); - } - else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET failed"); + MakeDomainNameFromDNSNameString(&name, "1.0.0.127.in-addr.arpa."); + CreatePTRRecord(&name); - if (inet_pton(AF_INET6, "::1", &addr) == 1) - { - for (i = 0; i < 16; i++) - { - static const char hexValues[] = "0123456789ABCDEF"; - buffer[i * 4 ] = hexValues[ptr[15 - i] & 0x0F]; - buffer[i * 4 + 1] = '.'; - buffer[i * 4 + 2] = hexValues[ptr[15 - i] >> 4]; - buffer[i * 4 + 3] = '.'; - } - mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa."); - MakeDomainNameFromDNSNameString(&name, buffer); - CreatePTRRecord(&name); - } - else LogMsg("SetupLocalHostRecords: ERROR!! inet_pton AF_INET6 failed"); + MakeDomainNameFromDNSNameString(&name, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa."); + CreatePTRRecord(&name); } #if APPLE_OSX_mDNSResponder // Don't compile for dnsextd target @@ -9465,6 +9483,56 @@ mDNSlocal void setSameDomainLabelPointer(void); // 6) client calls to enumerate domains now go over LocalOnly interface // (!!!KRS may add outgoing interface in addition) +#if TARGET_OS_IPHONE +mDNSlocal mDNSBool IsAppleInternalBuild(void) +{ + return (os_variant_has_internal_diagnostics("com.apple.mDNSResponder") ? mDNStrue : mDNSfalse); +} + +mDNSlocal mStatus RegisterLocalOnlyAddressRecord(const domainname *const name, mDNSu16 type, const void *rdata, mDNSu16 rdlength) +{ + switch(type) + { + case kDNSType_A: + if (rdlength != 4) return (mStatus_BadParamErr); + break; + + case kDNSType_AAAA: + if (rdlength != 16) return (mStatus_BadParamErr); + break; + + default: + return (mStatus_BadParamErr); + } + + AuthRecord *rr = mallocL("etchosts", sizeof(*rr)); + if (!rr) return (mStatus_NoMemoryErr); + mDNSPlatformMemZero(rr, sizeof(*rr)); + + mDNS_SetupResourceRecord(rr, NULL, mDNSInterface_LocalOnly, type, 1, kDNSRecordTypeKnownUnique, AuthRecordLocalOnly, FreeEtcHosts, NULL); + AssignDomainName(&rr->namestorage, name); + mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlength); + + const mStatus err = mDNS_Register_internal(&mDNSStorage, rr); + if (err) + { + LogMsg("RegisterLocalOnlyAddressRecord: mDNS_Register error %d registering %s", err, ARDisplayString(&mDNSStorage, rr)); + freeL("etchosts", rr); + } + return (err); +} + +mDNSlocal void RegisterLocalOnlyARecord(const domainname *const name, const mDNSv4Addr *const addr) +{ + RegisterLocalOnlyAddressRecord(name, kDNSType_A, addr->b, (mDNSu16)sizeof(mDNSv4Addr)); +} + +mDNSlocal void RegisterLocalOnlyAAAARecord(const domainname *const name, const mDNSv6Addr *const addr) +{ + RegisterLocalOnlyAddressRecord(name, kDNSType_AAAA, addr->b, (mDNSu16)sizeof(mDNSv6Addr)); +} +#endif + mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) { mStatus err; @@ -9694,7 +9762,41 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) #endif if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL"); - mDNSMacOSXUpdateEtcHosts(m); +#if TARGET_OS_IPHONE + // On device OSes (iOS, tvOS, watchOS, etc.), ignore /etc/hosts unless the OS is an internal build. When the /etc/hosts + // file is ignored, LocalOnly auth records will be registered for localhost and broadcasthost addresses contained in the + // standard /etc/hosts file: + // + // 127.0.0.1 localhost + // 255.255.255.255 broadcasthost + // ::1 localhost + + if (!IsAppleInternalBuild()) + { + const domainname *const localHostName = (const domainname *) "\x9" "localhost"; + const domainname *const broadcastHostName = (const domainname *) "\xd" "broadcasthost"; + const mDNSv4Addr localHostV4 = { { 127, 0, 0, 1 } }; + mDNSv6Addr localHostV6; + + // Register localhost 127.0.0.1 A record. + + RegisterLocalOnlyARecord(localHostName, &localHostV4); + + // Register broadcasthost 255.255.255.255 A record. + + RegisterLocalOnlyARecord(broadcastHostName, &onesIPv4Addr); + + // Register localhost ::1 AAAA record. + + mDNSPlatformMemZero(&localHostV6, sizeof(localHostV6)); + localHostV6.b[15] = 1; + RegisterLocalOnlyAAAARecord(localHostName, &localHostV6); + } + else +#endif + { + mDNSMacOSXUpdateEtcHosts(m); + } SetupLocalHostRecords(); return(mStatus_NoError); diff --git a/mDNSResponder/mDNSMacOSX/mDNSMacOSX.h b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.h index 55c74c6c..f189b07f 100644 --- a/mDNSResponder/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSResponder/mDNSMacOSX/mDNSMacOSX.h @@ -153,6 +153,7 @@ struct NetworkInterfaceInfoOSX_struct int BPF_mcfd; // Socket for our IPv6 ND group membership u_int BPF_len; mDNSBool isExpensive; // True if this interface has the IFEF_EXPENSIVE flag set. + mDNSBool isAWDL; // True if this interface has the IFEF_AWDL flag set. #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM dispatch_source_t BPF_source; #else @@ -272,7 +273,7 @@ struct CompileTimeAssertionChecks_mDNSMacOSX // Check our structures are reasonable sizes. Including overly-large buffers, or embedding // other overly-large structures instead of having a pointer to them, can inadvertently // cause structure sizes (and therefore memory usage) to balloon unreasonably. - char sizecheck_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <= 7464) ? 1 : -1]; + char sizecheck_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <= 8488) ? 1 : -1]; char sizecheck_mDNS_PlatformSupport [(sizeof(mDNS_PlatformSupport) <= 1378) ? 1 : -1]; }; diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.sb b/mDNSResponder/mDNSMacOSX/mDNSResponder.sb index 1458815c..29186314 100644 --- a/mDNSResponder/mDNSMacOSX/mDNSResponder.sb +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.sb @@ -1,6 +1,6 @@ ; -*- Mode: Scheme; tab-width: 4 -*- ; -; Copyright (c) 2012-2015 Apple Inc. All rights reserved. +; Copyright (c) 2012-2018 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: @@ -128,7 +128,7 @@ (literal "/private/var/preferences/SystemConfiguration/preferences.plist") (subpath "/System/Library/Preferences/Logging") (subpath "/AppleInternal/Library/Preferences/Logging") - (subpath "/private/var/preferences/Logging/Subsystems") + (subpath "/private/var/preferences/Logging") (subpath "/private/var/db/timezone") (subpath "/Library/Preferences/Logging")) diff --git a/mDNSResponder/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj b/mDNSResponder/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj index f1a8b1da..bbbd12ff 100644 --- a/mDNSResponder/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj +++ b/mDNSResponder/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj @@ -271,8 +271,9 @@ 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 */; }; + 789036921F7AC1FA0077A962 /* libnetwork.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 789036911F7AC1F90077A962 /* libnetwork.tbd */; }; + 789036931F7AC2050077A962 /* libnetwork.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 789036911F7AC1F90077A962 /* libnetwork.tbd */; }; 8415A6571897109000BDBA26 /* libdns_services.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8415A6561897109000BDBA26 /* libdns_services.dylib */; }; 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 */; }; @@ -324,8 +325,14 @@ B7E06B0D1DBA9DFE00E4580C /* ClientCommon.c in Sources */ = {isa = PBXBuildFile; fileRef = FF5852100DD27BD300862BDF /* ClientCommon.c */; }; B7E06B0E1DBA9E9700E4580C /* DomainBrowser.strings in Resources */ = {isa = PBXBuildFile; fileRef = B7016F4F1D5D0D1900107E7C /* DomainBrowser.strings */; }; BD03E88D1AD31278005E8A81 /* SymptomReporter.c in Sources */ = {isa = PBXBuildFile; fileRef = BD03E88C1AD31278005E8A81 /* SymptomReporter.c */; }; + BD28AE8F207B892D00F0B257 /* bonjour-mcast-diagnose in Copy diagnose scripts */ = {isa = PBXBuildFile; fileRef = BD28AE8E207B88F600F0B257 /* bonjour-mcast-diagnose */; }; + BD41B27D203EBE6100A53629 /* dns_sd.h in Headers */ = {isa = PBXBuildFile; fileRef = FFA572630AF190C20055A0F1 /* dns_sd.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BD41F9C4209B60AC0077F8B6 /* libpcap.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BD41F9C3209B60AC0077F8B6 /* libpcap.tbd */; }; BD691B2A1ED2F47100E6F317 /* DNS64.c in Sources */ = {isa = PBXBuildFile; fileRef = BD691B281ED2F43200E6F317 /* DNS64.c */; }; BD691B2B1ED2F4AB00E6F317 /* DNS64.h in Headers */ = {isa = PBXBuildFile; fileRef = BD691B291ED2F43200E6F317 /* DNS64.h */; }; + BD75E940206ADEF400656ED3 /* com.apple.mDNSResponder.plist in Copy AppleInternal Logging Profile */ = {isa = PBXBuildFile; fileRef = BDB61846206ADDDF00AFF600 /* com.apple.mDNSResponder.plist */; }; + BD893CE5206C0D980055F9E7 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD893CE4206C0D980055F9E7 /* SystemConfiguration.framework */; }; + BD893CE7206C0EAF0055F9E7 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD893CE6206C0EAF0055F9E7 /* CoreFoundation.framework */; }; BD9BA7551EAF91FB00658CCF /* dnssdutil.c in Sources */ = {isa = PBXBuildFile; fileRef = BD9BA7541EAF91E700658CCF /* dnssdutil.c */; }; BD9BA7581EAF929C00658CCF /* CoreUtils.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD9BA7571EAF929C00658CCF /* CoreUtils.framework */; }; BDA3F08A1C48DB920054FB4B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDA3F0891C48DB910054FB4B /* Foundation.framework */; }; @@ -333,6 +340,12 @@ 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, ); }; }; + BDAF4BC020B52D3D0062219E /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDAF4BBF20B52D3D0062219E /* CFNetwork.framework */; }; + BDB04221203FEF4C00419961 /* dns_sd.h in Headers */ = {isa = PBXBuildFile; fileRef = FFA572630AF190C20055A0F1 /* dns_sd.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BDB04222203FEF4D00419961 /* dns_sd.h in Headers */ = {isa = PBXBuildFile; fileRef = FFA572630AF190C20055A0F1 /* dns_sd.h */; settings = {ATTRIBUTES = (Public, ); }; }; + BDB04223203FF18000419961 /* dns_sd_private.h in Headers */ = {isa = PBXBuildFile; fileRef = BDA9A7871B3A923600523835 /* dns_sd_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + BDB04224203FF18000419961 /* dns_sd_private.h in Headers */ = {isa = PBXBuildFile; fileRef = BDA9A7871B3A923600523835 /* dns_sd_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + BDB61845206ADB9D00AFF600 /* com.apple.mDNSResponder.plist in Copy Base Logging Profile */ = {isa = PBXBuildFile; fileRef = BDB61843206ADB7700AFF600 /* com.apple.mDNSResponder.plist */; }; BDBF9B941ED74B9C001498A8 /* DNS64State.h in Headers */ = {isa = PBXBuildFile; fileRef = BDBF9B931ED74B8C001498A8 /* DNS64State.h */; }; D284BE540ADD80740027CCDF /* dnssd_ipc.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E11B5B04A28126019798ED /* dnssd_ipc.h */; }; D284BE580ADD80740027CCDF /* mDNS.c in Sources */ = {isa = PBXBuildFile; fileRef = 6575FBE9022EAF5A00000109 /* mDNS.c */; }; @@ -717,14 +730,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 8418673D15AB8BFF00BB7F70 /* CopyFiles */ = { + 8418673D15AB8BFF00BB7F70 /* Copy Base Logging Profile */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; dstPath = /System/Library/Preferences/Logging/Subsystems; dstSubfolderSpec = 0; files = ( - 729DF4601CD40630005ECF70 /* com.apple.mDNSResponder.plist in CopyFiles */, + BDB61845206ADB9D00AFF600 /* com.apple.mDNSResponder.plist in Copy Base Logging Profile */, ); + name = "Copy Base Logging Profile"; runOnlyForDeploymentPostprocessing = 1; }; B7D566C61E81D9B600E43008 /* CopyFiles */ = { @@ -738,6 +752,28 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + BD28AE8C207B888E00F0B257 /* Copy diagnose scripts */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/local/bin; + dstSubfolderSpec = 0; + files = ( + BD28AE8F207B892D00F0B257 /* bonjour-mcast-diagnose in Copy diagnose scripts */, + ); + name = "Copy diagnose scripts"; + runOnlyForDeploymentPostprocessing = 1; + }; + BD75E93F206ADEAD00656ED3 /* Copy AppleInternal Logging Profile */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /AppleInternal/Library/Preferences/Logging/Subsystems; + dstSubfolderSpec = 0; + files = ( + BD75E940206ADEF400656ED3 /* com.apple.mDNSResponder.plist in Copy AppleInternal Logging Profile */, + ); + name = "Copy AppleInternal Logging Profile"; + runOnlyForDeploymentPostprocessing = 1; + }; D284BE6A0ADD80740027CCDF /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -891,9 +927,9 @@ 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; }; + 789036911F7AC1F90077A962 /* libnetwork.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libnetwork.tbd; path = usr/lib/libnetwork.tbd; sourceTree = SDKROOT; }; 7F18A9F60587CEF6001880B3 /* DNSCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSCommon.c; path = ../mDNSCore/DNSCommon.c; sourceTree = SOURCE_ROOT; }; 7F18A9F70587CEF6001880B3 /* uDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uDNS.c; path = ../mDNSCore/uDNS.c; sourceTree = SOURCE_ROOT; }; 7F461DB5062DBF2900672BF3 /* DNSDigest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSDigest.c; path = ../mDNSCore/DNSDigest.c; sourceTree = SOURCE_ROOT; }; @@ -952,8 +988,12 @@ B7D6CA701D1076F3005E24CF /* DomainBrowser.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DomainBrowser.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B7E5920F1DB687A700A38085 /* Base */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Base; path = Base.lproj/DNSServiceDiscoveryPref.nib; sourceTree = ""; }; BD03E88C1AD31278005E8A81 /* SymptomReporter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SymptomReporter.c; sourceTree = ""; }; + BD28AE8E207B88F600F0B257 /* bonjour-mcast-diagnose */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "bonjour-mcast-diagnose"; sourceTree = ""; }; + BD41F9C3209B60AC0077F8B6 /* libpcap.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libpcap.tbd; path = usr/lib/libpcap.tbd; sourceTree = SDKROOT; }; BD691B281ED2F43200E6F317 /* DNS64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNS64.c; sourceTree = ""; }; BD691B291ED2F43200E6F317 /* DNS64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNS64.h; sourceTree = ""; }; + BD893CE4206C0D980055F9E7 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + BD893CE6206C0EAF0055F9E7 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; BD9BA7531EAF90E400658CCF /* dnssdutil */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnssdutil; sourceTree = BUILT_PRODUCTS_DIR; }; BD9BA7541EAF91E700658CCF /* dnssdutil.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssdutil.c; path = ../Clients/dnssdutil.c; sourceTree = ""; }; BD9BA7571EAF929C00658CCF /* CoreUtils.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreUtils.framework; path = System/Library/PrivateFrameworks/CoreUtils.framework; sourceTree = SDKROOT; }; @@ -961,6 +1001,9 @@ 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 = ../mDNSShared/dns_sd_private.h; sourceTree = ""; }; + BDAF4BBF20B52D3D0062219E /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; }; + BDB61843206ADB7700AFF600 /* com.apple.mDNSResponder.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = com.apple.mDNSResponder.plist; sourceTree = ""; }; + BDB61846206ADDDF00AFF600 /* com.apple.mDNSResponder.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.apple.mDNSResponder.plist; sourceTree = ""; }; BDBF9B931ED74B8C001498A8 /* DNS64State.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNS64State.h; sourceTree = ""; }; BDE238C11DF69D8300B9F696 /* dns_sd_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_sd_internal.h; path = ../mDNSShared/dns_sd_internal.h; sourceTree = ""; }; D284BE730ADD80740027CCDF /* mDNSResponder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1084,6 +1127,7 @@ 21B830A61D8A63E400AE2001 /* CoreFoundation.framework in Frameworks */, 21B830A21D8A63A300AE2001 /* libxml2.dylib in Frameworks */, 37538E141D7A43B600226BE4 /* libicucore.dylib in Frameworks */, + 789036931F7AC2050077A962 /* libnetwork.tbd in Frameworks */, 21B830A41D8A63BB00AE2001 /* Security.framework in Frameworks */, 21B830A51D8A63CB00AE2001 /* IOKit.framework in Frameworks */, ); @@ -1149,7 +1193,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BD41F9C4209B60AC0077F8B6 /* libpcap.tbd in Frameworks */, + BDAF4BC020B52D3D0062219E /* CFNetwork.framework in Frameworks */, + BD893CE7206C0EAF0055F9E7 /* CoreFoundation.framework in Frameworks */, BD9BA7581EAF929C00658CCF /* CoreUtils.framework in Frameworks */, + BD893CE5206C0D980055F9E7 /* SystemConfiguration.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1164,6 +1212,7 @@ D284BE660ADD80740027CCDF /* SystemConfiguration.framework in Frameworks */, 21B830A81D8A642200AE2001 /* libicucore.dylib in Frameworks */, 219D5542149ED645004464AE /* libxml2.dylib in Frameworks */, + 789036921F7AC1FA0077A962 /* libnetwork.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1246,6 +1295,8 @@ 19C28FBDFE9D53C911CA2CBB /* Products */, 37DDE9241BA382280092AC61 /* Unit Tests */, BD9BA7561EAF929C00658CCF /* Frameworks */, + BDB61842206ADB7700AFF600 /* LoggingProfiles */, + BD28AE8D207B88F600F0B257 /* Scripts */, ); name = mDNSResponder; sourceTree = ""; @@ -1267,7 +1318,6 @@ 21F51DBD1B3540DB0070B05C /* com.apple.dnsextd.plist */, 21F51DBF1B35412D0070B05C /* com.apple.mDNSResponder.plist */, 21F51DBE1B3541030070B05C /* com.apple.mDNSResponderHelper.plist */, - 729DF45F1CD40630005ECF70 /* com.apple.mDNSResponder.plist */, 21A57F4A145B2AE100939099 /* CryptoAlg.c */, 21A57F4B145B2AE100939099 /* CryptoAlg.h */, 21A57F51145B2B1400939099 /* CryptoSupport.c */, @@ -1568,14 +1618,44 @@ path = macOS; sourceTree = ""; }; + BD28AE8D207B88F600F0B257 /* Scripts */ = { + isa = PBXGroup; + children = ( + BD28AE8E207B88F600F0B257 /* bonjour-mcast-diagnose */, + ); + path = Scripts; + sourceTree = ""; + }; BD9BA7561EAF929C00658CCF /* Frameworks */ = { isa = PBXGroup; children = ( + BDAF4BBF20B52D3D0062219E /* CFNetwork.framework */, + BD41F9C3209B60AC0077F8B6 /* libpcap.tbd */, + BD893CE6206C0EAF0055F9E7 /* CoreFoundation.framework */, + BD893CE4206C0D980055F9E7 /* SystemConfiguration.framework */, + 789036911F7AC1F90077A962 /* libnetwork.tbd */, BD9BA7571EAF929C00658CCF /* CoreUtils.framework */, ); name = Frameworks; sourceTree = ""; }; + BDB61842206ADB7700AFF600 /* LoggingProfiles */ = { + isa = PBXGroup; + children = ( + BDB61843206ADB7700AFF600 /* com.apple.mDNSResponder.plist */, + BDB61844206ADB7700AFF600 /* AppleInternal */, + ); + path = LoggingProfiles; + sourceTree = ""; + }; + BDB61844206ADB7700AFF600 /* AppleInternal */ = { + isa = PBXGroup; + children = ( + BDB61846206ADDDF00AFF600 /* com.apple.mDNSResponder.plist */, + ); + path = AppleInternal; + sourceTree = ""; + }; DB2CC4420662DCE500335AB3 /* Java Support */ = { isa = PBXGroup; children = ( @@ -1784,6 +1864,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + BDB04221203FEF4C00419961 /* dns_sd.h in Headers */, + BDB04223203FF18000419961 /* dns_sd_private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1791,6 +1873,8 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + BDB04222203FEF4D00419961 /* dns_sd.h in Headers */, + BDB04224203FF18000419961 /* dns_sd_private.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1799,6 +1883,7 @@ buildActionMask = 2147483647; files = ( BDA9A7881B3A924C00523835 /* dns_sd_private.h in Headers */, + BD41B27D203EBE6100A53629 /* dns_sd.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1881,7 +1966,6 @@ 2141DD19123FFCDB0086D23E /* Headers */, 2141DD1A123FFCDB0086D23E /* Sources */, 2141DD1B123FFCDB0086D23E /* Frameworks */, - 2130256B12400DE600AC839F /* ShellScript */, ); buildRules = ( ); @@ -2115,8 +2199,10 @@ 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */, 4A7B9E8114FDA25500B84CC1 /* CopyFiles */, D284BE6C0ADD80740027CCDF /* Run Script */, - 8418673D15AB8BFF00BB7F70 /* CopyFiles */, 21F51DC01B35418C0070B05C /* CopyFiles */, + 8418673D15AB8BFF00BB7F70 /* Copy Base Logging Profile */, + BD75E93F206ADEAD00656ED3 /* Copy AppleInternal Logging Profile */, + BD28AE8C207B888E00F0B257 /* Copy diagnose scripts */, ); buildRules = ( ); @@ -2439,19 +2525,6 @@ shellPath = /bin/sh; shellScript = "if [ -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\nipsec=$(ls \"${SDKROOT}/usr/lib/libipsec.*\" 2> /dev/null | wc -l)\nif [ \"$ipsec\" != \"0\" ]\nthen\nrm -f \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\ntouch \"${CONFIGURATION_TEMP_DIR}/ipsec_options.h\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nelse\necho \"#define MDNS_NO_IPSEC 1\" > ${CONFIGURATION_TEMP_DIR}/ipsec_options.h\ntouch \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfor i in ${ARCHS}\ndo\nccflags=\"-arch $i $ccflags\"\ndone\ncc ${ccflags} \"${CONFIGURATION_TEMP_DIR}/empty.c\" -c -o \"${CONFIGURATION_TEMP_DIR}/libipsec.a\"\nrm -f \"${CONFIGURATION_TEMP_DIR}/empty.c\"\nfi\n"; }; - 2130256B12400DE600AC839F /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 8; - files = ( - ); - inputPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 1; - shellPath = /bin/sh; - shellScript = "DSTROOT=${DSTROOT}\n\nmkdir -p \"$DSTROOT/usr/include\"\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; - }; 21DE714D115831CB00DD4BD1 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 8; @@ -2463,7 +2536,7 @@ ); runOnlyForDeploymentPostprocessing = 1; 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"; + shellScript = "DSTROOT=${DSTROOT}\n\nif [[ \"${ACTION}\" == \"installhdrs\" ]] || [[ \"${ACTION}\" == \"installapi\" ]]; 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"; }; 37DDE9341BA384000092AC61 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -3154,7 +3227,6 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "__APPLE_USE_RFC_3542=1", - "_DNS_SD_LIBDISPATCH=1", "APPLE_OSX_mDNSResponder=1", "__MigTypeCheck=1", "mDNSResponderVersion=${MVERS}", @@ -3249,7 +3321,6 @@ "-lMobileGestalt", ); "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( - "-lAWACS", "-weak_framework", WebFilterDNS, "-weak_framework", @@ -3339,7 +3410,6 @@ buildSettings = { GCC_PREPROCESSOR_DEFINITIONS = ( "__APPLE_USE_RFC_3542=1", - "_DNS_SD_LIBDISPATCH=1", "APPLE_OSX_mDNSResponder=1", "__MigTypeCheck=1", "mDNSResponderVersion=${MVERS}", @@ -3431,6 +3501,7 @@ INSTALLHDRS_SCRIPT_PHASE = YES; INSTALL_PATH = /usr/local/lib/system; PRODUCT_NAME = dns_sd; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; }; name = Debug; @@ -3474,12 +3545,14 @@ "$(inherited)", "__DARWIN_NON_CANCELABLE=1", ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; 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; + IS_ZIPPERED = YES; LINK_WITH_STANDARD_LIBRARIES = NO; OTHER_LDFLAGS = ( "-Wl,-umbrella,System", @@ -3496,7 +3569,11 @@ "-llaunch", "-lsystem_asl", ); + OTHER_TAPI_FLAGS = "-umbrella System -extra-public-header $(SRCROOT)/DNSServiceDiscovery.h"; PRODUCT_NAME = libsystem_dnssd; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; }; name = Debug; }; @@ -3509,6 +3586,7 @@ "$(inherited)", "__DARWIN_NON_CANCELABLE=1", ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; INSTALL_PATH = /usr/lib/system; INTERPOSITION_SIM_SUFFIX = ""; @@ -3529,7 +3607,11 @@ "-llaunch", "-lsystem_asl", ); + OTHER_TAPI_FLAGS = "-umbrella System -extra-public-header $(SRCROOT)/DNSServiceDiscovery.h"; PRODUCT_NAME = libsystem_dnssd_debug; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; }; name = Debug; }; @@ -3543,6 +3625,7 @@ "$(inherited)", "__DARWIN_NON_CANCELABLE=1", ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; GENERATE_PROFILING_CODE = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; INSTALL_PATH = /usr/lib/system; @@ -3564,7 +3647,11 @@ "-llaunch", "-lsystem_asl", ); + OTHER_TAPI_FLAGS = "-umbrella System -extra-public-header $(SRCROOT)/DNSServiceDiscovery.h"; PRODUCT_NAME = libsystem_dnssd_profile; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; }; name = Debug; }; @@ -3755,6 +3842,7 @@ INSTALLHDRS_SCRIPT_PHASE = YES; INSTALL_PATH = /usr/local/lib/system; PRODUCT_NAME = dns_sd; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; "SKIP_INSTALL[sdk=iphonesimulator*]" = YES; }; name = Release; @@ -3836,7 +3924,6 @@ ); GCC_PREPROCESSOR_DEFINITIONS = ( "__APPLE_USE_RFC_3542=1", - "_DNS_SD_LIBDISPATCH=1", "APPLE_OSX_mDNSResponder=1", "__MigTypeCheck=1", "mDNSResponderVersion=${MVERS}", @@ -3899,7 +3986,6 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "__APPLE_USE_RFC_3542=1", - "_DNS_SD_LIBDISPATCH=1", "APPLE_OSX_mDNSResponder=1", "__MigTypeCheck=1", "mDNSResponderVersion=${MVERS}", @@ -4644,7 +4730,6 @@ DEAD_CODE_STRIPPING = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "__APPLE_USE_RFC_3542=1", - "_DNS_SD_LIBDISPATCH=1", "APPLE_OSX_mDNSResponder=1", "__MigTypeCheck=1", "mDNSResponderVersion=${MVERS}", @@ -4715,7 +4800,6 @@ "-lMobileGestalt", ); "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( - "-lAWACS", "-weak_framework", WebFilterDNS, "-weak_framework", @@ -4807,6 +4891,7 @@ "$(inherited)", "__DARWIN_NON_CANCELABLE=1", ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; INSTALL_PATH = /usr/lib/system; INTERPOSITION_SIM_SUFFIX = ""; @@ -4827,7 +4912,11 @@ "-llaunch", "-lsystem_asl", ); + OTHER_TAPI_FLAGS = "-umbrella System -extra-public-header $(SRCROOT)/DNSServiceDiscovery.h"; PRODUCT_NAME = libsystem_dnssd_debug; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; }; name = Release; }; @@ -4841,6 +4930,7 @@ "$(inherited)", "__DARWIN_NON_CANCELABLE=1", ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; GENERATE_PROFILING_CODE = YES; HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; INSTALL_PATH = /usr/lib/system; @@ -4862,7 +4952,11 @@ "-llaunch", "-lsystem_asl", ); + OTHER_TAPI_FLAGS = "-umbrella System -extra-public-header $(SRCROOT)/DNSServiceDiscovery.h"; PRODUCT_NAME = libsystem_dnssd_profile; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; }; name = Release; }; @@ -4889,12 +4983,14 @@ "$(inherited)", "__DARWIN_NON_CANCELABLE=1", ); + GCC_SYMBOLS_PRIVATE_EXTERN = YES; 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; + IS_ZIPPERED = YES; LINK_WITH_STANDARD_LIBRARIES = NO; OTHER_LDFLAGS = ( "-Wl,-umbrella,System", @@ -4911,7 +5007,11 @@ "-llaunch", "-lsystem_asl", ); + OTHER_TAPI_FLAGS = "-umbrella System -extra-public-header $(SRCROOT)/DNSServiceDiscovery.h"; PRODUCT_NAME = libsystem_dnssd; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include; + SUPPORTS_TEXT_BASED_API = YES; + TAPI_VERIFY_MODE = Pedantic; }; name = Release; }; diff --git a/mDNSResponder/mDNSMacOSX/uDNSPathEvalulation.c b/mDNSResponder/mDNSMacOSX/uDNSPathEvalulation.c index 96d4a0db..e4744736 100644 --- a/mDNSResponder/mDNSMacOSX/uDNSPathEvalulation.c +++ b/mDNSResponder/mDNSMacOSX/uDNSPathEvalulation.c @@ -17,7 +17,13 @@ #include "mDNSMacOSX.h" #include -#include + +#if __has_include() + #include +#else + #include +#endif + #include "dns_sd_internal.h" //Gets the DNSPolicy from NW PATH EVALUATOR diff --git a/mDNSResponder/mDNSPosix/ProxyResponder.c b/mDNSResponder/mDNSPosix/ProxyResponder.c index bd0fe582..94fde855 100644 --- a/mDNSResponder/mDNSPosix/ProxyResponder.c +++ b/mDNSResponder/mDNSPosix/ProxyResponder.c @@ -155,7 +155,7 @@ mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset, mDNS_RegisterService(m, recordset, &n, &t, &d, // Name, type, domain host, mDNSOpaque16fromIntVal(PortAsNumber), - txtbuffer, bptr-txtbuffer, // TXT data, length + mDNSNULL, txtbuffer, bptr-txtbuffer, // TXT data, length mDNSNULL, 0, // Subtypes mDNSInterface_Any, // Interface ID ServiceCallback, mDNSNULL, 0); // Callback, context, flags diff --git a/mDNSResponder/mDNSPosix/Responder.c b/mDNSResponder/mDNSPosix/Responder.c index 3850ea26..d4e20104 100755 --- a/mDNSResponder/mDNSPosix/Responder.c +++ b/mDNSResponder/mDNSPosix/Responder.c @@ -439,7 +439,7 @@ static mStatus RegisterOneService(const char * richTextName, status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ, &name, &type, &domain, // Name, type, domain NULL, mDNSOpaque16fromIntVal(portNumber), - text, textLen, // TXT data, length + NULL, text, textLen, // TXT data, length NULL, 0, // Subtypes mDNSInterface_Any, // Interface ID RegistrationCallback, thisServ, 0); // Callback, context, flags diff --git a/mDNSResponder/mDNSPosix/mDNSUNP.c b/mDNSResponder/mDNSPosix/mDNSUNP.c index 17a37c61..e00ddd62 100755 --- a/mDNSResponder/mDNSPosix/mDNSUNP.c +++ b/mDNSResponder/mDNSPosix/mDNSUNP.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -80,40 +80,56 @@ void plen_to_mask(int plen, char *addr) { } /* Gets IPv6 interface information from the /proc filesystem in linux*/ -struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) +struct ifi_info *get_ifi_info_linuxv6(int doaliases) { struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr; 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 ':' */ + int i, nitems, flags, index, plen, scope; struct addrinfo hints, *res0; int err; int sockfd = -1; struct ifreq ifr; + char ifnameFmt[16], addrStr[32 + 7 + 1], ifname[IFNAMSIZ], lastname[IFNAMSIZ]; res0=NULL; ifihead = NULL; ifipnext = &ifihead; - lastname[0] = 0; if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) { sockfd = socket(AF_INET6, SOCK_DGRAM, 0); if (sockfd < 0) { goto gotError; } - while (fscanf(fp, - "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\n", - addr[0],addr[1],addr[2],addr[3], - addr[4],addr[5],addr[6],addr[7], - &index, &plen, &scope, &flags, ifname) != EOF) { - - myflags = 0; - if (strncmp(lastname, ifname, IFNAMSIZ) == 0) { + + // Parse /proc/net/if_inet6 according to . + + // Create a string specifier with a width of IFNAMSIZ - 1 ("%s") to scan the interface name. The + // reason why we don't just use the string-ified macro expansion of IFNAMSIZ for the width is because the width + // needs to be a decimal string and there's no guarantee that IFNAMSIZ will be defined as a decimal integer. For + // example, it could be defined in hexadecimal or as an arithmetic expression. + + snprintf(ifnameFmt, sizeof(ifnameFmt), "%%%ds", IFNAMSIZ - 1); + + // Write the seven IPv6 address string colons and NUL terminator, i.e., "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx". + // The remaining 32 IPv6 address characters come from /proc/net/if_inet6. + + for (i = 4; i < 39; i += 5) addrStr[i] = ':'; + addrStr[39] = '\0'; + + lastname[0] = '\0'; + for (;;) { + nitems = fscanf(fp, " %4c%4c%4c%4c%4c%4c%4c%4c %x %x %x %x", + &addrStr[0], &addrStr[5], &addrStr[10], &addrStr[15], + &addrStr[20], &addrStr[25], &addrStr[30], &addrStr[35], + &index, &plen, &scope, &flags); + if (nitems != 12) break; + + nitems = fscanf(fp, ifnameFmt, ifname); + if (nitems != 1) break; + + if (strcmp(lastname, ifname) == 0) { if (doaliases == 0) continue; /* already processed this interface */ - myflags = IFI_ALIAS; } memcpy(lastname, ifname, IFNAMSIZ); ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info)); @@ -126,15 +142,11 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) *ifipnext = ifi; /* prev points to this new one */ ifipnext = &ifi->ifi_next; /* pointer to next one goes here */ - sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s", - addr[0],addr[1],addr[2],addr[3], - addr[4],addr[5],addr[6],addr[7]); - /* Add address of the interface */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_flags = AI_NUMERICHOST; - err = getaddrinfo(addr6, NULL, &hints, &res0); + err = getaddrinfo(addrStr, NULL, &hints, &res0); if (err) { goto gotError; } @@ -152,9 +164,9 @@ struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases) goto gotError; } - ((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_family=family; + ((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_family=AF_INET6; ((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_scope_id=scope; - inet_pton(family, ipv6addr, &((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_addr); + inet_pton(AF_INET6, ipv6addr, &((struct sockaddr_in6 *)ifi->ifi_netmask)->sin6_addr); /* Add interface name */ memcpy(ifi->ifi_name, ifname, IFI_NAME); @@ -228,7 +240,7 @@ struct ifi_info *get_ifi_info(int family, int doaliases) #endif #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX - if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases); + if (family == AF_INET6) return get_ifi_info_linuxv6(doaliases); #endif sockfd = -1; diff --git a/mDNSResponder/mDNSPosix/mDNSUNP.h b/mDNSResponder/mDNSPosix/mDNSUNP.h index cc81b7d3..2b36ceb0 100755 --- a/mDNSResponder/mDNSPosix/mDNSUNP.h +++ b/mDNSResponder/mDNSPosix/mDNSUNP.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,8 +62,8 @@ typedef unsigned int socklen_t; #define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr)) #endif -#define IFI_NAME 16 /* same as IFNAMSIZ in */ -#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */ +#define IFI_NAME IFNAMSIZ /* same as IFNAMSIZ in */ +#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */ // Renamed from my_in_pktinfo because in_pktinfo is used by Linux. @@ -98,7 +98,7 @@ struct ifi_info { #if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX #define PROC_IFINET6_PATH "/proc/net/if_inet6" -extern struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases); +extern struct ifi_info *get_ifi_info_linuxv6(int doaliases); #endif #if defined(AF_INET6) && HAVE_IPV6 diff --git a/mDNSResponder/mDNSShared/dns_sd.h b/mDNSResponder/mDNSShared/dns_sd.h index 0e5c35ee..a1f5a573 100644 --- a/mDNSResponder/mDNSShared/dns_sd.h +++ b/mDNSResponder/mDNSShared/dns_sd.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2015 Apple Inc. All rights reserved. + * Copyright (c) 2003-2018 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: @@ -66,7 +66,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 8787002 +#define _DNS_SD_H 8800035 #ifdef __cplusplus extern "C" { @@ -75,9 +75,11 @@ extern "C" { /* Set to 1 if libdispatch is supported * Note: May also be set by project and/or Makefile */ -#ifndef _DNS_SD_LIBDISPATCH +#if defined(__APPLE__) +#define _DNS_SD_LIBDISPATCH 1 +#else #define _DNS_SD_LIBDISPATCH 0 -#endif /* ndef _DNS_SD_LIBDISPATCH */ +#endif /* standard calling convention under Win32 is __stdcall */ /* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */ @@ -88,6 +90,12 @@ extern "C" { #define DNSSD_API #endif +#if (defined(__GNUC__) && (__GNUC__ >= 4)) +#define DNSSD_EXPORT __attribute__((visibility("default"))) +#else +#define DNSSD_EXPORT +#endif + #if defined(_WIN32) #include typedef SOCKET dnssd_sock_t; @@ -526,11 +534,25 @@ enum * This flag is private and should not be used. */ - kDNSServiceFlagsPrivateFour = 0x40000000 + kDNSServiceFlagsPrivateFour = 0x40000000, /* * This flag is private and should not be used. */ + kDNSServiceFlagsAllowExpiredAnswers = 0x80000000, + /* + * When kDNSServiceFlagsAllowExpiredAnswers is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, + * if there are matching expired records still in the cache, then they are immediately returned to the + * client, and in parallel a network query for that name is issued. All returned records from the query will + * remain in the cache after expiration. + */ + + kDNSServiceFlagsExpiredAnswer = 0x80000000 + /* + * When kDNSServiceFlagsAllowExpiredAnswers is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, + * an expired answer will have this flag set. + */ + }; #define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault) @@ -871,6 +893,7 @@ typedef int32_t DNSServiceErrorType; * if the daemon (or "system service" on Windows) is not running. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceGetProperty ( const char *property, /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */ @@ -930,6 +953,7 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty * error. */ +DNSSD_EXPORT dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); @@ -951,6 +975,7 @@ dnssd_sock_t DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef); * an error code indicating the specific failure that occurred. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); @@ -978,6 +1003,7 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); * */ +DNSSD_EXPORT void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef); @@ -1062,6 +1088,7 @@ typedef void (DNSSD_API *DNSServiceDomainEnumReply) * is not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains ( DNSServiceRef *sdRef, @@ -1252,6 +1279,7 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * is not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceRegister ( DNSServiceRef *sdRef, @@ -1307,6 +1335,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegister * error code indicating the error that occurred (the RecordRef is not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceAddRecord ( DNSServiceRef sdRef, @@ -1348,6 +1377,7 @@ DNSServiceErrorType DNSSD_API DNSServiceAddRecord * error code indicating the error that occurred. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord ( DNSServiceRef sdRef, @@ -1380,6 +1410,7 @@ DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord * error code indicating the error that occurred. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord ( DNSServiceRef sdRef, @@ -1485,6 +1516,7 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) * is not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceBrowse ( DNSServiceRef *sdRef, @@ -1613,6 +1645,7 @@ typedef void (DNSSD_API *DNSServiceResolveReply) * is not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceResolve ( DNSServiceRef *sdRef, @@ -1730,6 +1763,7 @@ typedef void (DNSSD_API *DNSServiceQueryRecordReply) * is not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceQueryRecord ( DNSServiceRef *sdRef, @@ -1834,6 +1868,7 @@ typedef void (DNSSD_API *DNSServiceGetAddrInfoReply) * the error that occurred. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo ( DNSServiceRef *sdRef, @@ -1870,6 +1905,7 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo * case the DNSServiceRef is not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); /* DNSServiceRegisterRecord @@ -1952,6 +1988,7 @@ typedef void (DNSSD_API *DNSServiceRegisterRecordReply) * not initialized). */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord ( DNSServiceRef sdRef, @@ -2001,6 +2038,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord * */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord ( DNSServiceFlags flags, @@ -2184,6 +2222,7 @@ typedef void (DNSSD_API *DNSServiceNATPortMappingReply) * display) then pass zero for protocol, internalPort, externalPort and ttl. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate ( DNSServiceRef *sdRef, @@ -2230,6 +2269,7 @@ DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate * */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceConstructFullName ( char * const fullName, @@ -2310,6 +2350,7 @@ typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignmen * the TXTRecordRef. */ +DNSSD_EXPORT void DNSSD_API TXTRecordCreate ( TXTRecordRef *txtRecord, @@ -2328,6 +2369,7 @@ void DNSSD_API TXTRecordCreate * */ +DNSSD_EXPORT void DNSSD_API TXTRecordDeallocate ( TXTRecordRef *txtRecord @@ -2371,6 +2413,7 @@ void DNSSD_API TXTRecordDeallocate * exceed the available storage. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API TXTRecordSetValue ( TXTRecordRef *txtRecord, @@ -2394,6 +2437,7 @@ DNSServiceErrorType DNSSD_API TXTRecordSetValue * exist in the TXTRecordRef. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API TXTRecordRemoveValue ( TXTRecordRef *txtRecord, @@ -2413,6 +2457,7 @@ DNSServiceErrorType DNSSD_API TXTRecordRemoveValue * Returns 0 if the TXTRecordRef is empty. */ +DNSSD_EXPORT uint16_t DNSSD_API TXTRecordGetLength ( const TXTRecordRef *txtRecord @@ -2430,6 +2475,7 @@ uint16_t DNSSD_API TXTRecordGetLength * to DNSServiceUpdateRecord(). */ +DNSSD_EXPORT const void * DNSSD_API TXTRecordGetBytesPtr ( const TXTRecordRef *txtRecord @@ -2484,6 +2530,7 @@ const void * DNSSD_API TXTRecordGetBytesPtr * Otherwise, it returns 0. */ +DNSSD_EXPORT int DNSSD_API TXTRecordContainsKey ( uint16_t txtLen, @@ -2513,6 +2560,7 @@ int DNSSD_API TXTRecordContainsKey * For non-empty value, valueLen will be length of value data. */ +DNSSD_EXPORT const void * DNSSD_API TXTRecordGetValuePtr ( uint16_t txtLen, @@ -2535,6 +2583,7 @@ const void * DNSSD_API TXTRecordGetValuePtr * */ +DNSSD_EXPORT uint16_t DNSSD_API TXTRecordGetCount ( uint16_t txtLen, @@ -2580,6 +2629,7 @@ uint16_t DNSSD_API TXTRecordGetCount * TXTRecordGetCount()-1. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex ( uint16_t txtLen, @@ -2632,6 +2682,7 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex * queue param is invalid */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue ( DNSServiceRef service, @@ -2646,6 +2697,7 @@ typedef void (DNSSD_API *DNSServiceSleepKeepaliveReply) DNSServiceErrorType errorCode, void *context ); +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive ( DNSServiceRef *sdRef, diff --git a/mDNSResponder/mDNSShared/dns_sd_private.h b/mDNSResponder/mDNSShared/dns_sd_private.h index 4d023686..2d429733 100644 --- a/mDNSResponder/mDNSShared/dns_sd_private.h +++ b/mDNSResponder/mDNSShared/dns_sd_private.h @@ -1,11 +1,12 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2015 Apple Inc. All rights reserved. + * Copyright (c) 2015-2018 Apple Inc. All rights reserved. */ #ifndef _DNS_SD_PRIVATE_H #define _DNS_SD_PRIVATE_H +#include // Private flags (kDNSServiceFlagsPrivateOne, kDNSServiceFlagsPrivateTwo, kDNSServiceFlagsPrivateThree, kDNSServiceFlagsPrivateFour) from dns_sd.h enum @@ -58,6 +59,7 @@ enum * returned to indicate that the calling process does not have entitlements * to use this API. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid); #endif @@ -77,12 +79,16 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef * * if the daemon is not running. The value of the pid is undefined if the return * value has error. */ +DNSSD_EXPORT DNSServiceErrorType DNSSD_API DNSServiceGetPID ( uint16_t srcport, int32_t *pid ); +DNSSD_EXPORT +DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain); + #define kDNSServiceCompPrivateDNS "PrivateDNS" #define kDNSServiceCompMulticastDNS "MulticastDNS" diff --git a/mDNSResponder/mDNSShared/dnssd_clientlib.c b/mDNSResponder/mDNSShared/dnssd_clientlib.c index cfc1d424..2a1f5ed4 100644 --- a/mDNSResponder/mDNSShared/dnssd_clientlib.c +++ b/mDNSResponder/mDNSShared/dnssd_clientlib.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2004-2011 Apple Inc. All rights reserved. + * Copyright (c) 2004-2018 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: @@ -31,10 +31,6 @@ #include "dns_sd.h" -#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY -#pragma export on -#endif - #if defined(_WIN32) // disable warning "conversion from to uint16_t" #pragma warning(disable:4244) @@ -361,6 +357,14 @@ DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex #define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s #define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) +// The "used" variable attribute prevents a non-exported variable from being stripped, even if its visibility is hidden, +// e.g., when compiling with -fvisibility=hidden. +#if defined(__GNUC__) +#define DNSSD_USED __attribute__((used)) +#else +#define DNSSD_USED +#endif + // NOT static -- otherwise the compiler may optimize it out // The "@(#) " pattern is a special prefix the "what" command looks for -const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; +const char VersionString_SCCS_libdnssd[] DNSSD_USED = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; diff --git a/mDNSResponder/mDNSShared/dnssd_clientstub.c b/mDNSResponder/mDNSShared/dnssd_clientstub.c index 27e90eee..e8600cba 100644 --- a/mDNSResponder/mDNSShared/dnssd_clientstub.c +++ b/mDNSResponder/mDNSShared/dnssd_clientstub.c @@ -1584,7 +1584,6 @@ DNSServiceErrorType DNSSD_API DNSServiceBrowse return err; } -DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain); DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain) { DNSServiceErrorType err; diff --git a/mDNSResponder/mDNSShared/mDNSDebug.c b/mDNSResponder/mDNSShared/mDNSDebug.c index 1243ae77..dfe77a1d 100644 --- a/mDNSResponder/mDNSShared/mDNSDebug.c +++ b/mDNSResponder/mDNSShared/mDNSDebug.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2015 Apple Inc. All rights reserved. + * Copyright (c) 2003-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,6 +77,7 @@ void LogMsg_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_MSG) void LogOperation_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_OPERATION) void LogSPS_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_SPS) void LogInfo_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_INFO) +void LogDebug_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_DEBUG) #endif #if MDNS_DEBUGMSGS diff --git a/mDNSResponder/mDNSShared/uds_daemon.c b/mDNSResponder/mDNSShared/uds_daemon.c index 946ad162..d1fbc7f2 100644 --- a/mDNSResponder/mDNSShared/uds_daemon.c +++ b/mDNSResponder/mDNSShared/uds_daemon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2015 Apple Inc. All rights reserved. + * Copyright (c) 2003-2018 Apple Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,9 @@ // not fully qualified) with any number of labels e.g., moon, moon.cs, moon.cs.be, etc. mDNSBool AlwaysAppendSearchDomains = mDNSfalse; +// Control enabling ioptimistic DNS +mDNSBool EnableAllowExpired = mDNStrue; + // Apple-specific functionality, not required for other platforms #if APPLE_OSX_mDNSResponder #include @@ -325,8 +328,8 @@ mDNSlocal void abort_request(request_state *req) // Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies if (!req->primary) { - if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd); - else LogOperation("%3d: Removing FD", req->sd); + if (req->errsd != req->sd) LogDebug("%3d: Removing FD and closing errsd %d", req->sd, req->errsd); + else LogDebug("%3d: Removing FD", req->sd); udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data); // Note: This also closes file descriptor req->sd for us if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; } @@ -1077,7 +1080,7 @@ mDNSlocal void connection_termination(request_state *request) mDNSlocal void handle_cancel_request(request_state *request) { request_state **req = &all_requests; - LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]); + LogDebug("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]); while (*req) { if ((*req)->primary == request && @@ -1689,7 +1692,7 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain &request->u.servicereg.name, &request->u.servicereg.type, domain, request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL, request->u.servicereg.port, - request->u.servicereg.txtdata, request->u.servicereg.txtlen, + mDNSNULL, request->u.servicereg.txtdata, request->u.servicereg.txtlen, instance->subtypes, request->u.servicereg.num_subtypes, interfaceID, regservice_callback, instance, request->flags); @@ -2166,7 +2169,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()"); + LogDebug("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); } } @@ -3162,7 +3165,7 @@ mDNSlocal mDNSBool RetryQuestionWithSearchDomains(DNSQuestion *question, request } else { - LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, AddRecord, question->SearchListIndex, question->AppendSearchDomains); + LogDebug("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, AddRecord, question->SearchListIndex, question->AppendSearchDomains); } return mDNSfalse; } @@ -3178,10 +3181,11 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu ConvertDomainNameToCString(answer->name, name); - LogOperation("%3d: %s(%##s, %s) RESULT %s interface %d: %s", req->sd, + LogOperation("%3d: %s(%##s, %s) RESULT %s interface %d: (%s)%s", req->sd, req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo", question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", - mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer)); + mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), + MortalityDisplayString(answer->mortality), RRDisplayString(m, answer)); len = sizeof(DNSServiceFlags); // calculate reply data length len += sizeof(mDNSu32); // interface index @@ -3195,6 +3199,8 @@ mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQu if (AddRecord) flags |= kDNSServiceFlagsAdd; + if (answer->mortality == Mortality_Ghost) + flags |= kDNSServiceFlagsExpiredAnswer; if (question->ValidationStatus != 0) { error = kDNSServiceErr_NoError; @@ -3452,7 +3458,7 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, // the "core" needs to temporarily turn off SuppressQuery to answer this query. if (AddRecord == QC_suppressed) { - LogInfo("queryrecord_result_callback: Suppressed question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + LogDebug("queryrecord_result_callback: Suppressed question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); queryrecord_result_reply(m, req, question, answer, AddRecord, kDNSServiceErr_NoSuchRecord); return; } @@ -3517,7 +3523,7 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, // appended .local, we need to see if we need to send an additional query. This should // normally happen just once because after we append .local, we ignore all negative // responses for .local above. - LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype)); + LogDebug("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype)); if (RetryQuestionWithSearchDomains(question, req, AddRecord)) { // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could @@ -3646,34 +3652,35 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) request->interfaceIndex = interfaceIndex; mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord)); - q->InterfaceID = InterfaceID; - q->flags = flags; - q->Target = zeroAddr; + q->InterfaceID = InterfaceID; + q->flags = flags; + q->Target = zeroAddr; if (!MakeDomainNameFromDNSNameString(&q->qname, name)) return(mStatus_BadParamErr); #if 0 if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains)) return (mStatus_NoError); #endif - q->qtype = rrtype; - q->qclass = rrclass; - q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - q->ExpectUnique = mDNSfalse; - q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; - q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; - q->WakeOnResolve = 0; + q->qtype = rrtype; + q->qclass = rrclass; + q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + q->ExpectUnique = mDNSfalse; + q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + q->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; + q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; + q->allowExpired = (EnableAllowExpired && (flags & kDNSServiceFlagsAllowExpiredAnswers) != 0) ? AllowExpired_AllowExpiredAnswers : AllowExpired_None; + q->WakeOnResolve = 0; q->UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; if ((flags & kDNSServiceFlagsValidate) != 0) q->ValidationRequired = DNSSEC_VALIDATION_SECURE; else if ((flags & kDNSServiceFlagsValidateOptional) != 0) q->ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL; q->ValidatingResponse = 0; - q->ProxyQuestion = 0; + q->ProxyQuestion = 0; q->AnonInfo = mDNSNULL; - q->QuestionCallback = queryrecord_result_callback; - q->QuestionContext = request; - q->SearchListIndex = 0; - q->StopTime = 0; + q->QuestionCallback = queryrecord_result_callback; + q->QuestionContext = request; + q->SearchListIndex = 0; + q->StopTime = 0; q->DNSSECAuthInfo = mDNSNULL; q->DAIFreeCallback = mDNSNULL; @@ -3737,7 +3744,7 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) LogMcastQ(q, request, q_start); if (callExternalHelpers(q->InterfaceID, &q->qname, q->flags)) { - LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()"); + LogDebug("handle_queryrecord_request: calling external_start_browsing_for_service()"); external_start_browsing_for_service(q->InterfaceID, &q->qname, q->qtype, q->flags); } } @@ -4258,7 +4265,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) 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"); + LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for A record"); external_stop_browsing_for_service(request->u.addrinfo.interface_id, &request->u.addrinfo.q4.qname, kDNSServiceType_A, request->flags); } } @@ -4293,7 +4300,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) 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"); + LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for AAAA record"); external_stop_browsing_for_service(request->u.addrinfo.interface_id, &request->u.addrinfo.q6.qname, kDNSServiceType_AAAA, request->flags); } } @@ -4423,19 +4430,20 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6); } - request->u.addrinfo.q4.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; - request->u.addrinfo.q4.ServiceID = request->u.addrinfo.q6.ServiceID = serviceIndex; - request->u.addrinfo.q4.flags = request->u.addrinfo.q6.flags = flags; - request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr; - request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d; - request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; - request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; - request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse; - request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; - request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; - request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; - 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.InterfaceID = request->u.addrinfo.q6.InterfaceID = request->u.addrinfo.interface_id; + request->u.addrinfo.q4.ServiceID = request->u.addrinfo.q6.ServiceID = serviceIndex; + request->u.addrinfo.q4.flags = request->u.addrinfo.q6.flags = flags; + request->u.addrinfo.q4.Target = request->u.addrinfo.q6.Target = zeroAddr; + request->u.addrinfo.q4.qname = request->u.addrinfo.q6.qname = d; + request->u.addrinfo.q4.qclass = request->u.addrinfo.q6.qclass = kDNSServiceClass_IN; + request->u.addrinfo.q4.LongLived = request->u.addrinfo.q6.LongLived = (flags & kDNSServiceFlagsLongLivedQuery ) != 0; + request->u.addrinfo.q4.ExpectUnique = request->u.addrinfo.q6.ExpectUnique = mDNSfalse; + request->u.addrinfo.q4.ForceMCast = request->u.addrinfo.q6.ForceMCast = (flags & kDNSServiceFlagsForceMulticast ) != 0; + request->u.addrinfo.q4.ReturnIntermed = request->u.addrinfo.q6.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; + request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; + request->u.addrinfo.q4.TimeoutQuestion = request->u.addrinfo.q6.TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; + request->u.addrinfo.q4.allowExpired = request->u.addrinfo.q6.allowExpired = (EnableAllowExpired && (flags & kDNSServiceFlagsAllowExpiredAnswers) != 0) ? AllowExpired_AllowExpiredAnswers : AllowExpired_None; + request->u.addrinfo.q4.WakeOnResolve = request->u.addrinfo.q6.WakeOnResolve = 0; request->u.addrinfo.q4.UseBackgroundTrafficClass = request->u.addrinfo.q6.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; if ((flags & kDNSServiceFlagsValidate) != 0) request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE; @@ -4497,7 +4505,7 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) LogMcastQ(&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"); + LogDebug("handle_addrinfo_request: calling external_start_browsing_for_service() for AAAA record"); external_start_browsing_for_service(InterfaceID, &d, kDNSServiceType_AAAA, flags); } } @@ -4539,7 +4547,7 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) if (callExternalHelpers(InterfaceID, &d, flags)) { - LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for kDNSServiceType_AAAA record"); + LogInfo("addrinfo_termination_callback: calling external_stop_browsing_for_service() for AAAA record"); external_stop_browsing_for_service(InterfaceID, &d, kDNSServiceType_AAAA, flags); } } @@ -4553,7 +4561,7 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) LogMcastQ(&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"); + LogDebug("handle_addrinfo_request: calling external_start_browsing_for_service() for A record"); external_start_browsing_for_service(InterfaceID, &d, kDNSServiceType_A, flags); } } @@ -4753,7 +4761,7 @@ mDNSlocal void read_msg(request_state *req) #if !defined(USE_TCP_LOOPBACK) got_errfd: #endif - 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]); + LogDebug("%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 @@ -4952,8 +4960,8 @@ 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: 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); + LogDebug("%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; // Also need to reset the parent's errsd, if this is a subordinate operation @@ -5024,7 +5032,7 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups); #endif // APPLE_OSX_mDNSResponder - LogOperation("%3d: connect_callback: Adding FD for uid %u", request->sd, request->uid); + LogDebug("%3d: connect_callback: Adding FD for uid %u", request->sd, request->uid); udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data); } } @@ -6254,10 +6262,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) <= 2954) ? 1 : -1]; + char sizecheck_request_state [(sizeof(request_state) <= 3696) ? 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) <= 1202) ? 1 : -1]; + char sizecheck_browser_t [(sizeof(browser_t) <= 1432) ? 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/mDNSResponder/unittests/mDNSCoreReceiveTest.c b/mDNSResponder/unittests/mDNSCoreReceiveTest.c index ece21407..856eb4ee 100644 --- a/mDNSResponder/unittests/mDNSCoreReceiveTest.c +++ b/mDNSResponder/unittests/mDNSCoreReceiveTest.c @@ -4,7 +4,6 @@ int InitmDNSCoreReceiveTest(void); int ValidQueryReqTest(void); int NullDstQueryReqTest(void); -int ReceiveArpLogMsgTest(void); void InitmDNSStorage(mDNS *const m); // This DNS message was gleaned from a uDNS query request packet that was captured with Wireshark. @@ -46,7 +45,6 @@ UNITTEST_HEADER(mDNSCoreReceiveTest) UNITTEST_TEST(InitmDNSCoreReceiveTest) UNITTEST_TEST(ValidQueryReqTest) UNITTEST_TEST(NullDstQueryReqTest) - UNITTEST_TEST(ReceiveArpLogMsgTest) UNITTEST_FOOTER UNITTEST_HEADER(InitmDNSCoreReceiveTest) @@ -56,16 +54,6 @@ UNITTEST_HEADER(InitmDNSCoreReceiveTest) mDNS_PacketLoggingEnabled = 0; UNITTEST_FOOTER -UNITTEST_HEADER(ReceiveArpLogMsgTest) - // Init unit test environment and verify no error occurred. - mStatus result = init_mdns_environment(mDNStrue); - UNITTEST_ASSERT(result == mStatus_NoError); - - UNITTEST_ASSERT(result == mStatus_NoError); - ArpLogMsgTest(&mDNSStorage, (const ARP_EthIP *) arp_request_packet, primary_interfaceID); - UNITTEST_ASSERT(result == mStatus_NoError); -UNITTEST_FOOTER - UNITTEST_HEADER(ValidQueryReqTest) mDNS *const m = &mDNSStorage; mDNSAddr srcaddr, dstaddr; @@ -73,9 +61,9 @@ UNITTEST_HEADER(ValidQueryReqTest) DNSMessage * msg; const mDNSu8 * end; - // This test case does not require setup of interfaces, the record's cache, or pending questions - // so m is initialized to all zeros. - InitmDNSStorage(m); + // Init unit test environment and verify no error occurred. + mStatus result = init_mdns_environment(mDNStrue); + UNITTEST_ASSERT(result == mStatus_NoError); // Used random values for srcaddr and srcport srcaddr.type = mDNSAddrType_IPv4; diff --git a/mDNSResponder/unittests/mdns_ut.c b/mDNSResponder/unittests/mdns_ut.c index 907cfa69..0384d2c1 100644 --- a/mDNSResponder/unittests/mdns_ut.c +++ b/mDNSResponder/unittests/mdns_ut.c @@ -6,12 +6,3 @@ mDNSexport mStatus mDNS_InitStorage_ut(mDNS *const m, mDNS_PlatformSupport *cons { return mDNS_InitStorage(m, p, rrcachestorage, rrcachesize, AdvertiseLocalAddresses, Callback, Context); } - -mDNSexport mStatus ArpLogMsgTest(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID) -{ - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); - static const char msg[] = "ARP Req message"; - LogMsg("Arp %-7s %s %.6a %.4a for %.4a", - intf->ifname, msg, arp->sha.b, arp->spa.b, arp->tpa.b); - return mStatus_NoError; -} diff --git a/mDNSResponder/unittests/unittest_common.h b/mDNSResponder/unittests/unittest_common.h index a4af2e67..4b62bb95 100644 --- a/mDNSResponder/unittests/unittest_common.h +++ b/mDNSResponder/unittests/unittest_common.h @@ -51,7 +51,5 @@ extern int LogEtcHosts_ut(mDNS *const m); extern mDNSBool mDNSMacOSXCreateEtcHostsEntry_ut(const domainname *domain, const struct sockaddr *sa, const domainname *cname, char *ifname, AuthHash *auth); extern void UpdateEtcHosts_ut(void *context); -extern mStatus ArpLogMsgTest(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID); - #endif /* UNITTEST_COMMON_H */ -- cgit v1.2.3