[rtems-libbsd commit] mDNSResponder: Update to v878.200.35
Sebastian Huber
sebh at rtems.org
Tue Jun 23 16:21:35 UTC 2020
Module: rtems-libbsd
Branch: master
Commit: a6b1d332b1d22fb47edd711a82e1857e42110b62
Changeset: http://git.rtems.org/rtems-libbsd/commit/?id=a6b1d332b1d22fb47edd711a82e1857e42110b62
Author: Sebastian Huber <sebastian.huber at embedded-brains.de>
Date: Thu Jun 18 13:07:18 2020 +0200
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(-)
diff --git a/mDNSResponder/Clients/dns-sd.c b/mDNSResponder/Clients/dns-sd.c
index 78c8f29..5260b3f 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 <dispatch/private.h>
-#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 <https://tools.ietf.org/html/rfc4648#section-4>.
+#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 1bde0da..206e2b6 100644
--- a/mDNSResponder/Clients/dnssdutil.c
+++ b/mDNSResponder/Clients/dnssdutil.c
@@ -6,10 +6,13 @@
#include <CoreUtils/CommonServices.h> // Include early.
#include <CoreUtils/AsyncConnection.h>
+#include <CoreUtils/CFUtils.h>
#include <CoreUtils/CommandLineUtils.h>
#include <CoreUtils/DataBufferUtils.h>
#include <CoreUtils/DebugServices.h>
#include <CoreUtils/HTTPUtils.h>
+#include <CoreUtils/JSONUtils.h>
+#include <CoreUtils/LogUtils.h>
#include <CoreUtils/MiscUtils.h>
#include <CoreUtils/NetUtils.h>
#include <CoreUtils/PrintFUtils.h>
@@ -20,10 +23,17 @@
#include <dns_sd.h>
#include <dns_sd_private.h>
+#include CF_RUNTIME_HEADER
+
#if( TARGET_OS_DARWIN )
+ #include <CFNetwork/CFHost.h>
+ #include <CoreFoundation/CoreFoundation.h>
+ #include <SystemConfiguration/SCPrivate.h>
#include <dnsinfo.h>
#include <libproc.h>
#include <netdb.h>
+ #include <pcap.h>
+ #include <spawn.h>
#include <sys/proc_info.h>
#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 <https://tools.ietf.org/html/rfc5737>.
+
+#define kTestDNSServerBaseAddrV4 UINT32_C( 0xCB007100 ) // 203.0.113.0
+
+// IPv6 address block 2001:db8::/32 is reserved for documentation. See <https://tools.ietf.org/html/rfc3849>.
+
+#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;
@@ -654,6 +782,36 @@ static CLIOption kBrowseAllOpts[] =
};
//===========================================================================================================================
+// 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
//===========================================================================================================================
@@ -782,6 +940,298 @@ static CLIOption kPIDToUUIDOpts[] =
};
//===========================================================================================================================
+// 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 <https://tools.ietf.org/html/rfc5737> and <https://tools.ietf.org/html/rfc3849>.\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;
@@ -849,6 +1304,78 @@ static CLIOption kResolvDNSQueryOpts[] =
};
//===========================================================================================================================
+// 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 : "<NULL> (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 );
}
@@ -4744,6 +5305,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-<N>". 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 <randCount>, inclusive.
+
+ for( i = 0; i < randCount; ++i ) randItegers[ i ] = (uint8_t)( i + 1 );
+
+ // Create a contiguous subarray starting at index 0 that contains <count> randomly chosen integers between
+ // 1 and <randCount>, 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
+ //
+ // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> 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
+ //
+ // <aliasCount> CNAME records, <addressCount> A records, and <addressCount> 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 : "<NULL>" );
- 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 : "<NULL>" );
+ 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
@@ -7964,6 +12294,18 @@ static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
}
//===========================================================================================================================
+// 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 );
@@ -8307,6 +12663,38 @@ exit:
}
//===========================================================================================================================
+// 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
//===========================================================================================================================
@@ -8369,15 +12757,58 @@ static const char * ServiceTypeDescription( const char *inName )
}
//===========================================================================================================================
+// 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 );
@@ -8459,6 +12999,20 @@ exit:
#endif
//===========================================================================================================================
+// GetCurrentMicroTime
+//===========================================================================================================================
+
+static MicroTime64 GetCurrentMicroTime( void )
+{
+ struct timeval now;
+
+ TIMEVAL_ZERO( now );
+ gettimeofday( &now, NULL );
+
+ return( (MicroTime64) TIMEVAL_USEC64( now ) );
+}
+
+//===========================================================================================================================
// SocketWriteAll
//
// Note: This was copied from CoreUtils because the SocketWriteAll function is currently not exported in the framework.
@@ -8919,47 +13473,6 @@ exit:
}
//===========================================================================================================================
-// 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
//
// Note: This was copied from CoreUtils because the StringArray_Free function is currently not exported in the framework.
@@ -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 978c895..d50841c 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 597c4cc..a249b96 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 <https://tools.ietf.org/html/rfc5952>.
+
+ 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<i; j++) *sbuffer++ = *s++; // Write the converted result
+ if (hexdump)
+ {
+ char *dst = sbuffer;
+ const char *const lim = &sbuffer[buflen - nwritten];
+ if (F.havePrecision)
+ {
+ for (i = 0; (i < F.precision) && (dst < lim); i++)
+ {
+ const unsigned int b = (unsigned int) *s++;
+ if (i > 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<i; j++) *sbuffer++ = *s++; // Write the converted result
+ }
nwritten += i;
if (nwritten >= buflen) goto exit;
diff --git a/mDNSResponder/mDNSCore/DNSCommon.h b/mDNSResponder/mDNSCore/DNSCommon.h
index e1ef261..b100a40 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 0788ab6..8deada2 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 68a696e..d690fd2 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 1962bb1..511aa3b 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 64dae89..cd91f4d 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 2848cda..6ad8e94 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 1f4e0ec..6fef38f 100644
--- a/mDNSResponder/mDNSMacOSX/DNS64.c
+++ b/mDNSResponder/mDNSMacOSX/DNS64.c
@@ -22,7 +22,13 @@
#include "DNS64.h"
#include <AssertMacros.h>
-#include <network/nat64.h>
+
+#if __has_include(<nw/private.h>)
+ #include <nw/private.h>
+#else
+ #include <network/nat64.h>
+#endif
+
#include <stdlib.h>
#include <string.h>
diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.c
index dd670ab..2b89bc2 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 <AvailabilityMacros.h>
-#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
-#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED
-#undef AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
-#define AVAILABLE_MAC_OS_X_VERSION_10_2_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_3
-
#include "../mDNSMacOSX/DNSServiceDiscovery.h"
#include "DNSServiceDiscoveryDefines.h"
diff --git a/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h b/mDNSResponder/mDNSMacOSX/DNSServiceDiscovery.h
index 004d325..3f683a8 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 <sys/cdefs.h>
#include <netinet/in.h>
+#include <os/availability.h>
-#include <AvailabilityMacros.h>
+#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 0000000..72352ce
--- /dev/null
+++ b/mDNSResponder/mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>DEFAULT-OPTIONS</key>
+ <dict>
+ <key>Level</key>
+ <dict>
+ <key>Persist</key>
+ <string>Info</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/mDNSResponder/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist b/mDNSResponder/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist
new file mode 100644
index 0000000..1eba185
--- /dev/null
+++ b/mDNSResponder/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>DEFAULT-OPTIONS</key>
+ <dict>
+ <key>Level</key>
+ <dict>
+ <key>Persist</key>
+ <string>Inherit</string>
+ <key>Enable</key>
+ <string>Inherit</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/mDNSResponder/mDNSMacOSX/Metrics.h b/mDNSResponder/mDNSMacOSX/Metrics.h
index ff419fd..e377229 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 e540f3b..dd20bc2 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 6d403b5..0000000
--- a/mDNSResponder/mDNSMacOSX/Private/com.apple.mDNSResponder.plist
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>DEFAULT-OPTIONS</key>
- <dict>
- <key>Default-Privacy-Setting</key>
- <string>Public</string>
- <key>Level</key>
- <dict>
- <key>Persist</key>
- <string>Inherit</string>
- <key>Enable</key>
- <string>Inherit</string>
- </dict>
- </dict>
-</dict>
-</plist>
-
diff --git a/mDNSResponder/mDNSMacOSX/Scripts/bonjour-mcast-diagnose b/mDNSResponder/mDNSMacOSX/Scripts/bonjour-mcast-diagnose
new file mode 100755
index 0000000..bda53ba
--- /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 0000000..2a81b5f
--- /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 0000000..0e48c21
--- /dev/null
+++ b/mDNSResponder/mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Label</key>
+ <string>com.apple.mDNSResponder.mdns-tcpdump</string>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/sbin/tcpdump</string>
+ <string>-w</string>
+ <string>/tmp/mdns-tcpdump.pcapng</string>
+ <string>-C</string>
+ <string>10</string>
+ <string>-W</string>
+ <string>16</string>
+ <string>( udp port 5353 ) or arp or icmp or icmp6</string>
+ </array>
+ <key>KeepAlive</key>
+ <false/>
+</dict>
+</plist>
diff --git a/mDNSResponder/mDNSMacOSX/daemon.c b/mDNSResponder/mDNSMacOSX/daemon.c
index fa192b6..e16f1db 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+1<argc && mDNSIsDigit(argv[i+1][0]) && argv[i+1][1]==0) ? atoi(argv[++i]) : 1;
if (!strcasecmp(argv[i], "-StrictUnicastOrdering" )) StrictUnicastOrdering = mDNStrue;
if (!strcasecmp(argv[i], "-AlwaysAppendSearchDomains")) AlwaysAppendSearchDomains = mDNStrue;
+ if (!strcasecmp(argv[i], "-DisableAllowExpired" )) EnableAllowExpired = mDNSfalse;
#if DEBUG
if (!strcasecmp(argv[i], "-UseDebugSocket")) useDebugSocket = mDNStrue;
if (!strcasecmp(argv[i], "-NoSandbox")) useSandbox = mDNSfalse;
@@ -1568,6 +1573,7 @@ mDNSexport int main(int argc, char **argv)
NoMulticastAdvertisements = PreferencesGetValueBool(kPreferencesKey_NoMulticastAdvertisements, NoMulticastAdvertisements);
StrictUnicastOrdering = PreferencesGetValueBool(kPreferencesKey_StrictUnicastOrdering, StrictUnicastOrdering);
AlwaysAppendSearchDomains = PreferencesGetValueBool(kPreferencesKey_AlwaysAppendSearchDomains, AlwaysAppendSearchDomains);
+ EnableAllowExpired = PreferencesGetValueBool(kPreferencesKey_EnableAllowExpired, EnableAllowExpired);
OfferSleepProxyService = PreferencesGetValueInt(kPreferencesKey_OfferSleepProxyService, OfferSleepProxyService);
UseInternalSleepProxy = PreferencesGetValueInt(kPreferencesKey_UseInternalSleepProxy, UseInternalSleepProxy);
diff --git a/mDNSResponder/mDNSMacOSX/helper-stubs.c b/mDNSResponder/mDNSMacOSX/helper-stubs.c
index a1e804f..c7c3496 100644
--- a/mDNSResponder/mDNSMacOSX/helper-stubs.c
+++ b/mDNSResponder/mDNSMacOSX/helper-stubs.c
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2007-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -55,10 +55,10 @@ static int64_t maxwait_secs = 5LL;
//*************************************************************************************************************
// Utility Functions
-static void LogDebug(const char *prefix, xpc_object_t o)
+static void HelperLog(const char *prefix, xpc_object_t o)
{
char *desc = xpc_copy_description(o);
- mDNSHELPER_DEBUG("LogDebug %s: %s", prefix, desc);
+ mDNSHELPER_DEBUG("HelperLog %s: %s", prefix, desc);
free(desc);
}
@@ -83,7 +83,7 @@ mDNSlocal int SendDict_ToServer(xpc_object_t msg)
{
__block int errorcode = kHelperErr_NoResponse;
- LogDebug("SendDict_ToServer Sending msg to Daemon", msg);
+ HelperLog("SendDict_ToServer Sending msg to Daemon", msg);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_retain(sem); // for the block below
@@ -94,7 +94,7 @@ mDNSlocal int SendDict_ToServer(xpc_object_t msg)
if (type == XPC_TYPE_DICTIONARY)
{
- LogDebug("SendDict_ToServer Received reply msg from Daemon", recv_msg);
+ HelperLog("SendDict_ToServer Received reply msg from Daemon", recv_msg);
uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus);
errorcode = xpc_dictionary_get_int64(recv_msg, kHelperErrCode);
@@ -112,7 +112,7 @@ mDNSlocal int SendDict_ToServer(xpc_object_t msg)
{
LogMsg("SendDict_ToServer Received unexpected reply from daemon [%s]",
xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION));
- LogDebug("SendDict_ToServer Unexpected Reply contents", recv_msg);
+ HelperLog("SendDict_ToServer Unexpected Reply contents", recv_msg);
}
dispatch_semaphore_signal(sem);
@@ -137,7 +137,7 @@ mDNSlocal xpc_object_t SendDict_GetReply(xpc_object_t msg)
if (!dict) return NULL;
xpc_retain(dict);
- LogDebug("SendDict_GetReply Sending msg to Daemon", msg);
+ HelperLog("SendDict_GetReply Sending msg to Daemon", msg);
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_retain(sem); // for the block below
@@ -148,7 +148,7 @@ mDNSlocal xpc_object_t SendDict_GetReply(xpc_object_t msg)
if (type == XPC_TYPE_DICTIONARY)
{
- LogDebug("SendDict_GetReply Received reply msg from Daemon", recv_msg);
+ HelperLog("SendDict_GetReply Received reply msg from Daemon", recv_msg);
uint64_t reply_status = xpc_dictionary_get_uint64(recv_msg, kHelperReplyStatus);
switch (reply_status)
@@ -171,7 +171,7 @@ mDNSlocal xpc_object_t SendDict_GetReply(xpc_object_t msg)
{
LogMsg("SendDict_GetReply Received unexpected reply from daemon [%s]",
xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION));
- LogDebug("SendDict_GetReply Unexpected Reply contents", recv_msg);
+ HelperLog("SendDict_GetReply Unexpected Reply contents", recv_msg);
}
dispatch_semaphore_signal(sem);
diff --git a/mDNSResponder/mDNSMacOSX/helper.c b/mDNSResponder/mDNSMacOSX/helper.c
index dc90b35..7b6cb89 100644
--- a/mDNSResponder/mDNSMacOSX/helper.c
+++ b/mDNSResponder/mDNSMacOSX/helper.c
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2007-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2007-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -710,7 +710,12 @@ fin:
enum DNSKeyFormat
{
- formatNotDNSKey, formatDdnsTypeItem, formatDnsPrefixedServiceItem, formatBtmmPrefixedServiceItem
+ formatNotDNSKey,
+ formatDdnsTypeItem,
+ formatDnsPrefixedServiceItem,
+#if MDNSRESPONDER_BTMM_SUPPORT
+ formatBtmmPrefixedServiceItem
+#endif
};
// On Mac OS X on Intel, the four-character string seems to be stored backwards, at least sometimes.
@@ -720,7 +725,9 @@ enum DNSKeyFormat
#ifndef NO_SECURITYFRAMEWORK
+#if MDNSRESPONDER_BTMM_SUPPORT
static const char btmmprefix[] = "btmmdns:";
+#endif
static const char dnsprefix[] = "dns:";
static const char ddns[] = "ddns";
static const char ddnsrev[] = "sndd";
@@ -778,8 +785,10 @@ static enum DNSKeyFormat getDNSKeyFormat(SecKeychainItemRef item, SecKeychainAtt
}
if (attributes->attr[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 3bb4ec6..f64e28a 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 <SystemConfiguration/SCPrivate.h>
#if TARGET_OS_IPHONE
-// For WiFiManagerClientRef etc, declarations.
-#include <MobileGestalt.h>
-#include <MobileWiFi/WiFiManagerClient.h>
+#include <MobileWiFi/WiFiManagerClient.h> // For WiFiManagerClientRef etc, declarations.
#include <dlfcn.h>
+#include <os/variant_private.h> // For os_variant_has_internal_diagnostics().
#endif // TARGET_OS_IPHONE
// Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic.
#include <Kernel/IOKit/apple80211/apple80211_var.h>
#include <network_information.h> // for nwi_state
-#if APPLE_OSX_mDNSResponder
+#if MDNSRESPONDER_BTMM_SUPPORT
#include <AWACS.h>
+#endif
+
+#if APPLE_OSX_mDNSResponder
#include <ne_session.h> // for ne_session_set_socket_attributes()
-#else
-#define NO_AWACS 1
-#endif // APPLE_OSX_mDNSResponder
+#endif
#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED
#include <IOKit/platform/IOPlatformSupportPrivate.h>
@@ -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 <rdar://problem/3409090>)
@@ -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 55c74c6..f189b07 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 1458815..2918631 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 f1a8b1d..bbbd12f 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 = "<group>"; tabWidth = 4; usesTabs = 0; };
6575FBEB022EAF7200000109 /* mDNSMacOSX.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = mDNSMacOSX.c; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
6575FBEC022EAF7200000109 /* daemon.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = daemon.c; sourceTree = "<group>"; 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 = "<group>"; };
72FB545A166D5F960090B2D9 /* dnsctl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dnsctl.c; path = ../Clients/dnsctl.c; sourceTree = "<group>"; };
72FB545F166D5FB00090B2D9 /* dnsctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnsctl; sourceTree = BUILT_PRODUCTS_DIR; };
+ 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 = "<group>"; };
BD03E88C1AD31278005E8A81 /* SymptomReporter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SymptomReporter.c; sourceTree = "<group>"; };
+ BD28AE8E207B88F600F0B257 /* bonjour-mcast-diagnose */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = "bonjour-mcast-diagnose"; sourceTree = "<group>"; };
+ 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 = "<group>"; };
BD691B291ED2F43200E6F317 /* DNS64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNS64.h; sourceTree = "<group>"; };
+ 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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
+ 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 = "<group>"; };
+ BDB61846206ADDDF00AFF600 /* com.apple.mDNSResponder.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.apple.mDNSResponder.plist; sourceTree = "<group>"; };
BDBF9B931ED74B8C001498A8 /* DNS64State.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNS64State.h; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>";
@@ -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 = "<group>";
};
+ BD28AE8D207B88F600F0B257 /* Scripts */ = {
+ isa = PBXGroup;
+ children = (
+ BD28AE8E207B88F600F0B257 /* bonjour-mcast-diagnose */,
+ );
+ path = Scripts;
+ sourceTree = "<group>";
+ };
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 = "<group>";
};
+ BDB61842206ADB7700AFF600 /* LoggingProfiles */ = {
+ isa = PBXGroup;
+ children = (
+ BDB61843206ADB7700AFF600 /* com.apple.mDNSResponder.plist */,
+ BDB61844206ADB7700AFF600 /* AppleInternal */,
+ );
+ path = LoggingProfiles;
+ sourceTree = "<group>";
+ };
+ BDB61844206ADB7700AFF600 /* AppleInternal */ = {
+ isa = PBXGroup;
+ children = (
+ BDB61846206ADDDF00AFF600 /* com.apple.mDNSResponder.plist */,
+ );
+ path = AppleInternal;
+ sourceTree = "<group>";
+ };
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 96d4a0d..e474473 100644
--- a/mDNSResponder/mDNSMacOSX/uDNSPathEvalulation.c
+++ b/mDNSResponder/mDNSMacOSX/uDNSPathEvalulation.c
@@ -17,7 +17,13 @@
#include "mDNSMacOSX.h"
#include <libproc.h>
-#include <network/private.h>
+
+#if __has_include(<nw/private.h>)
+ #include <nw/private.h>
+#else
+ #include <network/private.h>
+#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 bd0fe58..94fde85 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 3850ea2..d4e2010 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 17a37c6..e00ddd6 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 <https://www.tldp.org/HOWTO/Linux+IPv6-HOWTO/ch11s04.html>.
+
+ // Create a string specifier with a width of IFNAMSIZ - 1 ("%<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 cc81b7d..2b36ceb 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 <net/if.h> */
-#define IFI_HADDR 8 /* allow for 64-bit EUI-64 in future */
+#define IFI_NAME IFNAMSIZ /* same as IFNAMSIZ in <net/if.h> */
+#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 0e5c35e..a1f5a57 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 <winsock2.h>
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 4d02368..2d42973 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 <dns_sd.h>
// 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 cfc1d42..2a1f5ed 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 <data> 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 27e90ee..e8600cb 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 1243ae7..dfe77a1 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 946ad16..d1fbc7f 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 <sys/ucred.h>
@@ -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 ece2140..856eb4e 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 907cfa6..0384d2c 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 a4af2e6..4b62bb9 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 */
More information about the vc
mailing list