]> git.saurik.com Git - apple/mdnsresponder.git/commitdiff
mDNSResponder-878.200.35.tar.gz macos-1014 macos-10141 v878.200.35
authorApple <opensource@apple.com>
Thu, 4 Oct 2018 22:34:30 +0000 (22:34 +0000)
committerApple <opensource@apple.com>
Thu, 4 Oct 2018 22:34:30 +0000 (22:34 +0000)
42 files changed:
Clients/dns-sd.c
Clients/dnssdutil.c
Makefile
mDNSCore/DNSCommon.c
mDNSCore/DNSCommon.h
mDNSCore/mDNS.c
mDNSCore/mDNSDebug.h
mDNSCore/mDNSEmbeddedAPI.h
mDNSCore/uDNS.c
mDNSMacOSX/D2D.c
mDNSMacOSX/DNS64.c
mDNSMacOSX/DNSServiceDiscovery.c
mDNSMacOSX/DNSServiceDiscovery.h
mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist [new file with mode: 0644]
mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist [new file with mode: 0644]
mDNSMacOSX/Metrics.h
mDNSMacOSX/Metrics.m
mDNSMacOSX/Private/com.apple.mDNSResponder.plist [deleted file]
mDNSMacOSX/Scripts/bonjour-mcast-diagnose [new file with mode: 0755]
mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump [new file with mode: 0755]
mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist [new file with mode: 0644]
mDNSMacOSX/daemon.c
mDNSMacOSX/helper-stubs.c
mDNSMacOSX/helper.c
mDNSMacOSX/mDNSMacOSX.c
mDNSMacOSX/mDNSMacOSX.h
mDNSMacOSX/mDNSResponder.sb
mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj
mDNSMacOSX/uDNSPathEvalulation.c
mDNSPosix/ProxyResponder.c
mDNSPosix/Responder.c
mDNSPosix/mDNSUNP.c
mDNSPosix/mDNSUNP.h
mDNSShared/dns_sd.h
mDNSShared/dns_sd_private.h
mDNSShared/dnssd_clientlib.c
mDNSShared/dnssd_clientstub.c
mDNSShared/mDNSDebug.c
mDNSShared/uds_daemon.c
unittests/mDNSCoreReceiveTest.c
unittests/mdns_ut.c
unittests/unittest_common.h

index 78c8f29fb4531df0308743accc7999b7463caa28..5260b3f29b5ac873ca7effd526bf9aa886df2e8a 100644 (file)
@@ -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--;
index 1bde0daf27f13ce1871924f3213c36b68047ca38..206e2b6c314ee93018f0f1968014eb0d35046dcf 100644 (file)
@@ -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>
 #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
 
 #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            \
        "\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
 #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
 #define StringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED ) \
        StringOptionEx( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED, NULL )
 
+#define CFStringOption( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP, SHORT_HELP, IS_REQUIRED )    \
+       CLI_OPTION_CFSTRING_EX( SHORT_CHAR, LONG_NAME, VAL_PTR, ARG_HELP,                                               \
+               (IS_REQUIRED) ? SHORT_HELP kRequiredOptionSuffix : SHORT_HELP,                                          \
+               (IS_REQUIRED) ? kCLIOptionFlags_Required : kCLIOptionFlags_None, NULL )
+
 // DNS-SD API flag options
 
 static int             gDNSSDFlags                                             = 0;
@@ -634,7 +763,6 @@ static CLIOption            kPortMappingOpts[] =
 static const char *            gBrowseAll_Domain                               = NULL;
 static char **                 gBrowseAll_ServiceTypes                 = NULL;
 static size_t                  gBrowseAll_ServiceTypesCount    = 0;
-static int                             gBrowseAll_IncludeAWDL                  = false;
 static int                             gBrowseAll_BrowseTimeSecs               = 5;
 static int                             gBrowseAll_MaxConnectTimeSecs   = 0;
 
@@ -653,6 +781,36 @@ static CLIOption           kBrowseAllOpts[] =
        CLI_OPTION_END()
 };
 
+//===========================================================================================================================
+//     GetNameInfo Command Options
+//===========================================================================================================================
+
+static void    GetNameInfoCmd( void );
+
+static char *          gGetNameInfo_IPAddress                  = NULL;
+static int                     gGetNameInfoFlag_DGram                  = false;
+static int                     gGetNameInfoFlag_NameReqd               = false;
+static int                     gGetNameInfoFlag_NoFQDN                 = false;
+static int                     gGetNameInfoFlag_NumericHost    = false;
+static int                     gGetNameInfoFlag_NumericScope   = false;
+static int                     gGetNameInfoFlag_NumericServ    = false;
+
+static CLIOption               kGetNameInfoOpts[] =
+{
+       StringOption( 'a', "address",           &gGetNameInfo_IPAddress,        "IP address", "IPv4 or IPv6 address to use in sockaddr structure.", true ),
+       
+       CLI_OPTION_GROUP( "Flags" ),
+       BooleanOption( 0 , "flag-dgram",        &gGetNameInfoFlag_DGram,        "Use NI_DGRAM flag." ),
+       BooleanOption( 0 , "flag-namereqd",     &gGetNameInfoFlag_NameReqd,     "Use NI_NAMEREQD flag." ),
+       BooleanOption( 0 , "flag-nofqdn",       &gGetNameInfoFlag_NoFQDN,       "Use NI_NOFQDN flag." ),
+       BooleanOption( 0 , "flag-numerichost",  &gGetNameInfoFlag_NumericHost,  "Use NI_NUMERICHOST flag." ),
+       BooleanOption( 0 , "flag-numericscope", &gGetNameInfoFlag_NumericScope, "Use NI_NUMERICSCOPE flag." ),
+       BooleanOption( 0 , "flag-numericserv",  &gGetNameInfoFlag_NumericServ,  "Use NI_NUMERICSERV flag." ),
+       
+       CLI_SECTION( "Notes", "See getnameinfo(3) man page for more details.\n" ),
+       CLI_OPTION_END()
+};
+
 //===========================================================================================================================
 //     GetAddrInfoStress Command Options
 //===========================================================================================================================
@@ -781,6 +939,298 @@ static CLIOption          kPIDToUUIDOpts[] =
        CLI_OPTION_END()
 };
 
+//===========================================================================================================================
+//     DNSServer Command Options
+//===========================================================================================================================
+
+#define kDNSServerInfoText_Intro                                                                                                                                                                               \
+       "The DNS server answers certain queries in the d.test. domain. Responses are dynamically generated based on the\n"      \
+       "presence of special labels in the query's QNAME. There are currently seven types of special labels that can be\n"      \
+       "used to generate specific responses: Alias labels, Alias-TTL labels, Count labels, Tag labels, TTL labels, the\n"      \
+       "IPv4 label, and the IPv6 label.\n"
+
+#define kDNSServerInfoText_NameExistence                                                                                                                                                               \
+       "A name is considered to exist if and only if it ends in d.test., and the other labels, if\n"                                           \
+       "any, consist of\n"                                                                                                                                                                                                     \
+       "\n"                                                                                                                                                                                                                            \
+       "    1. at most one Alias or Alias-TTL label as the first label;\n"                                                                                                     \
+       "    2. at most one Count label;\n"                                                                                                                                                                     \
+       "    3. zero or more Tag labels;\n"                                                                                                                                                             \
+       "    4. at most one TTL label; and\n"                                                                                                                                                           \
+       "    5. at most one IPv4 or IPv6 label.\n"
+
+#define kDNSServerInfoText_ResourceRecords                                                                                                                                                             \
+       "Currently, the server only provides CNAME, A, and AAAA records.\n"                                                                                                     \
+       "\n"                                                                                                                                                                                                                            \
+       "Names that exist and begin with an Alias or Alias-TTL label are aliases of canonical names, i.e., they're the\n"       \
+       "names of CNAME records. See \"Alias Labels\" and \"Alias-TTL Labels\" for details.\n"                                                          \
+       "\n"                                                                                                                                                                                                                            \
+       "Names that exist and have an IPv4 label have at least one A record, but no AAAA records. Names that exist and\n"       \
+       "have an IPv6 label, have at least one AAAA record, but no A records. All other names that exist have at least\n"       \
+       "one A record and at least one AAAA record. See \"Count Labels\" for how the number of address records for a\n"         \
+       "given name is determined.\n"                                                                                                                                                                           \
+       "\n"                                                                                                                                                                                                                            \
+       "A records contain IPv4 addresses in the 203.0.113.0/24 block, while AAAA records contain IPv6 addresses in the\n"      \
+       "2001:db8::/32 block. Both of these address blocks are reserved for documentation.\n"                                                           \
+       "See <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;
@@ -848,6 +1303,78 @@ static CLIOption          kResolvDNSQueryOpts[] =
        CLI_OPTION_END()
 };
 
+//===========================================================================================================================
+//     CFHost Command Options
+//===========================================================================================================================
+
+static void    CFHostCmd( void );
+
+static const char *            gCFHost_Name            = NULL;
+static int                             gCFHost_WaitSecs        = 0;
+
+static CLIOption               kCFHostOpts[] =
+{
+       StringOption(  'n', "name", &gCFHost_Name,     "hostname", "Hostname to resolve.", true ),
+       IntegerOption( 'w', "wait", &gCFHost_WaitSecs, "seconds",  "Time in seconds to wait before a normal exit. (default: 0)", false ),
+       CLI_OPTION_END()
+};
+
+static CLIOption               kLegacyOpts[] =
+{
+       Command( "res_query", ResQueryCmd,       kResQueryOpts,       "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
+       Command( "dns_query", ResolvDNSQueryCmd, kResolvDNSQueryOpts, "Uses dns_query() from libresolv to query for a record.", true ),
+       Command( "cfhost",    CFHostCmd,         kCFHostOpts,         "Uses CFHost to resolve a hostname.", true ),
+       CLI_OPTION_END()
+};
+
+//===========================================================================================================================
+//     DNSConfigAdd Command Options
+//===========================================================================================================================
+
+static void    DNSConfigAddCmd( void );
+
+static CFStringRef             gDNSConfigAdd_ID                        = NULL;
+static char **                 gDNSConfigAdd_IPAddrArray       = NULL;
+static size_t                  gDNSConfigAdd_IPAddrCount       = 0;
+static char **                 gDNSConfigAdd_DomainArray       = NULL;
+static size_t                  gDNSConfigAdd_DomainCount       = 0;
+static const char *            gDNSConfigAdd_Interface         = NULL;
+
+static CLIOption               kDNSConfigAddOpts[] =
+{
+       CFStringOption(     0 , "id",        &gDNSConfigAdd_ID,                                      "ID", "Arbitrary ID to use for resolver entry.", true ),
+       MultiStringOption( 'a', "address",   &gDNSConfigAdd_IPAddrArray, &gDNSConfigAdd_IPAddrCount, "IP address", "DNS server IP address(es). Can be specified more than once.", true ),
+       MultiStringOption( 'd', "domain",    &gDNSConfigAdd_DomainArray, &gDNSConfigAdd_DomainCount, "domain", "Specific domain(s) for the resolver entry. Can be specified more than once.", false ),
+       StringOption(      'i', "interface", &gDNSConfigAdd_Interface,                               "interface name", "Specific interface for the resolver entry.", false ),
+       
+       CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
+       CLI_OPTION_END()
+};
+
+//===========================================================================================================================
+//     DNSConfigRemove Command Options
+//===========================================================================================================================
+
+static void    DNSConfigRemoveCmd( void );
+
+static CFStringRef             gDNSConfigRemove_ID = NULL;
+
+static CLIOption               kDNSConfigRemoveOpts[] =
+{
+       CFStringOption( 0, "id", &gDNSConfigRemove_ID, "ID", "ID of resolver entry to remove.", true ),
+       
+       CLI_SECTION( "Notes", "Run 'scutil -d -v --dns' to see the current DNS configuration. See scutil(8) man page for more details.\n" ),
+       CLI_OPTION_END()
+};
+
+static CLIOption               kDNSConfigOpts[] =
+{
+       Command( "add",    DNSConfigAddCmd,    kDNSConfigAddOpts,    "Add a supplemental resolver entry to the system's DNS configuration.", true ),
+       Command( "remove", DNSConfigRemoveCmd, kDNSConfigRemoveOpts, "Remove a supplemental resolver entry from the system's DNS configuration.", true ),
+       CLI_OPTION_END()
+};
+#endif // TARGET_OS_DARWIN
+
 //===========================================================================================================================
 //     Command Table
 //===========================================================================================================================
@@ -872,10 +1399,6 @@ static void       DNSCryptCmd( void );
 #endif
 static void    MDNSQueryCmd( void );
 static void    PIDToUUIDCmd( void );
-#if( TARGET_OS_DARWIN )
-static void    ResQueryCmd( void );
-static void    ResolvDNSQueryCmd( void );
-#endif
 static void    DaemonVersionCmd( void );
 
 static CLIOption               kGlobalOpts[] =
@@ -900,6 +1423,7 @@ static CLIOption           kGlobalOpts[] =
        
        // Uncommon commands.
        
+       Command( "getnameinfo",                 GetNameInfoCmd,                 kGetNameInfoOpts,               "Calls getnameinfo() and prints results.", true ),
        Command( "getAddrInfoStress",   GetAddrInfoStressCmd,   kGetAddrInfoStressOpts, "Runs DNSServiceGetAddrInfo() stress testing.", true ),
        Command( "DNSQuery",                    DNSQueryCmd,                    kDNSQueryOpts,                  "Crafts and sends a DNS query.", true ),
 #if( DNSSDUTIL_INCLUDE_DNSCRYPT )
@@ -907,10 +1431,12 @@ static CLIOption         kGlobalOpts[] =
 #endif
        Command( "mDNSQuery",                   MDNSQueryCmd,                   kMDNSQueryOpts,                 "Crafts and sends an mDNS query over the specified interface.", true ),
        Command( "pid2uuid",                    PIDToUUIDCmd,                   kPIDToUUIDOpts,                 "Prints the UUID of a process.", true ),
-       Command( "ssdp",                                NULL,                                   kSSDPOpts,                              "Commands for testing with Simple Service Discovery Protocol (SSDP).", true ),
+       Command( "server",                              DNSServerCmd,                   kDNSServerOpts,                 "DNS server for testing.", true ),
+       Command( "test",                                NULL,                                   kTestOpts,                              "Commands for testing DNS-SD.", true ),
+       Command( "ssdp",                                NULL,                                   kSSDPOpts,                              "Commands for testing Simple Service Discovery Protocol (SSDP).", true ),
 #if( TARGET_OS_DARWIN )
-       Command( "res_query",                   ResQueryCmd,                    kResQueryOpts,                  "Uses res_query() from either libresolv or libinfo to query for a record.", true ),
-       Command( "dns_query",                   ResolvDNSQueryCmd,              kResolvDNSQueryOpts,    "Uses dns_query() from libresolv to query for a record.", true ),
+       Command( "legacy",                              NULL,                                   kLegacyOpts,                    "Commands for legacy non-DNS-SD API.", true ),
+       Command( "dnsconfig",                   NULL,                                   kDNSConfigOpts,                 "Add/remove a supplemental resolver entry to/from the system's DNS configuration.", true ),
 #endif
        Command( "daemonVersion",               DaemonVersionCmd,               NULL,                                   "Prints the version of the DNS-SD daemon.", true ),
        
@@ -930,9 +1456,19 @@ static CLIOption          kGlobalOpts[] =
 
 static void    Exit( void *inContext ) ATTRIBUTE_NORETURN;
 
-#define kTimestampBufLen               27
+static int
+       PrintFTimestampHandler(
+               PrintFContext * inContext,
+               PrintFFormat *  inFormat,
+               PrintFVAList *  inArgs,
+               void *                  inUserContext );
+static int
+       PrintFDNSMessageHandler(
+               PrintFContext * inContext,
+               PrintFFormat *  inFormat,
+               PrintFVAList *  inArgs,
+               void *                  inUserContext );
 
-static char *                  GetTimestampStr( char inBuffer[ kTimestampBufLen ] );
 static DNSServiceFlags GetDNSSDFlagsFromOpts( void );
 
 typedef enum
@@ -972,29 +1508,6 @@ static OSStatus                   RecordClassFromArgString( const char *inString, uint16_t *outV
 static char *                  InterfaceIndexToName( uint32_t inIfIndex, char inNameBuf[ kInterfaceNameBufLen ] );
 static const char *            RecordTypeToString( unsigned int inValue );
 
-// DNS message helpers
-
-typedef struct
-{
-       uint8_t         id[ 2 ];
-       uint8_t         flags[ 2 ];
-       uint8_t         questionCount[ 2 ];
-       uint8_t         answerCount[ 2 ];
-       uint8_t         authorityCount[ 2 ];
-       uint8_t         additionalCount[ 2 ];
-       
-}      DNSHeader;
-
-#define kDNSHeaderLength               12
-check_compile_time( sizeof( DNSHeader ) == kDNSHeaderLength );
-
-#define DNSHeaderGetID( HDR )                                  ReadBig16( ( HDR )->id )
-#define DNSHeaderGetFlags( HDR )                               ReadBig16( ( HDR )->flags )
-#define DNSHeaderGetQuestionCount( HDR )               ReadBig16( ( HDR )->questionCount )
-#define DNSHeaderGetAnswerCount( HDR )                 ReadBig16( ( HDR )->answerCount )
-#define DNSHeaderGetAuthorityCount( HDR )              ReadBig16( ( HDR )->authorityCount )
-#define DNSHeaderGetAdditionalCount( HDR )             ReadBig16( ( HDR )->additionalCount )
-
 static OSStatus
        DNSMessageExtractDomainName(
                const uint8_t *         inMsgPtr,
@@ -1036,6 +1549,7 @@ static OSStatus
                const char *    inString,
                uint8_t **              outEndPtr );
 static Boolean DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 );
+static size_t  DomainNameLength( const uint8_t *inName );
 static OSStatus
        DomainNameFromString(
                uint8_t                 inDomainName[ kDomainNameLengthMax ],
@@ -1048,10 +1562,13 @@ static OSStatus
                char                            inBuf[ kDNSServiceMaxDomainName ],
                const uint8_t **        outNextPtr );
 
-static OSStatus        PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, Boolean inIsMDNS, Boolean inPrintRaw );
-
-#define PrintMDNSMessage( MSGPTR, MSGLEN, RAW )                PrintDNSMessage( MSGPTR, MSGLEN, true, RAW )
-#define PrintUDNSMessage( MSGPTR, MSGLEN, RAW )                PrintDNSMessage( MSGPTR, MSGLEN, false, RAW )
+static OSStatus
+       DNSMessageToText(
+               const uint8_t * inMsgPtr,
+               size_t                  inMsgLen,
+               Boolean                 inIsMDNS,
+               Boolean                 inPrintRaw,
+               char **                 outText );
 
 #define kDNSQueryMessageMaxLen         ( kDNSHeaderLength + kDomainNameLengthMax + 4 )
 
@@ -1076,37 +1593,82 @@ static OSStatus
                void *                          inContext,
                dispatch_source_t *     outSource );
 static OSStatus
-       DispatchReadSourceCreate(
-               SocketRef                       inSock,
-               DispatchHandler         inEventHandler,
-               DispatchHandler         inCancelHandler,
-               void *                          inContext,
-               dispatch_source_t *     outSource );
+       DispatchSocketSourceCreate(
+               SocketRef                               inSock,
+               dispatch_source_type_t  inType,
+               dispatch_queue_t                inQueue,
+               DispatchHandler                 inEventHandler,
+               DispatchHandler                 inCancelHandler,
+               void *                                  inContext,
+               dispatch_source_t *             outSource );
+
+#define DispatchReadSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
+       DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_READ, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
+
+#define DispatchWriteSourceCreate( SOCK, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE ) \
+       DispatchSocketSourceCreate( SOCK, DISPATCH_SOURCE_TYPE_WRITE, QUEUE, EVENT_HANDLER, CANCEL_HANDLER, CONTEXT, OUT_SOURCE )
+
 static OSStatus
        DispatchTimerCreate(
                dispatch_time_t         inStart,
                uint64_t                        inIntervalNs,
                uint64_t                        inLeewayNs,
+               dispatch_queue_t        inQueue,
                DispatchHandler         inEventHandler,
                DispatchHandler         inCancelHandler,
                void *                          inContext,
                dispatch_source_t *     outTimer );
+static OSStatus
+       DispatchProcessMonitorCreate(
+               pid_t                           inPID,
+               unsigned long           inFlags,
+               dispatch_queue_t        inQueue,
+               DispatchHandler         inEventHandler,
+               DispatchHandler         inCancelHandler,
+               void *                          inContext,
+               dispatch_source_t *     outMonitor );
 
 static const char *    ServiceTypeDescription( const char *inName );
 
 typedef struct
 {
-       SocketRef               sock;
-       void *                  context;
+       SocketRef               sock;                   // Socket.
+       void *                  userContext;    // User context.
+       int32_t                 refCount;               // Reference count.
        
 }      SocketContext;
 
-static void                    SocketContextCancelHandler( void *inContext );
+static OSStatus                        SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext );
+static SocketContext * SocketContextRetain( SocketContext *inContext );
+static void                            SocketContextRelease( SocketContext *inContext );
+static void                            SocketContextCancelHandler( void *inContext );
+
+#define ForgetSocketContext( X )       ForgetCustom( X, SocketContextRelease )
+
 static OSStatus                StringToInt32( const char *inString, int32_t *outValue );
 static OSStatus                StringToUInt32( const char *inString, uint32_t *outValue );
+static OSStatus                StringToLongLong( const char *inString, long long *outValue );
+static OSStatus                StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
+static OSStatus                StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen );
+static OSStatus                StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen );
 #if( TARGET_OS_DARWIN )
 static OSStatus                GetDefaultDNSServer( sockaddr_ip *outAddr );
 #endif
+static OSStatus
+       _ServerSocketOpenEx2( 
+               int                             inFamily, 
+               int                             inType, 
+               int                             inProtocol, 
+               const void *    inAddr, 
+               int                             inPort, 
+               int *                   outPort, 
+               int                             inRcvBufSize, 
+               Boolean                 inNoPortReuse,
+               SocketRef *             outSock );
+
+typedef uint64_t               MicroTime64;
+
+static MicroTime64     GetCurrentMicroTime( void );    // Gets the number of milliseconds since 1970-01-01T00:00:00Z
 
 #define AddRmvString( X )              ( ( (X) & kDNSServiceFlagsAdd ) ? "Add" : "Rmv" )
 #define Unused( X )                            (void)(X)
@@ -1117,12 +1679,17 @@ static OSStatus         GetDefaultDNSServer( sockaddr_ip *outAddr );
 
 int    main( int argc, const char **argv )
 {
+       OSStatus                err;
+       
        // Route DebugServices logging output to stderr.
        
        dlog_control( "DebugServices:output=file;stderr" );
        
+       PrintFRegisterExtension( "du:time",   PrintFTimestampHandler,  NULL );
+       PrintFRegisterExtension( "du:dnsmsg", PrintFDNSMessageHandler, NULL );
        CLIInit( argc, argv );
-       CLIParse( kGlobalOpts, kCLIFlags_None );
+       err = CLIParse( kGlobalOpts, kCLIFlags_None );
+       if( err ) exit( 1 );
        
        return( gExitCode );
 }
@@ -1299,7 +1866,7 @@ static void       BrowseCmd( void )
        {
                DNSServiceRef           sdRef;
                
-               if( useMainConnection ) sdRef = context->mainRef;
+               sdRef = useMainConnection ? context->mainRef : kBadDNSServiceRef;
                err = DNSServiceBrowse( &sdRef, context->flags, context->ifIndex, context->serviceTypes[ i ], context->domain,
                        BrowseCallback, context );
                require_noerr( err, exit );
@@ -1334,23 +1901,22 @@ exit:
 static void    BrowsePrintPrologue( const BrowseContext *inContext )
 {
        const int                                               timeLimitSecs   = inContext->timeLimitSecs;
-       const char * const *                    serviceType             = (const char **) inContext->serviceTypes;
+       const char * const *                    ptr                             = (const char **) inContext->serviceTypes;
        const char * const * const              end                             = (const char **) inContext->serviceTypes + inContext->serviceTypesCount;
-       char                                                    time[ kTimestampBufLen ];
        char                                                    ifName[ kInterfaceNameBufLen ];
        
        InterfaceIndexToName( inContext->ifIndex, ifName );
        
        FPrintF( stdout, "Flags:         %#{flags}\n",  inContext->flags, kDNSServiceFlagsDescriptors );
        FPrintF( stdout, "Interface:     %d (%s)\n",    (int32_t) inContext->ifIndex, ifName );
-       FPrintF( stdout, "Service types: %s",                   *serviceType++ );
-       while( serviceType < end ) FPrintF( stdout, ", %s", *serviceType++ );
+       FPrintF( stdout, "Service types: %s",                   *ptr++ );
+       while( ptr < end ) FPrintF( stdout, ", %s",             *ptr++ );
        FPrintF( stdout, "\n" );
        FPrintF( stdout, "Domain:        %s\n", inContext->domain ? inContext->domain : "<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 );
        }
@@ -4743,6 +5304,81 @@ static void BrowseIPAddrReleaseList( BrowseIPAddr *inList )
        }
 }
 
+//===========================================================================================================================
+//     GetNameInfoCmd
+//===========================================================================================================================
+
+const FlagStringPair           kGetNameInfoFlagStringPairs[] =
+{
+       CaseFlagStringify( NI_NUMERICSCOPE ),
+       CaseFlagStringify( NI_DGRAM ),
+       CaseFlagStringify( NI_NUMERICSERV ),
+       CaseFlagStringify( NI_NAMEREQD ),
+       CaseFlagStringify( NI_NUMERICHOST ),
+       CaseFlagStringify( NI_NOFQDN ),
+       { 0, NULL }
+};
+
+static void    GetNameInfoCmd( void )
+{
+       OSStatus                                        err;
+       sockaddr_ip                                     sip;
+       size_t                                          sockAddrLen;
+       unsigned int                            flags;
+       const FlagStringPair *          pair;
+       struct timeval                          now;
+       char                                            host[ NI_MAXHOST ];
+       char                                            serv[ NI_MAXSERV ];
+       
+       err = StringToSockAddr( gGetNameInfo_IPAddress, &sip, sizeof( sip ), &sockAddrLen );
+       check_noerr( err );
+       if( err )
+       {
+               FPrintF( stderr, "Failed to convert \"%s\" to a sockaddr.\n", gGetNameInfo_IPAddress );
+               goto exit;
+       }
+       
+       flags = 0;
+       if( gGetNameInfoFlag_DGram )            flags |= NI_DGRAM;
+       if( gGetNameInfoFlag_NameReqd )         flags |= NI_NAMEREQD;
+       if( gGetNameInfoFlag_NoFQDN )           flags |= NI_NOFQDN;
+       if( gGetNameInfoFlag_NumericHost )      flags |= NI_NUMERICHOST;
+       if( gGetNameInfoFlag_NumericScope )     flags |= NI_NUMERICSCOPE;
+       if( gGetNameInfoFlag_NumericServ )      flags |= NI_NUMERICSERV;
+       
+       // Print prologue.
+       
+       FPrintF( stdout, "SockAddr:   %##a\n",  &sip.sa );
+       FPrintF( stdout, "Flags:      0x%X < ", flags );
+       for( pair = kGetNameInfoFlagStringPairs; pair->str != NULL; ++pair )
+       {
+               if( flags & pair->flag ) FPrintF( stdout, "%s ", pair->str );
+       }
+       FPrintF( stdout, ">\n" );
+       FPrintF( stdout, "Start time: %{du:time}\n", NULL );
+       FPrintF( stdout, "---\n" );
+       
+       // Call getnameinfo().
+       
+       err = getnameinfo( &sip.sa, (socklen_t) sockAddrLen, host, (socklen_t) sizeof( host ), serv, (socklen_t) sizeof( serv ),
+               (int) flags );
+       gettimeofday( &now, NULL );
+       if( err )
+       {
+               FPrintF( stderr, "Error %d: %s.\n", err, gai_strerror( err ) );
+       }
+       else
+       {
+               FPrintF( stdout, "host: %s\n", host );
+               FPrintF( stdout, "serv: %s\n", serv );
+       }
+       FPrintF( stdout, "---\n" );
+       FPrintF( stdout, "End time:   %{du:time}\n", &now );
+       
+exit:
+       gExitCode = err ? 1 : 0;
+}
+
 //===========================================================================================================================
 //     GetAddrInfoStressCmd
 //===========================================================================================================================
@@ -4782,7 +5418,6 @@ static void       GetAddrInfoStressCmd( void )
        DNSServiceFlags                 flags;
        uint32_t                                ifIndex;
        char                                    ifName[ kInterfaceNameBufLen ];
-       char                                    time[ kTimestampBufLen ];
        
        if( gGAIStress_TestDurationSecs < 0 )
        {
@@ -4863,11 +5498,11 @@ static void     GetAddrInfoStressCmd( void )
        {
                FPrintF( stdout, "%d s\n", gGAIStress_TestDurationSecs );
        }
-       FPrintF( stdout, "Connection count:     %d\n",          gGAIStress_ConnectionCount );
-       FPrintF( stdout, "Request duration min: %d ms\n",       gGAIStress_DurationMinMs );
-       FPrintF( stdout, "Request duration max: %d ms\n",       gGAIStress_DurationMaxMs );
-       FPrintF( stdout, "Request count max:    %d\n",          gGAIStress_RequestCountMax );
-       FPrintF( stdout, "Start time:           %s\n",          GetTimestampStr( time ) );
+       FPrintF( stdout, "Connection count:     %d\n",                  gGAIStress_ConnectionCount );
+       FPrintF( stdout, "Request duration min: %d ms\n",               gGAIStress_DurationMinMs );
+       FPrintF( stdout, "Request duration max: %d ms\n",               gGAIStress_DurationMaxMs );
+       FPrintF( stdout, "Request count max:    %d\n",                  gGAIStress_RequestCountMax );
+       FPrintF( stdout, "Start time:           %{du:time}\n",  NULL);
        FPrintF( stdout, "---\n" );
        
        dispatch_main();
@@ -4893,7 +5528,6 @@ static void       GetAddrInfoStressEvent( void *inContext )
        unsigned int                                    nextMs;
        char                                                    randomStr[ kStressRandStrLen + 1 ];
        char                                                    hostname[ kStressRandStrLen + 4 + 1 ];
-       char                                                    time[ kTimestampBufLen ];
        Boolean                                                 isConnectionNew = false;
        static Boolean                                  printedHeader   = false;
        
@@ -4923,8 +5557,8 @@ static void       GetAddrInfoStressEvent( void *inContext )
                FPrintF( stdout, "%-26s Conn  Hostname Dur (ms)\n", "Timestamp" );
                printedHeader = true;
        }
-       FPrintF( stdout, "%-26s %3u%c %9s %8u\n",
-               GetTimestampStr( time ), context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
+       FPrintF( stdout, "%{du:time} %3u%c %9s %8u\n",
+               NULL, context->connectionNumber, isConnectionNew ? '*': ' ', hostname, nextMs );
        
        DNSServiceForget( &context->sdRef );
        sdRef = context->mainRef;
@@ -4970,8 +5604,6 @@ static void DNSSD_API
 //     DNSQueryCmd
 //===========================================================================================================================
 
-#define kDNSPort       53
-
 typedef struct
 {
        sockaddr_ip                             serverAddr;
@@ -5073,8 +5705,7 @@ static void       DNSQueryCmd( void )
        
        if( gDNSQuery_Verbose )
        {
-               FPrintF( stdout, "DNS message to send:\n\n" );
-               PrintUDNSMessage( msgPtr, msgLen, false );
+               FPrintF( stdout, "DNS message to send:\n\n%{du:dnsmsg}", msgPtr, msgLen );
                FPrintF( stdout, "---\n" );
        }
        
@@ -5103,7 +5734,7 @@ static void       DNSQueryCmd( void )
        
        if( context->timeLimitSecs == 0 ) goto exit;
        
-       err = DispatchReadSourceCreate( context->sock, DNSQueryReadHandler, DNSQueryCancelHandler, context,
+       err = DispatchReadSourceCreate( context->sock, NULL, DNSQueryReadHandler, DNSQueryCancelHandler, context,
                &context->readSource );
        require_noerr( err, exit );
        dispatch_resume( context->readSource );
@@ -5132,7 +5763,6 @@ exit:
 static void    DNSQueryPrintPrologue( const DNSQueryContext *inContext )
 {
        const int               timeLimitSecs = inContext->timeLimitSecs;
-       char                    time[ kTimestampBufLen ];
        
        FPrintF( stdout, "Name:        %s\n",           inContext->name );
        FPrintF( stdout, "Type:        %s (%u)\n",      RecordTypeToString( inContext->type ), inContext->type );
@@ -5141,7 +5771,7 @@ static void       DNSQueryPrintPrologue( const DNSQueryContext *inContext )
        FPrintF( stdout, "Time limit:  " );
        if( timeLimitSecs >= 0 )        FPrintF( stdout, "%d second%?c\n", timeLimitSecs, timeLimitSecs != 1, 's' );
        else                                            FPrintF( stdout, "∞\n" );
-       FPrintF( stdout, "Start time:  %s\n", GetTimestampStr( time ) );
+       FPrintF( stdout, "Start time:  %{du:time}\n", NULL );
        FPrintF( stdout, "---\n" );
 }
 
@@ -5152,11 +5782,11 @@ static void     DNSQueryPrintPrologue( const DNSQueryContext *inContext )
 static void    DNSQueryReadHandler( void *inContext )
 {
        OSStatus                                        err;
+       struct timeval                          now;
        const uint64_t                          nowTicks        = UpTicks();
        DNSQueryContext * const         context         = (DNSQueryContext *) inContext;
-       char                                            time[ kTimestampBufLen ];
        
-       GetTimestampStr( time );
+       gettimeofday( &now, NULL );
        
        if( context->useTCP )
        {
@@ -5198,11 +5828,11 @@ static void     DNSQueryReadHandler( void *inContext )
                check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
        }
        
-       FPrintF( stdout, "Receive time: %s\n",                  time );
+       FPrintF( stdout, "Receive time: %{du:time}\n",  &now );
        FPrintF( stdout, "Source:       %##a\n",                &context->serverAddr );
        FPrintF( stdout, "Message size: %zu\n",                 context->msgLen );
        FPrintF( stdout, "RTT:          %llu ms\n\n",   UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
-       PrintUDNSMessage( context->msgPtr, context->msgLen, context->printRawRData );
+       FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgPtr, context->msgLen );
        
        if( ( context->msgLen >= kDNSHeaderLength ) && ( DNSHeaderGetID( (DNSHeader *) context->msgPtr ) == context->queryID ) )
        {
@@ -5336,7 +5966,7 @@ static void       DNSCryptCmd( void )
        DNSCryptContext *               context         = NULL;
        size_t                                  writtenBytes;
        size_t                                  totalBytes;
-       SocketContext *                 sockContext;
+       SocketContext *                 sockCtx;
        SocketRef                               sock            = kInvalidSocketRef;
        const char *                    ptr;
        
@@ -5403,17 +6033,15 @@ static void     DNSCryptCmd( void )
        err = SocketWriteAll( sock, context->msgBuf, context->msgLen, 5 );
        require_noerr( err, exit );
        
-       sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
-       require_action( sockContext, exit, err = kNoMemoryErr );
+       err = SocketContextCreate( sock, context, &sockCtx );
+       require_noerr( err, exit );
+       sock = kInvalidSocketRef;
        
-       err = DispatchReadSourceCreate( sock, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockContext,
+       err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveCertHandler, SocketContextCancelHandler, sockCtx,
                &context->readSource );
-       if( err ) ForgetMem( &sockContext );
+       if( err ) ForgetSocketContext( &sockCtx );
        require_noerr( err, exit );
        
-       sockContext->context    = context;
-       sockContext->sock               = sock;
-       sock = kInvalidSocketRef;
        dispatch_resume( context->readSource );
        
        if( context->timeLimitSecs > 0 )
@@ -5436,9 +6064,10 @@ exit:
 static void    DNSCryptReceiveCertHandler( void *inContext )
 {
        OSStatus                                        err;
+       struct timeval                          now;
        const uint64_t                          nowTicks        = UpTicks();
-       SocketContext * const           sockContext     = (SocketContext *) inContext;
-       DNSCryptContext * const         context         = (DNSCryptContext *) sockContext->context;
+       SocketContext * const           sockCtx         = (SocketContext *) inContext;
+       DNSCryptContext * const         context         = (DNSCryptContext *) sockCtx->userContext;
        const DNSHeader *                       hdr;
        sockaddr_ip                                     fromAddr;
        const uint8_t *                         ptr;
@@ -5446,23 +6075,21 @@ static void     DNSCryptReceiveCertHandler( void *inContext )
        size_t                                          txtLen;
        unsigned int                            answerCount, i;
        uint8_t                                         targetName[ kDomainNameLengthMax ];
-       char                                            time[ kTimestampBufLen ];
        
-       GetTimestampStr( time );
+       gettimeofday( &now, NULL );
        
        dispatch_source_forget( &context->readSource );
        
-       err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
+       err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
                &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
        require_noerr( err, exit );
        check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
        
-       FPrintF( stdout, "Receive time: %s\n",                  time );
+       FPrintF( stdout, "Receive time: %{du:time}\n",  &now );
        FPrintF( stdout, "Source:       %##a\n",                &context->serverAddr );
        FPrintF( stdout, "Message size: %zu\n",                 context->msgLen );
        FPrintF( stdout, "RTT:          %llu ms\n\n",   UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
-       
-       PrintUDNSMessage( context->msgBuf, context->msgLen, context->printRawRData );
+       FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, context->msgBuf, context->msgLen );
        
        require_action_quiet( context->msgLen >= kDNSHeaderLength, exit, err = kSizeErr );
        
@@ -5521,28 +6148,28 @@ exit:
 static void    DNSCryptReceiveResponseHandler( void *inContext )
 {
        OSStatus                                                err;
+       struct timeval                                  now;
        const uint64_t                                  nowTicks        = UpTicks();
-       SocketContext * const                   sockContext     = (SocketContext *) inContext;
-       DNSCryptContext * const                 context         = (DNSCryptContext *) sockContext->context;
+       SocketContext * const                   sockCtx         = (SocketContext *) inContext;
+       DNSCryptContext * const                 context         = (DNSCryptContext *) sockCtx->userContext;
        sockaddr_ip                                             fromAddr;
        DNSCryptResponseHeader *                hdr;
        const uint8_t *                                 end;
        uint8_t *                                               ciphertext;
        uint8_t *                                               plaintext;
        const uint8_t *                                 response;
-       char                                                    time[ kTimestampBufLen ];
        uint8_t                                                 nonce[ crypto_box_NONCEBYTES ];
        
-       GetTimestampStr( time );
+       gettimeofday( &now, NULL );
        
        dispatch_source_forget( &context->readSource );
        
-       err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
+       err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &context->msgLen,
                &fromAddr, sizeof( fromAddr ), NULL, NULL, NULL, NULL );
        require_noerr( err, exit );
        check( SockAddrCompareAddr( &fromAddr, &context->serverAddr ) == 0 );
        
-       FPrintF( stdout, "Receive time: %s\n",                  time );
+       FPrintF( stdout, "Receive time: %{du:time}\n",  &now );
        FPrintF( stdout, "Source:       %##a\n",                &context->serverAddr );
        FPrintF( stdout, "Message size: %zu\n",                 context->msgLen );
        FPrintF( stdout, "RTT:          %llu ms\n\n",   UpTicksToMilliseconds( nowTicks - context->sendTicks ) );
@@ -5586,7 +6213,7 @@ static void       DNSCryptReceiveResponseHandler( void *inContext )
        require_noerr( err, exit );
        
        response = plaintext + crypto_box_ZEROBYTES;
-       PrintUDNSMessage( response, (size_t)( end - response ), context->printRawRData );
+       FPrintF( stdout, "%.*{du:dnsmsg}", context->printRawRData ? 1 : 0, response, (size_t)( end - response ) );
        Exit( kExitReason_ReceivedResponse );
        
 exit:
@@ -5762,7 +6389,7 @@ exit:
 static OSStatus        DNSCryptSendQuery( DNSCryptContext *inContext )
 {
        OSStatus                        err;
-       SocketContext *         sockContext;
+       SocketContext *         sockCtx;
        SocketRef                       sock = kInvalidSocketRef;
        
        check( inContext->msgLen > 0 );
@@ -5775,18 +6402,15 @@ static OSStatus DNSCryptSendQuery( DNSCryptContext *inContext )
        err = SocketWriteAll( sock, inContext->msgBuf, inContext->msgLen, 5 );
        require_noerr( err, exit );
        
-       sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
-       require_action( sockContext, exit, err = kNoMemoryErr );
+       err = SocketContextCreate( sock, inContext, &sockCtx );
+       require_noerr( err, exit );
+       sock = kInvalidSocketRef;
        
-       err = DispatchReadSourceCreate( sock, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockContext,
+       err = DispatchReadSourceCreate( sockCtx->sock, NULL, DNSCryptReceiveResponseHandler, SocketContextCancelHandler, sockCtx,
                &inContext->readSource );
-       if( err ) ForgetMem( &sockContext );
+       if( err ) ForgetSocketContext( &sockCtx );
        require_noerr( err, exit );
        
-       sockContext->context    = inContext;
-       sockContext->sock               = sock;
-       sock = kInvalidSocketRef;
-       
        dispatch_resume( inContext->readSource );
        
 exit:
@@ -5857,11 +6481,6 @@ static char *    CertTimeStr( time_t inTime, char inBuffer[ kCertTimeStrBufLen ] )
 //     MDNSQueryCmd
 //===========================================================================================================================
 
-#define kMDNSPort                                      5353
-
-#define kDefaultMDNSMessageID          0
-#define kDefaultMDNSQueryFlags         0
-
 typedef struct
 {
        const char *                    qnameStr;                                                       // Name (QNAME) of the record being queried as a C string.
@@ -6039,37 +6658,33 @@ static void     MDNSQueryCmd( void )
        
        if( IsValidSocket( sockV4 ) )
        {
-               SocketContext *         sockContext;
+               SocketContext *         sockCtx;
                
-               sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
-               require_action( sockContext, exit, err = kNoMemoryErr );
+               err = SocketContextCreate( sockV4, context, &sockCtx );
+               require_noerr( err, exit );
+               sockV4 = kInvalidSocketRef;
                
-               err = DispatchReadSourceCreate( sockV4, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext,
+               err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
                        &context->readSourceV4 );
-               if( err ) ForgetMem( &sockContext );
+               if( err ) ForgetSocketContext( &sockCtx );
                require_noerr( err, exit );
                
-               sockContext->context    = context;
-               sockContext->sock               = sockV4;
-               sockV4 = kInvalidSocketRef;
                dispatch_resume( context->readSourceV4 );
        }
        
        if( IsValidSocket( sockV6 ) )
        {
-               SocketContext *         sockContext;
+               SocketContext *         sockCtx;
                
-               sockContext = (SocketContext *) calloc( 1, sizeof( *sockContext ) );
-               require_action( sockContext, exit, err = kNoMemoryErr );
+               err = SocketContextCreate( sockV6, context, &sockCtx );
+               require_noerr( err, exit );
+               sockV6 = kInvalidSocketRef;
                
-               err = DispatchReadSourceCreate( sockV6, MDNSQueryReadHandler, SocketContextCancelHandler, sockContext,
+               err = DispatchReadSourceCreate( sockCtx->sock, NULL, MDNSQueryReadHandler, SocketContextCancelHandler, sockCtx,
                        &context->readSourceV6 );
-               if( err ) ForgetMem( &sockContext );
+               if( err ) ForgetSocketContext( &sockCtx );
                require_noerr( err, exit );
                
-               sockContext->context    = context;
-               sockContext->sock               = sockV6;
-               sockV6 = kInvalidSocketRef;
                dispatch_resume( context->readSourceV6 );
        }
        
@@ -6093,7 +6708,6 @@ exit:
 static void    MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
 {
        const int               receiveSecs = inContext->receiveSecs;
-       char                    time[ kTimestampBufLen ];
        
        FPrintF( stdout, "Interface:        %d (%s)\n",         (int32_t) inContext->ifIndex, inContext->ifName );
        FPrintF( stdout, "Name:             %s\n",                      inContext->qnameStr );
@@ -6105,7 +6719,7 @@ static void       MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
        FPrintF( stdout, "Receive duration: " );
        if( receiveSecs >= 0 )  FPrintF( stdout, "%d second%?c\n", receiveSecs, receiveSecs != 1, 's' );
        else                                    FPrintF( stdout, "∞\n" );
-       FPrintF( stdout, "Start time:  %s\n",           GetTimestampStr( time ) );
+       FPrintF( stdout, "Start time:       %{du:time}\n",      NULL );
 }
 
 //===========================================================================================================================
@@ -6115,16 +6729,16 @@ static void     MDNSQueryPrintPrologue( const MDNSQueryContext *inContext )
 static void    MDNSQueryReadHandler( void *inContext )
 {
        OSStatus                                                err;
-       SocketContext * const                   sockContext     = (SocketContext *) inContext;
-       MDNSQueryContext * const                context         = (MDNSQueryContext *) sockContext->context;
+       struct timeval                                  now;
+       SocketContext * const                   sockCtx = (SocketContext *) inContext;
+       MDNSQueryContext * const                context = (MDNSQueryContext *) sockCtx->userContext;
        size_t                                                  msgLen;
        sockaddr_ip                                             fromAddr;
-       char                                                    time[ kTimestampBufLen ];
        Boolean                                                 foundAnswer     = false;
        
-       GetTimestampStr( time );
+       gettimeofday( &now, NULL );
        
-       err = SocketRecvFrom( sockContext->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
+       err = SocketRecvFrom( sockCtx->sock, context->msgBuf, sizeof( context->msgBuf ), &msgLen, &fromAddr,
                sizeof( fromAddr ), NULL, NULL, NULL, NULL );
        require_noerr( err, exit );
        
@@ -6162,11 +6776,10 @@ static void     MDNSQueryReadHandler( void *inContext )
        if( context->allResponses || foundAnswer )
        {
                FPrintF( stdout, "---\n" );
-               FPrintF( stdout, "Receive time: %s\n",          time );
-               FPrintF( stdout, "Source:       %##a\n",        &fromAddr );
-               FPrintF( stdout, "Message size: %zu\n\n",       msgLen );
-               
-               PrintMDNSMessage( context->msgBuf, msgLen, context->printRawRData );
+               FPrintF( stdout, "Receive time: %{du:time}\n",  &now );
+               FPrintF( stdout, "Source:       %##a\n",                &fromAddr );
+               FPrintF( stdout, "Message size: %zu\n\n%#.*{du:dnsmsg}",
+                       msgLen, context->printRawRData ? 1 : 0, context->msgBuf, msgLen );
        }
        
 exit:
@@ -6194,221 +6807,3657 @@ exit:
 }
 
 //===========================================================================================================================
-//     SSDPDiscoverCmd
+//     DNSServerCmd
 //===========================================================================================================================
 
-#define kSSDPPort              1900
+typedef uint32_t               DNSServerEventType;
+#define kDNSServerEvent_Started                1
+#define kDNSServerEvent_Stopped                2
+
+typedef struct DNSServerPrivate *              DNSServerRef;
 
 typedef struct
 {
-       HTTPHeader                              header;                 // HTTP header object for sending and receiving.
-       dispatch_source_t               readSourceV4;   // Read dispatch source for IPv4 socket.
-       dispatch_source_t               readSourceV6;   // Read dispatch source for IPv6 socket.
-       int                                             receiveSecs;    // After send, the amount of time to spend receiving.
-       uint32_t                                ifindex;                // Index of the interface over which to send the query.
-       Boolean                                 useIPv4;                // True if the query should be sent via IPv4 multicast.
-       Boolean                                 useIPv6;                // True if the query should be sent via IPv6 multicast.
+       DNSServerRef                    server;                         // Reference to the DNS server.
+       dispatch_source_t               sigIntSource;           // Dispatch SIGINT source.
+       dispatch_source_t               sigTermSource;          // Dispatch SIGTERM source.
+#if( TARGET_OS_DARWIN )
+       dispatch_source_t               processMonitor;         // Process monitor source for process being followed, if any.
+       pid_t                                   followPID;                      // PID of process being followed (we exit when they exit), if any.
+       Boolean                                 resolverRegistered;     // True if system DNS settings contains a resolver entry for server.
+#endif
+       Boolean                                 loopbackOnly;           // True if the server should be bound to the loopback interface.
+       Boolean                                 serverStarted;          // True if the server was successfully started.
+       Boolean                                 calledStop;                     // True if the server was explicitly stopped.
        
-}      SSDPDiscoverContext;
+}      DNSServerCmdContext;
 
-static void            SSDPDiscoverPrintPrologue( const SSDPDiscoverContext *inContext );
-static void            SSDPDiscoverReadHandler( void *inContext );
-static int             SocketToPortNumber( SocketRef inSock );
-static OSStatus        WriteSSDPSearchRequest( HTTPHeader *inHeader, const void *inHostSA, int inMX, const char *inST );
+typedef void ( *DNSServerEventHandler_f )( DNSServerEventType inType, void *inContext );
 
-static void    SSDPDiscoverCmd( void )
+CFTypeID       DNSServerGetTypeID( void );
+static OSStatus
+       DNSServerCreate(
+               dispatch_queue_t                inQueue,
+               DNSServerEventHandler_f inEventHandler,
+               void *                                  inEventContext,
+               int                                             inResponseDelayMs,
+               Boolean                                 inLoopbackOnly,
+               DNSServerRef *                  outServer );
+static void    DNSServerStart( DNSServerRef inServer );
+static void    DNSServerStop( DNSServerRef inServer );
+
+static void    DNSServerCmdContextFree( DNSServerCmdContext *inContext );
+static void    DNSServerCmdEventHandler( DNSServerEventType inType, void *inContext );
+static void    DNSServerCmdSigIntHandler( void *inContext );
+static void    DNSServerCmdSigTermHandler( void *inContext );
+#if( TARGET_OS_DARWIN )
+static void    DNSServerCmdFollowedProcessHandler( void *inContext );
+#endif
+
+ulog_define_ex( "com.apple.dnssdutil", DNSServer, kLogLevelInfo, kLogFlags_None, "DNSServer", NULL );
+#define ds_ulog( LEVEL, ... )          ulog( &log_category_from_name( DNSServer ), (LEVEL), __VA_ARGS__ )
+
+static void    DNSServerCmd( void )
 {
        OSStatus                                        err;
-       SSDPDiscoverContext *           context;
-       dispatch_source_t                       signalSource    = NULL;
-       SocketRef                                       sockV4                  = kInvalidSocketRef;
-       SocketRef                                       sockV6                  = kInvalidSocketRef;
-       ssize_t                                         n;
-       int                                                     sendCount;
-       char                                            time[ kTimestampBufLen ];
-       
-       // Set up SIGINT handler.
-       
-       signal( SIGINT, SIG_IGN );
-       err = DispatchSignalSourceCreate( SIGINT, Exit, kExitReason_SIGINT, &signalSource );
-       require_noerr( err, exit );
-       dispatch_resume( signalSource );
+       DNSServerCmdContext *           context;
        
-       // Check command parameters.
-       
-       if( gSSDPDiscover_ReceiveSecs < -1 )
-       {
-               FPrintF( stdout, "Invalid receive time: %d seconds.\n", gSSDPDiscover_ReceiveSecs );
-               err = kParamErr;
-               goto exit;
-       }
-       
-       // Create context.
-       
-       context = (SSDPDiscoverContext *) calloc( 1, sizeof( *context ) );
+       context = (DNSServerCmdContext *) calloc( 1, sizeof( *context ) );
        require_action( context, exit, err = kNoMemoryErr );
        
-       context->receiveSecs    = gSSDPDiscover_ReceiveSecs;
-       context->useIPv4                = ( gSSDPDiscover_UseIPv4 || !gSSDPDiscover_UseIPv6 ) ? true : false;
-       context->useIPv6                = ( gSSDPDiscover_UseIPv6 || !gSSDPDiscover_UseIPv4 ) ? true : false;
-       
-       err = InterfaceIndexFromArgString( gInterface, &context->ifindex );
-       require_noerr_quiet( err, exit );
-       
-       // Set up IPv4 socket.
+       context->loopbackOnly = gDNSServer_LoopbackOnly ? true : false;
        
-       if( context->useIPv4 )
+#if( TARGET_OS_DARWIN )
+       if( gDNSServer_FollowPID )
        {
-               int port;
-               err = UDPClientSocketOpen( AF_INET, NULL, 0, -1, &port, &sockV4 );
-               require_noerr( err, exit );
+               long long               value;
                
-               err = SocketSetMulticastInterface( sockV4, NULL, context->ifindex );
-               require_noerr( err, exit );
+               err = StringToLongLong( gDNSServer_FollowPID, &value );
+               if( !err && ( value < 0 ) ) err = kValueErr;
+               if( err )
+               {
+                       FPrintF( stderr, "Invalid followPID argument \"%s\".\n", gDNSServer_FollowPID );
+                       goto exit;
+               }
+               context->followPID = (pid_t) value;
                
-               err = setsockopt( sockV4, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &(uint8_t){ 1 }, (socklen_t) sizeof( uint8_t ) );
-               err = map_socket_noerr_errno( sockV4, err );
+               err = DispatchProcessMonitorCreate( context->followPID, DISPATCH_PROC_EXIT, dispatch_get_main_queue(),
+                       DNSServerCmdFollowedProcessHandler, NULL, context, &context->processMonitor );
                require_noerr( err, exit );
+               dispatch_resume( context->processMonitor );
+       }
+       else
+       {
+               context->followPID = -1;
        }
+#endif
        
-       // Set up IPv6 socket.
+       signal( SIGINT, SIG_IGN );
+       err = DispatchSignalSourceCreate( SIGINT, DNSServerCmdSigIntHandler, context, &context->sigIntSource );
+       require_noerr( err, exit );
+       dispatch_resume( context->sigIntSource );
        
-       if( context->useIPv6 )
+       signal( SIGTERM, SIG_IGN );
+       err = DispatchSignalSourceCreate( SIGTERM, DNSServerCmdSigTermHandler, context, &context->sigTermSource );
+       require_noerr( err, exit );
+       dispatch_resume( context->sigTermSource );
+       
+       if( gDNSServer_Foreground )
        {
-               err = UDPClientSocketOpen( AF_INET6, NULL, 0, -1, NULL, &sockV6 );
-               require_noerr( err, exit );
-               
-               err = SocketSetMulticastInterface( sockV6, NULL, context->ifindex );
-               require_noerr( err, exit );
-               
-               err = setsockopt( sockV6, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &(int){ 1 }, (socklen_t) sizeof( int ) );
-               err = map_socket_noerr_errno( sockV6, err );
-               require_noerr( err, exit );
+               LogControl( "DNSServer:output=file;stdout,DNSServer:flags=time;prefix" );
        }
        
-       // Print prologue.
+       if( ( gDNSServer_DefaultTTL < 0 ) || ( gDNSServer_DefaultTTL > INT32_MAX ) )
+       {
+               ds_ulog( kLogLevelError, "The default TTL %d provided by user is out-of-range. Will use %d instead.\n",
+                       gDNSServer_DefaultTTL, kDNSServerDefaultTTL );
+               gDNSServer_DefaultTTL = kDNSServerDefaultTTL;
+       }
        
-       SSDPDiscoverPrintPrologue( context );
+       err = DNSServerCreate( dispatch_get_main_queue(), DNSServerCmdEventHandler, context, gDNSServer_ResponseDelayMs,
+               context->loopbackOnly, &context->server );
+       require_noerr( err, exit );
        
-       // Send mDNS query message.
+       DNSServerStart( context->server );
+       dispatch_main();
        
-       sendCount = 0;
-       if( IsValidSocket( sockV4 ) )
+exit:
+       ds_ulog( kLogLevelError, "Failed to start DNS server: %#m\n", err );
+       if( context ) DNSServerCmdContextFree( context );
+       if( err ) exit( 1 );
+}
+
+//===========================================================================================================================
+//     DNSServerCmdContextFree
+//===========================================================================================================================
+
+static void    DNSServerCmdContextFree( DNSServerCmdContext *inContext )
+{
+       ForgetCF( &inContext->server );
+       dispatch_source_forget( &inContext->sigIntSource );
+       dispatch_source_forget( &inContext->sigTermSource );
+       dispatch_source_forget( &inContext->processMonitor );
+       free( inContext );
+}
+
+//===========================================================================================================================
+//     DNSServerCmdEventHandler
+//===========================================================================================================================
+
+#if( TARGET_OS_DARWIN )
+static OSStatus        _DNSServerCmdRegisterResolver( void );
+static OSStatus        _DNSServerCmdUnregisterResolver( void );
+#endif
+
+static void    DNSServerCmdEventHandler( DNSServerEventType inType, void *inContext )
+{
+       DNSServerCmdContext * const             context = (DNSServerCmdContext *) inContext;
+#if( TARGET_OS_DARWIN )
+       OSStatus                                                err;
+#endif
+       
+       if( inType == kDNSServerEvent_Started )
        {
-               struct sockaddr_in              mcastAddr4;
-               
-               memset( &mcastAddr4, 0, sizeof( mcastAddr4 ) );
-               SIN_LEN_SET( &mcastAddr4 );
-               mcastAddr4.sin_family           = AF_INET;
-               mcastAddr4.sin_port                     = htons( kSSDPPort );
-               mcastAddr4.sin_addr.s_addr      = htonl( 0xEFFFFFFA );  // 239.255.255.250
-               
-               err = WriteSSDPSearchRequest( &context->header, &mcastAddr4, gSSDPDiscover_MX, gSSDPDiscover_ST );
-               require_noerr( err, exit );
-               
-               n = sendto( sockV4, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr4,
-                       (socklen_t) sizeof( mcastAddr4 ) );
-               err = map_socket_value_errno( sockV4, n == (ssize_t) context->header.len, n );
+               context->serverStarted = true;
+       #if( TARGET_OS_DARWIN )
+               err = _DNSServerCmdRegisterResolver();
                if( err )
                {
-                       FPrintF( stderr, "*** Failed to send query on IPv4 socket with error %#m\n", err );
-                       ForgetSocket( &sockV4 );
+                       ds_ulog( kLogLevelError, "Failed to add resolver to DNS configuration for \"d.test.\" domain: %#m\n", err );
+                       if( context->loopbackOnly ) exit( 1 );
                }
                else
                {
-                       if( gSSDPDiscover_Verbose )
-                       {
-                               GetTimestampStr( time );
-                               FPrintF( stdout, "---\n" );
-                               FPrintF( stdout, "Send time:    %s\n",          time );
-                               FPrintF( stdout, "Source Port:  %d\n",          SocketToPortNumber( sockV4 ) );
-                               FPrintF( stdout, "Destination:  %##a\n",        &mcastAddr4 );
-                               FPrintF( stdout, "Message size: %zu\n",         context->header.len );
-                               FPrintF( stdout, "HTTP header:\n%1{text}",      context->header.buf, context->header.len );
-                       }
-                       ++sendCount;
+                       context->resolverRegistered = true;
                }
+       #endif
        }
-       
-       if( IsValidSocket( sockV6 ) )
+       else if( inType == kDNSServerEvent_Stopped )
        {
-               struct sockaddr_in6             mcastAddr6;
-               
-               memset( &mcastAddr6, 0, sizeof( mcastAddr6 ) );
-               SIN6_LEN_SET( &mcastAddr6 );
-               mcastAddr6.sin6_family                          = AF_INET6;
-               mcastAddr6.sin6_port                            = htons( kSSDPPort );
-               mcastAddr6.sin6_addr.s6_addr[  0 ]      = 0xFF; // SSDP IPv6 link-local multicast address FF02::C
-               mcastAddr6.sin6_addr.s6_addr[  1 ]      = 0x02;
-               mcastAddr6.sin6_addr.s6_addr[ 15 ]      = 0x0C;
-               
-               err = WriteSSDPSearchRequest( &context->header, &mcastAddr6, gSSDPDiscover_MX, gSSDPDiscover_ST );
-               require_noerr( err, exit );
-               
-               n = sendto( sockV6, context->header.buf, context->header.len, 0, (const struct sockaddr *) &mcastAddr6,
-                       (socklen_t) sizeof( mcastAddr6 ) );
-               err = map_socket_value_errno( sockV6, n == (ssize_t) context->header.len, n );
-               if( err )
-               {
-                       FPrintF( stderr, "*** Failed to send query on IPv6 socket with error %#m\n", err );
-                       ForgetSocket( &sockV6 );
-               }
-               else
+       #if( TARGET_OS_DARWIN )
+               if( context->resolverRegistered )
                {
-                       if( gSSDPDiscover_Verbose )
+                       err = _DNSServerCmdUnregisterResolver();
+                       if( err )
                        {
-                               GetTimestampStr( time );
-                               FPrintF( stdout, "---\n" );
-                               FPrintF( stdout, "Send time:    %s\n",          time );
-                               FPrintF( stdout, "Source Port:  %d\n",          SocketToPortNumber( sockV6 ) );
-                               FPrintF( stdout, "Destination:  %##a\n",        &mcastAddr6 );
-                               FPrintF( stdout, "Message size: %zu\n",         context->header.len );
-                               FPrintF( stdout, "HTTP header:\n%1{text}",      context->header.buf, context->header.len );
+                               ds_ulog( kLogLevelError, "Failed to remove resolver from DNS configuration: %#m\n", err );
+                       }
+                       else
+                       {
+                               context->resolverRegistered = false;
                        }
-                       ++sendCount;
                }
+               
+               if( !context->calledStop )
+               {
+                       ds_ulog( kLogLevelError, "The server stopped unexpectedly.\n" );
+                       exit( 1 );
+               }
+       #endif
+               DNSServerCmdContextFree( context );
        }
-       require_action_quiet( sendCount > 0, exit, err = kUnexpectedErr );
-       
-       // If there's no wait period after the send, then exit.
-       
+}
+
+#if( TARGET_OS_DARWIN )
+//===========================================================================================================================
+//     _DNSServerCmdRegisterResolver
+//===========================================================================================================================
+
+static OSStatus        _DNSServerCmdRegisterResolver( void )
+{
+       OSStatus                                err;
+       SCDynamicStoreRef               store;
+       CFPropertyListRef               plist           = NULL;
+       CFStringRef                             key                     = NULL;
+       const uint32_t                  loopbackV4      = htonl( INADDR_LOOPBACK );
+       Boolean                                 success;
+       
+       store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
+       err = map_scerror( store );
+       require_noerr( err, exit );
+       
+       err = CFPropertyListCreateFormatted( kCFAllocatorDefault, &plist,
+               "{"
+                       "%kO="
+                       "["
+                               "%s"
+                       "]"
+                       "%kO="
+                       "["
+                               "%.4a"
+                               "%.16a"
+                       "]"
+               "}",
+               kSCPropNetDNSSupplementalMatchDomains, "d.test.",
+               kSCPropNetDNSServerAddresses, &loopbackV4, in6addr_loopback.s6_addr );
+       require_noerr( err, exit );
+       
+       key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
+               CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
+       require_action( key, exit, err = kUnknownErr );
+       
+       success = SCDynamicStoreSetValue( store, key, plist );
+       require_action( success, exit, err = kUnknownErr );
+       
+exit:
+       CFReleaseNullSafe( store );
+       CFReleaseNullSafe( plist );
+       CFReleaseNullSafe( key );
+       return( err );
+}
+
+//===========================================================================================================================
+//     _DNSServerCmdUnregisterResolver
+//===========================================================================================================================
+
+static OSStatus        _DNSServerCmdUnregisterResolver( void )
+{
+       OSStatus                                err;
+       SCDynamicStoreRef               store;
+       CFStringRef                             key = NULL;
+       Boolean                                 success;
+       
+       store = SCDynamicStoreCreate( NULL, CFSTR( "com.apple.dnssdutil" ), NULL, NULL );
+       err = map_scerror( store );
+       require_noerr( err, exit );
+       
+       key = SCDynamicStoreKeyCreateNetworkServiceEntity( NULL, kSCDynamicStoreDomainState,
+               CFSTR( "com.apple.dnssdutil.server" ), kSCEntNetDNS );
+       require_action( key, exit, err = kUnknownErr );
+       
+       success = SCDynamicStoreRemoveValue( store, key );
+       require_action( success, exit, err = kUnknownErr );
+       
+exit:
+       CFReleaseNullSafe( store );
+       CFReleaseNullSafe( key );
+       return( err );
+}
+#endif
+
+//===========================================================================================================================
+//     DNSServerCmdSigIntHandler
+//===========================================================================================================================
+
+static void    _DNSServerCmdExternalExit( DNSServerCmdContext *inContext, int inSignal );
+
+static void    DNSServerCmdSigIntHandler( void *inContext )
+{
+       _DNSServerCmdExternalExit( (DNSServerCmdContext *) inContext, SIGINT );
+}
+
+//===========================================================================================================================
+//     DNSServerCmdSigTermHandler
+//===========================================================================================================================
+
+static void    DNSServerCmdSigTermHandler( void *inContext )
+{
+       _DNSServerCmdExternalExit( (DNSServerCmdContext *) inContext, SIGTERM );
+}
+
+#if( TARGET_OS_DARWIN )
+//===========================================================================================================================
+//     DNSServerCmdFollowedProcessHandler
+//===========================================================================================================================
+
+static void    DNSServerCmdFollowedProcessHandler( void *inContext )
+{
+       DNSServerCmdContext * const             context = (DNSServerCmdContext *) inContext;
+       
+       if( dispatch_source_get_data( context->processMonitor ) & DISPATCH_PROC_EXIT )
+       {
+               _DNSServerCmdExternalExit( context, 0 );
+       }
+}
+#endif
+
+//===========================================================================================================================
+//     _DNSServerCmdExternalExit
+//===========================================================================================================================
+
+#define SignalNumberToString( X ) (            \
+       ( (X) == SIGINT )  ? "SIGINT"  :        \
+       ( (X) == SIGTERM ) ? "SIGTERM" :        \
+                                                "???" )
+
+static void    _DNSServerCmdExternalExit( DNSServerCmdContext *inContext, int inSignal )
+{
+       OSStatus                err;
+       
+#if( TARGET_OS_DARWIN )
+       if( inSignal == 0 )
+       {
+               ds_ulog( kLogLevelNotice, "Exiting: followed process (%lld) exited\n", (int64_t) inContext->followPID );
+       }
+       else
+#endif
+       {
+               ds_ulog( kLogLevelNotice, "Exiting: received signal %d (%s)\n", inSignal, SignalNumberToString( inSignal ) );
+       }
+       
+#if( TARGET_OS_DARWIN )
+       if( inContext->resolverRegistered )
+       {
+               err = _DNSServerCmdUnregisterResolver();
+               if( err )
+               {
+                       ds_ulog( kLogLevelError, "Failed to remove resolver from DNS configuration: %#m\n", err );
+                       goto exit;
+               }
+               inContext->resolverRegistered = false;
+       }
+#endif
+       if( inContext->serverStarted )
+       {
+               DNSServerStop( inContext->server );
+               inContext->calledStop = true;
+       }
+       err = kNoErr;
+       
+exit:
+       exit( err ? 1 : 0 );
+}
+
+//===========================================================================================================================
+//     DNSServerCreate
+//===========================================================================================================================
+
+typedef struct DNSDelayedResponse              DNSDelayedResponse;
+struct DNSDelayedResponse
+{
+       DNSDelayedResponse *            next;
+       sockaddr_ip                                     clientAddr;
+       uint64_t                                        targetTicks;
+       uint8_t *                                       msgPtr;
+       size_t                                          msgLen;
+};
+
+#define DNSScheduledResponseFree( X )          do { ForgetMem( &(X)->msgPtr ) ; free( X ); } while( 0 )
+
+struct DNSServerPrivate
+{
+       CFRuntimeBase                           base;                           // CF object base.
+       dispatch_queue_t                        queue;                          // Queue for DNS server's events.
+       dispatch_source_t                       readSourceUDPv4;        // Read source for IPv4 UDP socket.
+       dispatch_source_t                       readSourceUDPv6;        // Read source for IPv6 UDP socket.
+       dispatch_source_t                       readSourceTCPv4;        // Read source for IPv4 TCP socket.
+       dispatch_source_t                       readSourceTCPv6;        // Read source for IPv6 TCP socket.
+       DNSServerEventHandler_f         eventHandler;
+       void *                                          eventContext;
+       DNSDelayedResponse *            responseList;
+       int                                                     responseDelayMs;
+       dispatch_source_t                       responseTimer;
+       Boolean                                         loopbackOnly;
+       Boolean                                         stopped;
+};
+
+CF_CLASS_DEFINE( DNSServer );
+
+static OSStatus
+       DNSServerCreate(
+               dispatch_queue_t                inQueue,
+               DNSServerEventHandler_f inEventHandler,
+               void *                                  inEventContext,
+               int                                             inResponseDelayMs,
+               Boolean                                 inLoopbackOnly,
+               DNSServerRef *                  outServer )
+{
+       OSStatus                        err;
+       DNSServerRef            obj = NULL;
+       
+       CF_OBJECT_CREATE( DNSServer, obj, err, exit );
+       
+       ReplaceDispatchQueue( &obj->queue, inQueue );
+       obj->eventHandler               = inEventHandler;
+       obj->eventContext               = inEventContext;
+       obj->responseDelayMs    = inResponseDelayMs;
+       if( inLoopbackOnly ) obj->loopbackOnly = true;
+       
+       *outServer = obj;
+       obj = NULL;
+       err = kNoErr;
+       
+exit:
+       CFReleaseNullSafe( obj );
+       return( err );
+}
+
+//===========================================================================================================================
+//     _DNSServerFinalize
+//===========================================================================================================================
+
+static void    _DNSServerFinalize( CFTypeRef inObj )
+{
+       DNSServerRef const              me = (DNSServerRef) inObj;
+       
+       check( !me->readSourceUDPv4 );
+       check( !me->readSourceUDPv6 );
+       check( !me->readSourceTCPv4 );
+       check( !me->readSourceTCPv6 );
+       check( !me->responseTimer );
+       dispatch_forget( &me->queue );
+}
+
+//===========================================================================================================================
+//     DNSServerStart
+//===========================================================================================================================
+
+static void    _DNSServerStart( void *inContext );
+static void    _DNSServerUDPReadHandler( void *inContext );
+static void    _DNSServerTCPReadHandler( void *inContext );
+
+static void    DNSServerStart( DNSServerRef me )
+{
+       CFRetain( me );
+       dispatch_async_f( me->queue, me, _DNSServerStart );
+}
+
+static void    _DNSServerStart( void *inContext )
+{
+       OSStatus                                err;
+       DNSServerRef const              me                      = (DNSServerRef) inContext;
+       SocketRef                               sock            = kInvalidSocketRef;
+       SocketContext *                 sockCtx         = NULL;
+       const uint32_t                  loopbackV4      = htonl( INADDR_LOOPBACK );
+       
+       // Create IPv4 UDP socket.
+       
+       err = _ServerSocketOpenEx2( AF_INET, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &loopbackV4 : NULL,
+               kDNSPort, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
+       require_noerr( err, exit );
+       
+       // Create read source for IPv4 UDP socket.
+       
+       err = SocketContextCreate( sock, me, &sockCtx );
+       require_noerr( err, exit );
+       sock = kInvalidSocketRef;
+       
+       err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
+               &me->readSourceUDPv4 );
+       require_noerr( err, exit );
+       dispatch_resume( me->readSourceUDPv4 );
+       sockCtx = NULL;
+       
+       // Create IPv6 UDP socket.
+       
+       err = _ServerSocketOpenEx2( AF_INET6, SOCK_DGRAM, IPPROTO_UDP, me->loopbackOnly ? &in6addr_loopback : NULL,
+               kDNSPort, NULL, kSocketBufferSize_DontSet, me->loopbackOnly ? true : false, &sock );
+       require_noerr( err, exit );
+       
+       // Create read source for IPv6 UDP socket.
+       
+       err = SocketContextCreate( sock, me, &sockCtx );
+       require_noerr( err, exit );
+       sock = kInvalidSocketRef;
+       
+       err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerUDPReadHandler, SocketContextCancelHandler, sockCtx,
+               &me->readSourceUDPv6 );
+       require_noerr( err, exit );
+       dispatch_resume( me->readSourceUDPv6 );
+       sockCtx = NULL;
+       
+       // Create IPv4 TCP socket.
+       
+       err = _ServerSocketOpenEx2( AF_INET, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &loopbackV4 : NULL,
+               kDNSPort, NULL, kSocketBufferSize_DontSet, false, &sock );
+       require_noerr( err, exit );
+       
+       // Create read source for IPv4 TCP socket.
+       
+       err = SocketContextCreate( sock, me, &sockCtx );
+       require_noerr( err, exit );
+       sock = kInvalidSocketRef;
+       
+       err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
+               &me->readSourceTCPv4 );
+       require_noerr( err, exit );
+       dispatch_resume( me->readSourceTCPv4 );
+       sockCtx = NULL;
+       
+       // Create IPv6 TCP socket.
+       
+       err = _ServerSocketOpenEx2( AF_INET6, SOCK_STREAM, IPPROTO_TCP, me->loopbackOnly ? &in6addr_loopback : NULL,
+               kDNSPort, NULL, kSocketBufferSize_DontSet, false, &sock );
+       require_noerr( err, exit );
+       
+       // Create read source for IPv6 TCP socket.
+       
+       err = SocketContextCreate( sock, me, &sockCtx );
+       require_noerr( err, exit );
+       sock = kInvalidSocketRef;
+       
+       err = DispatchReadSourceCreate( sockCtx->sock, me->queue, _DNSServerTCPReadHandler, SocketContextCancelHandler, sockCtx,
+               &me->readSourceTCPv6 );
+       require_noerr( err, exit );
+       dispatch_resume( me->readSourceTCPv6 );
+       sockCtx = NULL;
+       
+       CFRetain( me );
+       if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Started, me->eventContext );
+       
+exit:
+       ForgetSocket( &sock );
+       if( sockCtx ) SocketContextRelease( sockCtx );
+       if( err ) DNSServerStop( me );
+       CFRelease( me );
+}
+
+//===========================================================================================================================
+//     DNSServerStop
+//===========================================================================================================================
+
+static void    _DNSServerStop( void *inContext );
+static void    _DNSServerStop2( void *inContext );
+
+static void    DNSServerStop( DNSServerRef me )
+{
+       CFRetain( me );
+       dispatch_async_f( me->queue, me, _DNSServerStop );
+}
+
+static void    _DNSServerStop( void *inContext )
+{
+       DNSServerRef const                      me = (DNSServerRef) inContext;
+       DNSDelayedResponse *            resp;
+       
+       dispatch_source_forget( &me->readSourceUDPv4 );
+       dispatch_source_forget( &me->readSourceUDPv6 );
+       dispatch_source_forget( &me->readSourceTCPv4 );
+       dispatch_source_forget( &me->readSourceTCPv6 );
+       dispatch_source_forget( &me->responseTimer );
+       
+       while( ( resp = me->responseList ) != NULL )
+       {
+               me->responseList = resp->next;
+               DNSScheduledResponseFree( resp );
+       }
+       
+       dispatch_async_f( me->queue, me, _DNSServerStop2 );
+}
+
+static void    _DNSServerStop2( void *inContext )
+{
+       DNSServerRef const              me = (DNSServerRef) inContext;
+       
+       if( !me->stopped )
+       {
+               me->stopped = true;
+               if( me->eventHandler ) me->eventHandler( kDNSServerEvent_Stopped, me->eventContext );
+               CFRelease( me );
+       }
+       CFRelease( me );
+}
+
+//===========================================================================================================================
+//     _DNSServerUDPReadHandler
+//===========================================================================================================================
+
+static OSStatus
+       _DNSServerAnswerQuery(
+               const uint8_t * inQueryPtr,
+               size_t                  inQueryLen,
+               Boolean                 inForTCP,
+               uint8_t **              outResponsePtr,
+               size_t *                outResponseLen );
+
+#define _DNSServerAnswerQueryForUDP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
+       _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, false, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
+
+#define _DNSServerAnswerQueryForTCP( IN_QUERY_PTR, IN_QUERY_LEN, IN_RESPONSE_PTR, IN_RESPONSE_LEN ) \
+       _DNSServerAnswerQuery( IN_QUERY_PTR, IN_QUERY_LEN, true, IN_RESPONSE_PTR, IN_RESPONSE_LEN )
+
+static void    _DNSServerUDPDelayedSend( void *inContext );
+
+static void    _DNSServerUDPReadHandler( void *inContext )
+{
+       OSStatus                                        err;
+       SocketContext * const           sockCtx         = (SocketContext *) inContext;
+       DNSServerRef const                      me                      = (DNSServerRef) sockCtx->userContext;
+       struct timeval                          now;
+       ssize_t                                         n;
+       sockaddr_ip                                     clientAddr;
+       socklen_t                                       clientAddrLen;
+       uint8_t *                                       responsePtr     = NULL; // malloc'd
+       size_t                                          responseLen;
+       uint8_t                                         msg[ 512 ];
+       
+       gettimeofday( &now, NULL );
+       
+       // Receive message.
+       
+       clientAddrLen = (socklen_t) sizeof( clientAddr );
+       n = recvfrom( sockCtx->sock, (char *) msg, sizeof( msg ), 0, &clientAddr.sa, &clientAddrLen );
+       err = map_socket_value_errno( sockCtx->sock, n >= 0, n );
+       require_noerr( err, exit );
+       
+       ds_ulog( kLogLevelInfo, "UDP server received %zd bytes from %##a at %{du:time}.\n", n, &clientAddr, &now );
+       
+       if( n < kDNSHeaderLength )
+       {
+               ds_ulog( kLogLevelInfo, "UDP DNS message is too small (%zd < %d).\n", n, kDNSHeaderLength );
+               goto exit;
+       }
+       
+       ds_ulog( kLogLevelInfo, "UDP received message:\n\n%1{du:dnsmsg}", msg, (size_t) n );
+       
+       // Create response.
+       
+       err = _DNSServerAnswerQueryForUDP( msg, (size_t) n, &responsePtr, &responseLen );
+       require_noerr_quiet( err, exit );
+       
+       // Schedule response.
+       
+       if( me->responseDelayMs > 0 )
+       {
+               DNSDelayedResponse *            resp;
+               DNSDelayedResponse **           ptr;
+               DNSDelayedResponse *            newResp;
+               
+               newResp = (DNSDelayedResponse *) calloc( 1, sizeof( *newResp ) );
+               require_action( newResp, exit, err = kNoMemoryErr );
+               
+               SockAddrCopy( &clientAddr, &newResp->clientAddr );
+               newResp->targetTicks    = UpTicks() + MillisecondsToUpTicks( (uint64_t) me->responseDelayMs );
+               newResp->msgLen                 = responseLen;
+               newResp->msgPtr                 = responsePtr;
+               responsePtr = NULL;
+               
+               for( ptr = &me->responseList; ( resp = *ptr ) != NULL; ptr = &resp->next )
+               {
+                       if( newResp->targetTicks < resp->targetTicks ) break;
+               }
+               
+               newResp->next = resp;
+               *ptr = newResp;
+               
+               if( me->responseList == newResp )
+               {
+                       dispatch_source_forget( &me->responseTimer );
+                       
+                       err = DispatchTimerCreate( dispatch_time_milliseconds( me->responseDelayMs ), DISPATCH_TIME_FOREVER,
+                               ( (uint64_t) me->responseDelayMs ) * kNanosecondsPerMillisecond / 10, me->queue,
+                               _DNSServerUDPDelayedSend, NULL, sockCtx, &me->responseTimer );
+                       require_noerr( err, exit );
+                       dispatch_resume( me->responseTimer );
+               }
+       }
+       else
+       {
+               ds_ulog( kLogLevelInfo, "UDP sending %zu byte response:\n\n%1{du:dnsmsg}", responseLen, responsePtr, responseLen );
+               
+               n = sendto( sockCtx->sock, (char *) responsePtr, responseLen, 0, &clientAddr.sa, clientAddrLen );
+               err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) responseLen, n );
+               require_noerr( err, exit );
+       }
+       
+exit:
+       FreeNullSafe( responsePtr );
+       return;
+}
+
+static void    _DNSServerUDPDelayedSend( void *inContext )
+{
+       OSStatus                                        err;
+       SocketContext * const           sockCtx         = (SocketContext *) inContext;
+       DNSServerRef const                      me                      = (DNSServerRef) sockCtx->userContext;
+       DNSDelayedResponse *            resp;
+       ssize_t                                         n;
+       uint64_t                                        nowTicks;
+       DNSDelayedResponse *            freeList        = NULL;
+       
+       dispatch_source_forget( &me->responseTimer );
+       
+       nowTicks = UpTicks();
+       while( ( resp = me->responseList ) != NULL )
+       {
+               if( resp->targetTicks > nowTicks ) break;
+               me->responseList = resp->next;
+               
+               ds_ulog( kLogLevelInfo, "UDP sending %zu byte response (delayed):\n\n%1{du:dnsmsg}",
+                       resp->msgLen, resp->msgPtr, resp->msgLen );
+               
+               n = sendto( sockCtx->sock, (char *) resp->msgPtr, resp->msgLen, 0, &resp->clientAddr.sa,
+                       SockAddrGetSize( &resp->clientAddr ) );
+               err = map_socket_value_errno( sockCtx->sock, n == (ssize_t) resp->msgLen, n );
+               check_noerr( err );
+               
+               resp->next      = freeList;
+               freeList        = resp;
+               nowTicks = UpTicks();
+       }
+       
+       if( ( resp = me->responseList ) != NULL )
+       {
+               uint64_t                remainingNs;
+               
+               remainingNs = UpTicksToNanoseconds( resp->targetTicks - nowTicks );
+               if( remainingNs > INT64_MAX ) remainingNs = INT64_MAX;
+               
+               err = DispatchTimerCreate( dispatch_time( DISPATCH_TIME_NOW, (int64_t) remainingNs ), DISPATCH_TIME_FOREVER, 0,
+                       me->queue, _DNSServerUDPDelayedSend, NULL, sockCtx, &me->responseTimer );
+               require_noerr( err, exit );
+               dispatch_resume( me->responseTimer );
+       }
+       
+exit:
+       while( ( resp = freeList ) != NULL )
+       {
+               freeList = resp->next;
+               DNSScheduledResponseFree( resp );
+       }
+}
+
+//===========================================================================================================================
+//     _DNSServerAnswerQuery
+//===========================================================================================================================
+
+#define kLabelPrefix_Alias                     "alias"
+#define kLabelPrefix_AliasTTL          "alias-ttl"
+#define kLabelPrefix_Count                     "count"
+#define kLabelPrefix_TTL                       "ttl"
+#define kLabel_IPv4                                    "ipv4"
+#define kLabel_IPv6                                    "ipv6"
+
+#define kMaxAliasTTLCount              ( ( kDomainLabelLengthMax - sizeof_string( kLabelPrefix_AliasTTL ) ) / 2 )
+
+static OSStatus
+       _DNSServerInitializeResponseMessage(
+               DataBuffer *    inDB,
+               unsigned int    inID,
+               unsigned int    inFlags,
+               const uint8_t * inQName,
+               unsigned int    inQType,
+               unsigned int    inQClass );
+static OSStatus
+       _DNSServerAnswerQueryDynamically(
+               const uint8_t * inQName,
+               unsigned int    inQType,
+               unsigned int    inQClass,
+               Boolean                 inForTCP,
+               DataBuffer *    inDB );
+
+static OSStatus
+       _DNSServerAnswerQuery(
+               const uint8_t * const   inQueryPtr,
+               const size_t                    inQueryLen,
+               Boolean                                 inForTCP,
+               uint8_t **                              outResponsePtr,
+               size_t *                                outResponseLen )
+{
+       OSStatus                                        err;
+       DataBuffer                                      dataBuf;
+       const uint8_t *                         ptr;
+       const uint8_t * const           queryEnd = &inQueryPtr[ inQueryLen ];
+       const DNSHeader *                       qhdr;
+       unsigned int                            msgID, qflags, qtype, qclass, rflags;
+       uint8_t                                         qname[ kDomainNameLengthMax ];
+       
+       DataBuffer_Init( &dataBuf, NULL, 0, kDNSMaxTCPMessageSize );
+       
+       require_action_quiet( inQueryLen >= kDNSHeaderLength, exit, err = kUnderrunErr );
+       
+       qhdr    = (const DNSHeader *) inQueryPtr;
+       msgID   = DNSHeaderGetID( qhdr );
+       qflags  = DNSHeaderGetFlags( qhdr );
+       
+       // Minimal checking of the query message's header.
+       
+       if( ( qflags & kDNSHeaderFlag_Response ) ||                                     // The message must be a query, not a response.
+               ( DNSFlagsGetOpCode( qflags ) != kDNSOpCode_Query ) ||  // OPCODE must be QUERY (standard query).
+               ( DNSHeaderGetQuestionCount( qhdr ) != 1 ) )                    // There should be a single question.
+       {
+               err = kRequestErr;
+               goto exit;
+       }
+       
+       // Get QNAME.
+       
+       ptr = (const uint8_t *) &qhdr[ 1 ];
+       err = DNSMessageExtractDomainName( inQueryPtr, inQueryLen, ptr, qname, &ptr );
+       require_noerr( err, exit );
+       
+       // Get QTYPE and QCLASS.
+       
+       require_action_quiet( ( queryEnd - ptr ) >= 4, exit, err = kUnderrunErr );
+       qtype   = DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields *) ptr );
+       qclass  = DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields *) ptr );
+       ptr += 4;
+       
+       // Create a tentative response message.
+       
+       rflags = kDNSHeaderFlag_Response;
+       if( qflags & kDNSHeaderFlag_RecursionDesired ) rflags |= kDNSHeaderFlag_RecursionDesired;
+       DNSFlagsSetOpCode( rflags, kDNSOpCode_Query );
+       
+       err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
+       require_noerr( err, exit );
+       
+       err = _DNSServerAnswerQueryDynamically( qname, qtype, qclass, inForTCP, &dataBuf );
+       if( err )
+       {
+               DNSFlagsSetRCode( rflags, kDNSRCode_ServerFailure );
+               err = _DNSServerInitializeResponseMessage( &dataBuf, msgID, rflags, qname, qtype, qclass );
+               require_noerr( err, exit );
+       }
+       
+       err = DataBuffer_Detach( &dataBuf, outResponsePtr, outResponseLen );
+       require_noerr( err, exit );
+       
+exit:
+       DataBuffer_Free( &dataBuf );
+       return( err );
+}
+
+static OSStatus
+       _DNSServerInitializeResponseMessage(
+               DataBuffer *    inDB,
+               unsigned int    inID,
+               unsigned int    inFlags,
+               const uint8_t * inQName,
+               unsigned int    inQType,
+               unsigned int    inQClass )
+{
+       OSStatus                                        err;
+       DNSHeader                                       header;
+       DNSQuestionFixedFields          fields;
+       
+       DataBuffer_Reset( inDB );
+       
+       memset( &header, 0, sizeof( header ) );
+       DNSHeaderSetID( &header, inID );
+       DNSHeaderSetFlags( &header, inFlags );
+       DNSHeaderSetQuestionCount( &header, 1 );
+       
+       err = DataBuffer_Append( inDB, &header, sizeof( header ) );
+       require_noerr( err, exit );
+       
+       err = DataBuffer_Append( inDB, inQName, DomainNameLength( inQName ) );
+       require_noerr( err, exit );
+       
+       DNSQuestionFixedFieldsInit( &fields, inQType, inQClass );
+       err = DataBuffer_Append( inDB, &fields, sizeof( fields ) );
+       require_noerr( err, exit );
+       
+exit:
+       return( err );
+}
+
+static OSStatus
+       _DNSServerAnswerQueryDynamically(
+               const uint8_t * const   inQName,
+               const unsigned int              inQType,
+               const unsigned int              inQClass,
+               const Boolean                   inForTCP,
+               DataBuffer * const              inDB )
+{
+       OSStatus                        err;                                                    // General-purpose error variable.
+       const uint8_t *         labelPtr;                                               // QNAME label pointer.
+       size_t                          labelLen;                                               // QNAME label length.
+       DNSHeader *                     hdr;                                                    // Response header pointer.
+       unsigned int            flags;                                                  // Response header flags.
+       unsigned int            rcode;                                                  // Response header response code.
+       unsigned int            answerCount             = 0;                    // Number of answers contained in response.
+       int32_t                         aliasCount              = -1;                   // Arg from "alias" label. Valid values are in [2 .. 2^31 - 1].
+       int                                     count                   = -1;                   // First arg from "count" label. Valid values are in [1 .. 255].
+       int                                     randCount               = -1;                   // Second arg from "count" label. Valid values are in [1 .. 255].
+       int32_t                         ttl                             = -1;                   // Arg from "ttl" label. Valid values are in [0 .. 2^31 - 1].
+       uint32_t                        aliasTTLs[ kMaxAliasTTLCount ]; // Args from "alias-ttl" label. Valid values are in [0 .. 2^31 - 1].
+       int                                     i;                                                              // General-purpose array index.
+       Boolean                         useAliasTTLs    = false;                // True if QNAME contained a valid "alias-ttl" label.
+       Boolean                         nameExists              = false;                // True if name specified by QNAME exists.
+       Boolean                         nameHasA                = false;                // True if name specified by QNAME has an A record.
+       Boolean                         nameHasAAAA             = false;                // True if name specified by QNAME has a AAAA record.
+       Boolean                         notImplemented  = false;                // True if the kind of the query is not supported.
+       Boolean                         truncated               = false;                // True if the response message is truncated.
+       uint8_t                         namePtr[ 2 ];                                   // Name compression pointer.
+       
+       if( inQClass != kDNSServiceClass_IN )
+       {
+               notImplemented = true;
+               goto done;
+       }
+       
+       for( labelPtr = inQName; ( labelLen = *labelPtr ) != 0; labelPtr += ( 1 + labelLen ) )
+       {
+               const char * const              labelStr = (const char *) &labelPtr[ 1 ];
+               const char *                    next;
+               long long                               arg;
+               int                                             n;
+               
+               require_action( labelLen <= kDomainNameLengthMax, exit, err = kUnexpectedErr );
+               
+               // Check if the first label is a valid alias TTL sequence label.
+               
+               if( ( labelPtr == inQName ) && ( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_AliasTTL ) == 0 ) )
+               {
+                       const char *                    src                     = &labelStr[ sizeof_string( kLabelPrefix_AliasTTL ) ];
+                       const char * const              end                     = &labelStr[ labelLen ];
+                       int                                             argCount        = 0;
+                       
+                       while( src < end )
+                       {
+                               n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next );
+                               if( n != 1 ) break;
+                               if( ( arg < 0 ) || ( arg > INT32_MAX ) ) break; // TTL must be >= 0 and <= (2^31 - 1).
+                               aliasTTLs[ argCount++ ] = (uint32_t) arg;
+                               src = next;
+                       }
+                       if( ( argCount > 0 ) && ( src == end ) )
+                       {
+                               aliasCount              = argCount;
+                               useAliasTTLs    = true;
+                               continue;
+                       }
+               }
+               
+               // Check if the first label is a valid alias label.
+               
+               if( ( labelPtr == inQName ) && ( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_Alias ) == 0 ) )
+               {
+                       const char *                    src = &labelStr[ sizeof_string( kLabelPrefix_Alias ) ];
+                       const char * const              end = &labelStr[ labelLen ];
+                       
+                       if( src == end )
+                       {
+                               aliasCount = 1;
+                               continue;
+                       }
+                       
+                       n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next );
+                       if( ( n == 1 ) && ( next == end ) )
+                       {
+                               if( ( arg < 2 ) || ( arg > INT32_MAX ) ) break; // Alias count must be >= 2 and <= (2^31 - 1).
+                               aliasCount = (int32_t) arg;
+                               continue;
+                       }
+               }
+               
+               // Check if the label is a valid count label.
+               
+               if( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_Count ) == 0  )
+               {
+                       const char *                    src = &labelStr[ sizeof_string( kLabelPrefix_Count ) ];
+                       const char * const              end = &labelStr[ labelLen ];
+                       
+                       n = SNScanF( src, (size_t)( end - src ), "-%3lld%#n", &arg, &next );
+                       if( n == 1 )
+                       {
+                               if( count > 0 ) break;                                          // Count cannot be specified more than once.
+                               if( ( arg < 1 ) || ( arg > 255 ) ) break;       // Count must be >= 1 and <= 255.
+                               count = (int) arg;
+                               
+                               src = next;
+                               if( src < end )
+                               {
+                                       n = SNScanF( src, (size_t)( end - src ), "-%3lld%#n", &arg, &next );
+                                       if( ( n != 1 ) || ( next != end ) ) break;
+                                       if( ( arg < count ) || ( arg > 255 ) ) break;   // Rand count must be >= count and <= 255.
+                                       randCount = (int) arg;
+                               }
+                               continue;
+                       }
+               }
+               
+               // Check if the label is a valid tag label.
+               
+               if( strnicmp_prefix( labelStr, labelLen, "tag-" ) == 0  ) continue;
+               
+               // Check if the label is a valid TTL label.
+               
+               if( strnicmp_prefix( labelStr, labelLen, kLabelPrefix_TTL ) == 0  )
+               {
+                       const char *                    src = &labelStr[ sizeof_string( kLabelPrefix_TTL ) ];
+                       const char * const              end = &labelStr[ labelLen ];
+                       
+                       n = SNScanF( src, (size_t)( end - src ), "-%10lld%#n", &arg, &next );
+                       if( ( n == 1 ) && ( next == end ) )
+                       {
+                               if( ttl >= 0 ) break;                                                   // TTL cannot be specified more than once.
+                               if( ( arg < 0 ) || ( arg > INT32_MAX ) ) break; // TTL must be >= 0 and <= (2^31 - 1).
+                               ttl = (int32_t) arg;
+                               continue;
+                       }
+               }
+               
+               // Check if the label is a valid IPv4 or IPv6 label.
+               
+               if( MemIEqual( labelStr, labelLen, kLabel_IPv4, sizeof_string( kLabel_IPv4 ) ) )
+               {
+                       if( nameHasA || nameHasAAAA ) break;    // Valid names have at most one IPv4 or IPv6 label.
+                       nameHasA = true;
+                       continue;
+               }
+               if( MemIEqual( labelStr, labelLen, kLabel_IPv6, sizeof_string( kLabel_IPv6 ) ) )
+               {
+                       if( nameHasA || nameHasAAAA ) break;    // Valid names have at most one IPv4 or IPv6 label.
+                       nameHasAAAA = true;
+                       continue;
+               }
+               
+               // If the remaining labels are equal to "d.test.", the name exists.
+               
+               if( DomainNameEqual( labelPtr, (const uint8_t *) "\x01" "d" "\x04" "test" ) ) nameExists = true;
+               break;
+       }
+       require_quiet( nameExists, done );
+       
+       // Set default values for count and TTL, if those labels were present.
+       
+       if( count <= 0 ) count = 1;
+       check( ( gDNSServer_DefaultTTL >= 0 ) && ( gDNSServer_DefaultTTL <= INT32_MAX ) );
+       if( ttl < 0 ) ttl = gDNSServer_DefaultTTL;
+       
+       // Names that don't specify v4 or v6 have both A and AAAA records.
+       
+       if( !nameHasA && !nameHasAAAA )
+       {
+               nameHasA        = true;
+               nameHasAAAA     = true;
+       }
+       
+       check( ( count >= 1 ) && ( count <= 255 ) );
+       check( ( randCount <= 0 ) || ( ( randCount >= count ) && ( randCount <= 255 ) ) );
+       
+       if( aliasCount > 0 )
+       {
+               size_t                          nameOffset;
+               uint8_t                         rdataLabel[ 1 + kDomainLabelLengthMax + 1  ];
+               
+               // If aliasCount is non-zero, then the first label of QNAME is either "alias" or "alias-<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
@@ -7963,6 +12293,18 @@ static Boolean  DomainNameEqual( const uint8_t *inName1, const uint8_t *inName2 )
        return( true );
 }
 
+//===========================================================================================================================
+//     DomainNameLength
+//===========================================================================================================================
+
+static size_t  DomainNameLength( const uint8_t * const inName )
+{
+       const uint8_t *         ptr;
+       
+       for( ptr = inName; *ptr != 0; ptr += ( 1 + *ptr ) ) {}
+       return( (size_t)( ptr - inName ) + 1 );
+}
+
 //===========================================================================================================================
 //     DomainNameFromString
 //===========================================================================================================================
@@ -8039,7 +12381,7 @@ exit:
 }
 
 //===========================================================================================================================
-//     PrintDNSMessage
+//     DNSMessageToText
 //===========================================================================================================================
 
 #define DNSFlagsOpCodeToString( X ) (                                  \
@@ -8059,12 +12401,17 @@ exit:
        ( (X) == kDNSRCode_Refused )            ? "Refused"             :       \
                                                                                  "???" )
 
-#define DNSFlagsGetOpCode( X )         ( ( (X) >> 11 ) & 0x0F )
-#define DNSFlagsGetRCode( X )          (   (X)         & 0x0F )
-
-static OSStatus        PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const Boolean inIsMDNS, const Boolean inPrintRaw )
+static OSStatus
+       DNSMessageToText(
+               const uint8_t * inMsgPtr,
+               size_t                  inMsgLen,
+               const Boolean   inMDNS,
+               const Boolean   inPrintRaw,
+               char **                 outText )
 {
        OSStatus                                        err;
+       DataBuffer                                      dataBuf;
+       size_t                                          len;
        const DNSHeader *                       hdr;
        const uint8_t * const           msgEnd = inMsgPtr + inMsgLen;
        const uint8_t *                         ptr;
@@ -8072,6 +12419,9 @@ static OSStatus  PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const
        unsigned int                            questionCount, answerCount, authorityCount, additionalCount, i, totalRRCount;
        char                                            nameStr[ kDNSServiceMaxDomainName ];
        
+       DataBuffer_Init( &dataBuf, NULL, 0, SIZE_MAX );
+       #define _Append( ... )          do { err = DataBuffer_AppendF( &dataBuf, __VA_ARGS__ ); require_noerr( err, exit ); } while( 0 )
+       
        require_action_quiet( inMsgLen >= kDNSHeaderLength, exit, err = kSizeErr );
        
        hdr                             = (DNSHeader *) inMsgPtr;
@@ -8084,24 +12434,26 @@ static OSStatus        PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const
        opcode                  = DNSFlagsGetOpCode( flags );
        rcode                   = DNSFlagsGetRCode( flags );
        
-       FPrintF( stdout, "ID:               0x%04X (%u)\n", id, id );
-       FPrintF( stdout, "Flags:            0x%04X %c/%s %cAA%cTC%cRD%cRA %s\n",
+       _Append( "ID:               0x%04X (%u)\n", id, id );
+       _Append( "Flags:            0x%04X %c/%s %cAA%cTC%cRD%cRA%?s%?s %s\n",
                flags,
                ( flags & kDNSHeaderFlag_Response )                             ? 'R' : 'Q', DNSFlagsOpCodeToString( opcode ),
                ( flags & kDNSHeaderFlag_AuthAnswer )                   ? ' ' : '!',
                ( flags & kDNSHeaderFlag_Truncation )                   ? ' ' : '!',
                ( flags & kDNSHeaderFlag_RecursionDesired )             ? ' ' : '!',
                ( flags & kDNSHeaderFlag_RecursionAvailable )   ? ' ' : '!',
+               !inMDNS, ( flags & kDNSHeaderFlag_AuthenticData )               ? " AD" : "!AD",
+               !inMDNS, ( flags & kDNSHeaderFlag_CheckingDisabled )    ? " CD" : "!CD",
                DNSFlagsRCodeToString( rcode ) );
-       FPrintF( stdout, "Question count:   %u\n", questionCount );
-       FPrintF( stdout, "Answer count:     %u\n", answerCount );
-       FPrintF( stdout, "Authority count:  %u\n", authorityCount );
-       FPrintF( stdout, "Additional count: %u\n", additionalCount );
+       _Append( "Question count:   %u\n", questionCount );
+       _Append( "Answer count:     %u\n", answerCount );
+       _Append( "Authority count:  %u\n", authorityCount );
+       _Append( "Additional count: %u\n", additionalCount );
        
-       ptr = (uint8_t *)( hdr + 1 );
+       ptr = (const uint8_t *) &hdr[ 1 ];
        for( i = 0; i < questionCount; ++i )
        {
-               unsigned int            qType, qClass;
+               unsigned int            qtype, qclass;
                Boolean                         isQU;
                
                err = DNSMessageExtractDomainNameString( inMsgPtr, inMsgLen, ptr, nameStr, &ptr );
@@ -8113,20 +12465,18 @@ static OSStatus        PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const
                        goto exit;
                }
                
-               qType = ReadBig16( ptr );
-               ptr += 2;
-               qClass = ReadBig16( ptr );
-               ptr += 2;
+               qtype   = DNSQuestionFixedFieldsGetType( (const DNSQuestionFixedFields *) ptr );
+               qclass  = DNSQuestionFixedFieldsGetClass( (const DNSQuestionFixedFields *) ptr );
+               ptr += 4;
                
-               isQU = ( inIsMDNS && ( qClass & kQClassUnicastResponseBit ) ) ? true : false;
-               if( inIsMDNS ) qClass &= ~kQClassUnicastResponseBit;
+               isQU = ( inMDNS && ( qclass & kQClassUnicastResponseBit ) ) ? true : false;
+               if( inMDNS ) qclass &= ~kQClassUnicastResponseBit;
                
-               if( i == 0 ) FPrintF( stdout, "\nQUESTION SECTION\n" );
+               if( i == 0 ) _Append( "\nQUESTION SECTION\n" );
                
-               FPrintF( stdout, "%s %2s %?2s%?2u %-5s\n",
-                       nameStr, inIsMDNS ? ( isQU ? "QU" : "QM" ) : "",
-                       ( qClass == kDNSServiceClass_IN ), "IN", ( qClass != kDNSServiceClass_IN ), qClass,
-                       RecordTypeToString( qType ) );
+               _Append( "%s %2s %?2s%?2u %-5s\n",
+                       nameStr, inMDNS ? ( isQU ? "QU" : "QM" ) : "",
+                       ( qclass == kDNSServiceClass_IN ), "IN", ( qclass != kDNSServiceClass_IN ), qclass, RecordTypeToString( qtype ) );
        }
        
        totalRRCount = answerCount + authorityCount + additionalCount;
@@ -8147,8 +12497,8 @@ static OSStatus  PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const
                err = DomainNameToString( name, NULL, nameStr, NULL );
                require_noerr( err, exit );
                
-               cacheFlush = ( inIsMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
-               if( inIsMDNS ) class &= ~kRRClassCacheFlushBit;
+               cacheFlush = ( inMDNS && ( class & kRRClassCacheFlushBit ) ) ? true : false;
+               if( inMDNS ) class &= ~kRRClassCacheFlushBit;
                
                rdataStr = NULL;
                if( !inPrintRaw ) DNSRecordDataToString( rdataPtr, rdataLen, type, inMsgPtr, inMsgLen, &rdataStr );
@@ -8158,20 +12508,26 @@ static OSStatus        PrintDNSMessage( const uint8_t *inMsgPtr, size_t inMsgLen, const
                        require_action( rdataStr, exit, err = kNoMemoryErr );
                }
                
-               if(      answerCount     && ( i ==   0                              ) ) FPrintF( stdout, "\nANSWER SECTION\n" );
-               else if( authorityCount  && ( i ==   answerCount                    ) ) FPrintF( stdout, "\nAUTHORITY SECTION\n" );
-               else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) FPrintF( stdout, "\nADDITIONAL SECTION\n" );
+               if(      answerCount     && ( i ==   0                              ) ) _Append( "\nANSWER SECTION\n" );
+               else if( authorityCount  && ( i ==   answerCount                    ) ) _Append( "\nAUTHORITY SECTION\n" );
+               else if( additionalCount && ( i == ( answerCount + authorityCount ) ) ) _Append( "\nADDITIONAL SECTION\n" );
                
-               FPrintF( stdout, "%-42s %6u %2s %?2s%?2u %-5s %s\n",
+               _Append( "%-42s %6u %2s %?2s%?2u %-5s %s\n",
                        nameStr, ttl, cacheFlush ? "CF" : "",
                        ( class == kDNSServiceClass_IN ), "IN", ( class != kDNSServiceClass_IN ), class,
                        RecordTypeToString( type ), rdataStr );
                free( rdataStr );
        }
-       FPrintF( stdout, "\n" );
-       err = kNoErr;
+       _Append( "\n" );
+       
+       err = DataBuffer_Append( &dataBuf, "", 1 );
+       require_noerr( err, exit );
+       
+       err = DataBuffer_Detach( &dataBuf, (uint8_t **) outText, &len );
+       require_noerr( err, exit );
        
 exit:
+       DataBuffer_Free( &dataBuf );
        return( err );
 }
 
@@ -8194,21 +12550,18 @@ static OSStatus
        uint8_t *                               ptr;
        size_t                                  msgLen;
        
-       WriteBig16( hdr->id,                            inMsgID );
-       WriteBig16( hdr->flags,                         inFlags );
-       WriteBig16( hdr->questionCount,         1 );
-       WriteBig16( hdr->answerCount,           0 );
-       WriteBig16( hdr->authorityCount,        0 );
-       WriteBig16( hdr->additionalCount,       0 );
+       memset( hdr, 0, sizeof( *hdr ) );
+       DNSHeaderSetID( hdr, inMsgID );
+       DNSHeaderSetFlags( hdr, inFlags );
+       DNSHeaderSetQuestionCount( hdr, 1 );
        
        ptr = (uint8_t *)( hdr + 1 );
        err = DomainNameFromString( ptr, inQName, &ptr );
        require_noerr_quiet( err, exit );
        
-       WriteBig16( ptr, inQType );
-       ptr += 2;
-       WriteBig16( ptr, inQClass );
-       ptr += 2;
+       DNSQuestionFixedFieldsInit( (DNSQuestionFixedFields *) ptr, inQType, inQClass );
+       ptr += 4;
+       
        msgLen = (size_t)( ptr - inMsg );
        check( msgLen <= kDNSQueryMessageMaxLen );
        
@@ -8246,21 +12599,23 @@ exit:
 }
 
 //===========================================================================================================================
-//     DispatchReadSourceCreate
+//     DispatchSocketSourceCreate
 //===========================================================================================================================
 
 static OSStatus
-       DispatchReadSourceCreate(
-               SocketRef                       inSock,
-               DispatchHandler         inEventHandler,
-               DispatchHandler         inCancelHandler,
-               void *                          inContext,
-               dispatch_source_t *     outSource )
+       DispatchSocketSourceCreate(
+               SocketRef                               inSock,
+               dispatch_source_type_t  inType,
+               dispatch_queue_t                inQueue,
+               DispatchHandler                 inEventHandler,
+               DispatchHandler                 inCancelHandler,
+               void *                                  inContext,
+               dispatch_source_t *             outSource )
 {
        OSStatus                                err;
        dispatch_source_t               source;
        
-       source = dispatch_source_create( DISPATCH_SOURCE_TYPE_READ, (uintptr_t) inSock, 0, dispatch_get_main_queue() );
+       source = dispatch_source_create( inType, (uintptr_t) inSock, 0, inQueue ? inQueue : dispatch_get_main_queue() );
        require_action( source, exit, err = kUnknownErr );
        
        dispatch_set_context( source, inContext );
@@ -8283,6 +12638,7 @@ static OSStatus
                dispatch_time_t         inStart,
                uint64_t                        inIntervalNs,
                uint64_t                        inLeewayNs,
+               dispatch_queue_t        inQueue,
                DispatchHandler         inEventHandler,
                DispatchHandler         inCancelHandler,
                void *                          inContext,
@@ -8291,7 +12647,7 @@ static OSStatus
        OSStatus                                err;
        dispatch_source_t               timer;
        
-       timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue() );
+       timer = dispatch_source_create( DISPATCH_SOURCE_TYPE_TIMER, 0, 0, inQueue ? inQueue : dispatch_get_main_queue() );
        require_action( timer, exit, err = kUnknownErr );
        
        dispatch_source_set_timer( timer, inStart, inIntervalNs, inLeewayNs );
@@ -8306,6 +12662,38 @@ exit:
        return( err );
 }
 
+//===========================================================================================================================
+//     DispatchProcessMonitorCreate
+//===========================================================================================================================
+
+static OSStatus
+       DispatchProcessMonitorCreate(
+               pid_t                           inPID,
+               unsigned long           inFlags,
+               dispatch_queue_t        inQueue,
+               DispatchHandler         inEventHandler,
+               DispatchHandler         inCancelHandler,
+               void *                          inContext,
+               dispatch_source_t *     outMonitor )
+{
+       OSStatus                                err;
+       dispatch_source_t               monitor;
+       
+       monitor = dispatch_source_create( DISPATCH_SOURCE_TYPE_PROC, (uintptr_t) inPID, inFlags,
+               inQueue ? inQueue : dispatch_get_main_queue() );
+       require_action( monitor, exit, err = kUnknownErr );
+       
+       dispatch_set_context( monitor, inContext );
+       dispatch_source_set_event_handler_f( monitor, inEventHandler );
+       dispatch_source_set_cancel_handler_f( monitor, inCancelHandler );
+       
+       *outMonitor = monitor;
+       err = kNoErr;
+       
+exit:
+       return( err );
+}
+
 //===========================================================================================================================
 //     ServiceTypeDescription
 //===========================================================================================================================
@@ -8368,16 +12756,59 @@ static const char *    ServiceTypeDescription( const char *inName )
        return( NULL );
 }
 
+//===========================================================================================================================
+//     SocketContextCreate
+//===========================================================================================================================
+
+static OSStatus        SocketContextCreate( SocketRef inSock, void * inUserContext, SocketContext **outContext )
+{
+       OSStatus                        err;
+       SocketContext *         context;
+       
+       context = (SocketContext *) calloc( 1, sizeof( *context ) );
+       require_action( context, exit, err = kNoMemoryErr );
+       
+       context->refCount               = 1;
+       context->sock                   = inSock;
+       context->userContext    = inUserContext;
+       
+       *outContext = context;
+       err = kNoErr;
+       
+exit:
+       return( err );
+}
+
+//===========================================================================================================================
+//     SocketContextRetain
+//===========================================================================================================================
+
+static SocketContext * SocketContextRetain( SocketContext *inContext )
+{
+       ++inContext->refCount;
+       return( inContext );
+}
+
+//===========================================================================================================================
+//     SocketContextRelease
+//===========================================================================================================================
+
+static void    SocketContextRelease( SocketContext *inContext )
+{
+       if( --inContext->refCount == 0 )
+       {
+               ForgetSocket( &inContext->sock );
+               free( inContext );
+       }
+}
+
 //===========================================================================================================================
 //     SocketContextCancelHandler
 //===========================================================================================================================
 
 static void    SocketContextCancelHandler( void *inContext )
 {
-       SocketContext * const           context = (SocketContext *) inContext;
-       
-       ForgetSocket( &context->sock );
-       free( context );
+       SocketContextRelease( (SocketContext *) inContext );
 }
 
 //===========================================================================================================================
@@ -8421,6 +12852,115 @@ exit:
        return( err );
 }
 
+//===========================================================================================================================
+//     StringToLongLong
+//===========================================================================================================================
+
+static OSStatus        StringToLongLong( const char *inString, long long *outValue )
+{
+       OSStatus                err;
+       long long               value;
+       char *                  endPtr;
+       
+       set_errno_compat( 0 );
+       value = strtol( inString, &endPtr, 0 );
+       err = errno_compat();
+       if( ( ( value == LLONG_MIN ) || ( value == LLONG_MAX ) ) && ( err == ERANGE ) ) goto exit;
+       require_action_quiet( ( *endPtr == '\0' ) && ( endPtr != inString ), exit, err = kParamErr );
+       
+       *outValue = value;
+       err = kNoErr;
+       
+exit:
+       return( err );
+}
+
+//===========================================================================================================================
+//     StringToARecordData
+//===========================================================================================================================
+
+static OSStatus        StringToARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
+{
+       OSStatus                        err;
+       uint32_t *                      addrPtr;
+       const size_t            addrLen = sizeof( *addrPtr );
+       const char *            end;
+       
+       addrPtr = (uint32_t *) malloc( addrLen );
+       require_action( addrPtr, exit, err = kNoMemoryErr );
+       
+       err = StringToIPv4Address( inString, kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix, addrPtr,
+               NULL, NULL, NULL, &end );
+       if( !err && ( *end != '\0' ) ) err = kMalformedErr;
+       require_noerr_quiet( err, exit );
+       
+       *addrPtr = HostToBig32( *addrPtr );
+       
+       *outPtr = (uint8_t *) addrPtr;
+       addrPtr = NULL;
+       *outLen = addrLen;
+       
+exit:
+       FreeNullSafe( addrPtr );
+       return( err );
+}
+
+//===========================================================================================================================
+//     StringToAAAARecordData
+//===========================================================================================================================
+
+static OSStatus        StringToAAAARecordData( const char *inString, uint8_t **outPtr, size_t *outLen )
+{
+       OSStatus                        err;
+       uint8_t *                       addrPtr;
+       const size_t            addrLen = 16;
+       const char *            end;
+       
+       addrPtr = (uint8_t *) malloc( addrLen );
+       require_action( addrPtr, exit, err = kNoMemoryErr );
+       
+       err = StringToIPv6Address( inString,
+               kStringToIPAddressFlagsNoPort | kStringToIPAddressFlagsNoPrefix | kStringToIPAddressFlagsNoScope,
+               addrPtr, NULL, NULL, NULL, &end );
+       if( !err && ( *end != '\0' ) ) err = kMalformedErr;
+       require_noerr_quiet( err, exit );
+       
+       *outPtr = addrPtr;
+       addrPtr = NULL;
+       *outLen = addrLen;
+       
+exit:
+       FreeNullSafe( addrPtr );
+       return( err );
+}
+
+//===========================================================================================================================
+//     StringToDomainName
+//===========================================================================================================================
+
+static OSStatus        StringToDomainName( const char *inString, uint8_t **outPtr, size_t *outLen )
+{
+       OSStatus                err;
+       uint8_t *               namePtr;
+       size_t                  nameLen;
+       uint8_t *               end;
+       uint8_t                 nameBuf[ kDomainNameLengthMax ];
+       
+       err = DomainNameFromString( nameBuf, inString, &end );
+       require_noerr_quiet( err, exit );
+       
+       nameLen = (size_t)( end - nameBuf );
+       namePtr = memdup( nameBuf, nameLen );
+       require_action( namePtr, exit, err = kNoMemoryErr );
+       
+       *outPtr = namePtr;
+       namePtr = NULL;
+       if( outLen ) *outLen = nameLen;
+       
+exit:
+       return( err );
+}
+
 #if( TARGET_OS_DARWIN )
 //===========================================================================================================================
 //     GetDefaultDNSServer
@@ -8446,7 +12986,7 @@ static OSStatus  GetDefaultDNSServer( sockaddr_ip *outAddr )
                        addr = resolver->nameserver[ 0 ];
                        break;
                }
-       }
+       }
        require_action_quiet( addr, exit, err = kNotFoundErr );
        
        SockAddrCopy( addr, outAddr );
@@ -8458,6 +12998,20 @@ exit:
 }
 #endif
 
+//===========================================================================================================================
+//     GetCurrentMicroTime
+//===========================================================================================================================
+
+static MicroTime64     GetCurrentMicroTime( void )
+{
+       struct timeval          now;
+       
+       TIMEVAL_ZERO( now );
+       gettimeofday( &now, NULL );
+       
+       return( (MicroTime64) TIMEVAL_USEC64( now ) );
+}
+
 //===========================================================================================================================
 //     SocketWriteAll
 //
@@ -8918,47 +13472,6 @@ exit:
        return( err );
 }
 
-//===========================================================================================================================
-//     StringArray_Append
-//
-//     Note: This was copied from CoreUtils because the StringArray_Append function is currently not exported in the framework.
-//===========================================================================================================================
-
-OSStatus       StringArray_Append( char ***ioArray, size_t *ioCount, const char *inStr )
-{
-       OSStatus                err;
-       char *                  newStr;
-       size_t                  oldCount;
-       size_t                  newCount;
-       char **                 oldArray;
-       char **                 newArray;
-       
-       newStr = strdup( inStr );
-       require_action( newStr, exit, err = kNoMemoryErr );
-       
-       oldCount = *ioCount;
-       newCount = oldCount + 1;
-       newArray = (char **) malloc( newCount * sizeof( *newArray ) );
-       require_action( newArray, exit, err = kNoMemoryErr );
-       
-       if( oldCount > 0 )
-       {
-               oldArray = *ioArray;
-               memcpy( newArray, oldArray, oldCount * sizeof( *oldArray ) );
-               free( oldArray );
-       }
-       newArray[ oldCount ] = newStr;
-       newStr = NULL;
-       
-       *ioArray = newArray;
-       *ioCount = newCount;
-       err = kNoErr;
-       
-exit:
-       if( newStr ) free( newStr );
-       return( err );
-}
-
 //===========================================================================================================================
 //     StringArray_Free
 //
@@ -9150,3 +13663,193 @@ Boolean
        if( outSrc )            *outSrc                 = (const char *) src;
        return( true );
 }
+
+//===========================================================================================================================
+//     _ServerSocketOpenEx2
+//
+//     Note: Based on ServerSocketOpenEx() from CoreUtils. Added parameter to not use SO_REUSEPORT.
+//===========================================================================================================================
+
+static OSStatus
+       _ServerSocketOpenEx2( 
+               int                             inFamily, 
+               int                             inType, 
+               int                             inProtocol, 
+               const void *    inAddr, 
+               int                             inPort, 
+               int *                   outPort, 
+               int                             inRcvBufSize, 
+               Boolean                 inNoPortReuse,
+               SocketRef *             outSock )
+{
+       OSStatus                err;
+       int                             port;
+       SocketRef               sock;
+       int                             name;
+       int                             option;
+       sockaddr_ip             sip;
+       socklen_t               len;
+       
+       port = ( inPort < 0 ) ? -inPort : inPort; // Negated port number means "try this port, but allow dynamic".
+       
+       sock = socket( inFamily, inType, inProtocol );
+       err = map_socket_creation_errno( sock );
+       require_noerr_quiet( err, exit );
+       
+#if( defined( SO_NOSIGPIPE ) )
+       setsockopt( sock, SOL_SOCKET, SO_NOSIGPIPE, &(int){ 1 }, (socklen_t) sizeof( int ) );
+#endif
+       
+       err = SocketMakeNonBlocking( sock );
+       require_noerr( err, exit );
+       
+       // Set receive buffer size. This has to be done on the listening socket *before* listen is called because
+       // accept does not return until after the window scale option is exchanged during the 3-way handshake. 
+       // Since accept returns a new socket, the only way to use a larger window scale option is to set the buffer
+       // size on the listening socket since SO_RCVBUF is inherited by the accepted socket. See UNPv1e3 Section 7.5.
+       
+       err = SocketSetBufferSize( sock, SO_RCVBUF, inRcvBufSize );
+       check_noerr( err );
+       
+       // Allow port or address reuse because we may bind separate IPv4 and IPv6 sockets to the same port.
+       
+       if( ( inType != SOCK_DGRAM ) || !inNoPortReuse )
+       {
+               option = 1;
+               name = ( inType == SOCK_DGRAM ) ? SO_REUSEPORT : SO_REUSEADDR;
+               err = setsockopt( sock, SOL_SOCKET, name, (char *) &option, (socklen_t) sizeof( option ) );
+               err = map_socket_noerr_errno( sock, err );
+               require_noerr( err, exit );
+       }
+       
+       if( inFamily == AF_INET )
+       {
+               // Bind to the port. If it fails, retry with a dynamic port.
+               
+               memset( &sip.v4, 0, sizeof( sip.v4 ) );
+               SIN_LEN_SET( &sip.v4 );
+               sip.v4.sin_family               = AF_INET;
+               sip.v4.sin_port                 = htons( (uint16_t) port );
+               sip.v4.sin_addr.s_addr  = inAddr ? *( (const uint32_t *) inAddr ) : htonl( INADDR_ANY );
+               err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
+               err = map_socket_noerr_errno( sock, err );
+               if( err && ( inPort < 0 ) )
+               {
+                       sip.v4.sin_port = 0;
+                       err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v4 ) );
+                       err = map_socket_noerr_errno( sock, err );
+               }
+               require_noerr( err, exit );
+       }
+#if( defined( AF_INET6 ) )
+       else if( inFamily == AF_INET6 )
+       {
+               // Restrict this socket to IPv6 only because we're going to use a separate socket for IPv4.
+               
+               option = 1;
+               err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, (socklen_t) sizeof( option ) );
+               err = map_socket_noerr_errno( sock, err );
+               require_noerr( err, exit );
+               
+               // Bind to the port. If it fails, retry with a dynamic port.
+               
+               memset( &sip.v6, 0, sizeof( sip.v6 ) );
+               SIN6_LEN_SET( &sip.v6 );
+               sip.v6.sin6_family      = AF_INET6;
+               sip.v6.sin6_port        = htons( (uint16_t) port );
+               sip.v6.sin6_addr        = inAddr ? *( (const struct in6_addr *) inAddr ) : in6addr_any; 
+               err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
+               err = map_socket_noerr_errno( sock, err );
+               if( err && ( inPort < 0 ) )
+               {
+                       sip.v6.sin6_port = 0;
+                       err = bind( sock, &sip.sa, (socklen_t) sizeof( sip.v6 ) );
+                       err = map_socket_noerr_errno( sock, err );
+               }
+               require_noerr( err, exit );
+       }
+#endif
+       else
+       {
+               dlogassert( "Unsupported family: %d", inFamily );
+               err = kUnsupportedErr;
+               goto exit;
+       }
+       
+       if( inType == SOCK_STREAM )
+       {
+               err = listen( sock, SOMAXCONN );
+               err = map_socket_noerr_errno( sock, err );
+               if( err )
+               {
+                       err = listen( sock, 5 );
+                       err = map_socket_noerr_errno( sock, err );
+                       require_noerr( err, exit );
+               }
+       }
+       
+       if( outPort )
+       {
+               len = (socklen_t) sizeof( sip );
+               err = getsockname( sock, &sip.sa, &len );
+               err = map_socket_noerr_errno( sock, err );
+               require_noerr( err, exit );
+               
+               *outPort = SockAddrGetPort( &sip );
+       }
+       *outSock = sock;
+       sock = kInvalidSocketRef;
+       
+exit:
+       ForgetSocket( &sock );
+       return( err );
+}
+
+//===========================================================================================================================
+//     memdup
+//
+//     Note: This was copied from CoreUtils because it's currently not exported in the framework.
+//===========================================================================================================================
+
+void * memdup( const void *inPtr, size_t inLen )
+{
+       void *          mem;
+       
+       mem = malloc( ( inLen > 0 ) ? inLen : 1 ); // If inLen is 0, use 1 since malloc( 0 ) is not well defined.
+       require( mem, exit );
+       if( inLen > 0 ) memcpy( mem, inPtr, inLen );
+       
+exit:
+       return( mem );
+}
+
+#if( !TARGET_OS_WINDOWS )
+//===========================================================================================================================
+//     memicmp
+//
+//     Note: This was copied from CoreUtils because it's currently not exported in the framework.
+//===========================================================================================================================
+
+int    memicmp( const void *inP1, const void *inP2, size_t inLen )
+{
+       const unsigned char *           p1;
+       const unsigned char *           e1;
+       const unsigned char *           p2;
+       int                                                     c1;
+       int                                                     c2;
+       
+       p1 = (const unsigned char *) inP1;
+       e1 = p1 + inLen;
+       p2 = (const unsigned char *) inP2;
+       while( p1 < e1 )
+       {
+               c1 = *p1++;
+               c2 = *p2++;
+               c1 = tolower( c1 );
+               c2 = tolower( c2 );
+               if( c1 < c2 ) return( -1 );
+               if( c1 > c2 ) return(  1 );
+       }
+       return( 0 );
+}
+#endif
index 978c8953fd1b18661b7ea8f8cdafbf3f138aa3ae..d50841c286d25647f7df899282501b5bb832f678 100644 (file)
--- a/Makefile
+++ b/Makefile
 #         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)
 
index 597c4cc0d2bbec34130ebf780197d666a777a4a1..a249b967a96e14e9ff392721009c88c6e9b4961a 100644 (file)
@@ -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;
 
index e1ef261e7b14996040051c653fa0c90fabf389e7..b100a400f87d9c4c57c43ebbe38f117e2945a991 100644 (file)
@@ -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);
index 0788ab67a979a5a66663d1c517b34e179c1750c5..8deada2e5b2b6a74b8410ac2ab52552712538843 100755 (executable)
@@ -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);
                 }
             }
index 68a696ef8ab148b05858884130f598c79886d4ff..d690fd2b95eaf51b73475a5d1ae9e6adee4ebc46 100755 (executable)
@@ -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
index 1962bb19eaa9e757c8f456f061bd8fd258c2d589..511aa3b6eba695deb1ce24c8f30fd454d541e511 100755 (executable)
@@ -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
 };
 
index 64dae891442384224480544c946c81bf50876dcf..cd91f4dae58385b372f578de6076a2604f0ce74f 100755 (executable)
@@ -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
index 2848cdabde7ea99441d1f20d4c20b8115a4c96b6..6ad8e945dd1250387c0cca7dd993fad38ea443c6 100644 (file)
@@ -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);
index 1f4e0ec2f46121964f46c48f700d02b78fd5f1e7..6fef38f852d91229cd1772e3287cab90d9e4ffce 100644 (file)
 #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>
 
index dd670ab1182be6ceaefff850d54e8854ec458f13..2b89bc243830228fdb6a0322195775deb59baa55 100644 (file)
@@ -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.
  * 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"
 
index 004d325990e7a6c7908c61b4cb494f61b03b2ad9..3f683a839727f1bb37bebfae858e029606d01071 100644 (file)
@@ -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/mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist b/mDNSMacOSX/LoggingProfiles/AppleInternal/com.apple.mDNSResponder.plist
new file mode 100644 (file)
index 0000000..72352ce
--- /dev/null
@@ -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/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist b/mDNSMacOSX/LoggingProfiles/com.apple.mDNSResponder.plist
new file mode 100644 (file)
index 0000000..1eba185
--- /dev/null
@@ -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>
index ff419fd8a2f14c3f9dbf10fc4b3810b1bd7e550f..e37722998ab056b0a5908d07dff009bfbb897cfe 100644 (file)
@@ -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);
index e540f3be0347180efe6b4ac03048cb3ae4c5fcfb..dd20bc27b50f548dacc29ad7e5f9f436e731a6c9 100644 (file)
@@ -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/mDNSMacOSX/Private/com.apple.mDNSResponder.plist b/mDNSMacOSX/Private/com.apple.mDNSResponder.plist
deleted file mode 100644 (file)
index 6d403b5..0000000
+++ /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/mDNSMacOSX/Scripts/bonjour-mcast-diagnose b/mDNSMacOSX/Scripts/bonjour-mcast-diagnose
new file mode 100755 (executable)
index 0000000..bda53ba
--- /dev/null
@@ -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/mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump b/mDNSMacOSX/Scripts/bonjour-start-mdns-tcpdump
new file mode 100755 (executable)
index 0000000..2a81b5f
--- /dev/null
@@ -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/mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist b/mDNSMacOSX/Scripts/com.apple.mDNSResponder.mdns-tcpdump.plist
new file mode 100644 (file)
index 0000000..0e48c21
--- /dev/null
@@ -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>
index fa192b6a1e4e7833e165f8c98625061ffb4bb460..e16f1dbfdf82274ed92736fb964933db036a531e 100644 (file)
@@ -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);
 
index a1e804fd5e7bb3e015197da9e8a090c53033542f..c7c3496811312943b36ae36f611a8b1b97687e38 100644 (file)
@@ -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);
index dc90b35dd04a9cc3695e39b4eb98ac1a738cb2fb..7b6cb89748d606e5fba21821187a93fa98629a6a 100644 (file)
@@ -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:
index 3bb4ec69c4f9b0520bf1c82da4935012016e6946..f64e28afbf975808d1410657b749572b53fb9cc7 100644 (file)
@@ -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.
 #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);
index 55c74c6c90c80dab64c3407aa67a135791356ec1..f189b07fa4de665f464dadba355f048c48cc1fff 100644 (file)
@@ -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];
 };
 
index 1458815ccb5ae1b7030926a280b19e1beaf5b514..2918631402b3cb4cf335b7d92c3e44d46f7ea2c3 100644 (file)
@@ -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:
        (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"))
 
index f1a8b1da6e94a0312ecc8a7bf6b8382cef84bb99..bbbd12ff9cfe85d26e3a50ff98540b6b0cb73e2b 100644 (file)
                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 */; };
                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 */; };
                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 */; };
                        );
                        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 */ = {
                        );
                        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;
                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; };
                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; };
                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; };
                                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 */,
                        );
                        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;
                };
                                D284BE660ADD80740027CCDF /* SystemConfiguration.framework in Frameworks */,
                                21B830A81D8A642200AE2001 /* libicucore.dylib in Frameworks */,
                                219D5542149ED645004464AE /* libxml2.dylib in Frameworks */,
+                               789036921F7AC1FA0077A962 /* libnetwork.tbd in Frameworks */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                19C28FBDFE9D53C911CA2CBB /* Products */,
                                37DDE9241BA382280092AC61 /* Unit Tests */,
                                BD9BA7561EAF929C00658CCF /* Frameworks */,
+                               BDB61842206ADB7700AFF600 /* LoggingProfiles */,
+                               BD28AE8D207B88F600F0B257 /* Scripts */,
                        );
                        name = mDNSResponder;
                        sourceTree = "<group>";
                                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 */,
                        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 = (
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               BDB04221203FEF4C00419961 /* dns_sd.h in Headers */,
+                               BDB04223203FF18000419961 /* dns_sd_private.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               BDB04222203FEF4D00419961 /* dns_sd.h in Headers */,
+                               BDB04224203FF18000419961 /* dns_sd_private.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                        buildActionMask = 2147483647;
                        files = (
                                BDA9A7881B3A924C00523835 /* dns_sd_private.h in Headers */,
+                               BD41B27D203EBE6100A53629 /* dns_sd.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                2141DD19123FFCDB0086D23E /* Headers */,
                                2141DD1A123FFCDB0086D23E /* Sources */,
                                2141DD1B123FFCDB0086D23E /* Frameworks */,
-                               2130256B12400DE600AC839F /* ShellScript */,
                        );
                        buildRules = (
                        );
                                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 = (
                        );
                        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;
                        );
                        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;
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "__APPLE_USE_RFC_3542=1",
-                                       "_DNS_SD_LIBDISPATCH=1",
                                        "APPLE_OSX_mDNSResponder=1",
                                        "__MigTypeCheck=1",
                                        "mDNSResponderVersion=${MVERS}",
                                        "-lMobileGestalt",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = (
-                                       "-lAWACS",
                                        "-weak_framework",
                                        WebFilterDNS,
                                        "-weak_framework",
                        buildSettings = {
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "__APPLE_USE_RFC_3542=1",
-                                       "_DNS_SD_LIBDISPATCH=1",
                                        "APPLE_OSX_mDNSResponder=1",
                                        "__MigTypeCheck=1",
                                        "mDNSResponderVersion=${MVERS}",
                                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;
                                        "$(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",
                                        "-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;
                };
                                        "$(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 = "";
                                        "-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;
                };
                                        "$(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;
                                        "-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;
                };
                                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;
                                );
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "__APPLE_USE_RFC_3542=1",
-                                       "_DNS_SD_LIBDISPATCH=1",
                                        "APPLE_OSX_mDNSResponder=1",
                                        "__MigTypeCheck=1",
                                        "mDNSResponderVersion=${MVERS}",
                                GCC_OPTIMIZATION_LEVEL = 0;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "__APPLE_USE_RFC_3542=1",
-                                       "_DNS_SD_LIBDISPATCH=1",
                                        "APPLE_OSX_mDNSResponder=1",
                                        "__MigTypeCheck=1",
                                        "mDNSResponderVersion=${MVERS}",
                                DEAD_CODE_STRIPPING = YES;
                                GCC_PREPROCESSOR_DEFINITIONS = (
                                        "__APPLE_USE_RFC_3542=1",
-                                       "_DNS_SD_LIBDISPATCH=1",
                                        "APPLE_OSX_mDNSResponder=1",
                                        "__MigTypeCheck=1",
                                        "mDNSResponderVersion=${MVERS}",
                                        "-lMobileGestalt",
                                );
                                "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = (
-                                       "-lAWACS",
                                        "-weak_framework",
                                        WebFilterDNS,
                                        "-weak_framework",
                                        "$(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 = "";
                                        "-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;
                };
                                        "$(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;
                                        "-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;
                };
                                        "$(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",
                                        "-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;
                };
index 96d4a0dbbadec8efd3f303901aab9d191b78fb8b..e4744736af55d7170978905bf808a3942ca52796 100644 (file)
 
 #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
index bd0fe582e528e98502b269fbfc27681d7a87f114..94fde85593eda58329834d5e5aedea33a21bb682 100644 (file)
@@ -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
index 3850ea2606b7a1e182ea4849fde6bf4f5cac3906..d4e20104c71f476b33201ffd644122bb4ecadf67 100755 (executable)
@@ -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
index 17a37c6116443621d911bafb5cf7898d0724637c..e00ddd62b881a54eab294d9ad5e6d8b1a64cbea1 100755 (executable)
@@ -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;
index cc81b7d39363ba6b214d6fa46ecfec55e280a4d0..2b36ceb04252a581e4b54a0c74c700a915ce52c5 100755 (executable)
@@ -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             /* 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
index 0e5c35eee24d7c57d9641b15be1851f57a1565a9..a1f5a573f9df0320a54230aab3d6b0f5ef5508f8 100644 (file)
@@ -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,
index 4d0236865e66cc6dd89eadfebcb5fba760a905b8..2d4297336184c9b50db8fbf6af83495f81048fec 100644 (file)
@@ -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"
 
index cfc1d4246b785f41d2cb7860b84e9ef1f5e9a371..2a1f5ed4d4e0d235390dc02279fa19702b557250 100644 (file)
@@ -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:
 
 #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__ ")";
index 27e90eeec4c96f0683cfa622214575e4706b8e28..e8600cba257de8762bdedd4102b1697d09976e1e 100644 (file)
@@ -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;
index 1243ae77a79956f2304825ecf5d61a648f4cf59e..dfe77a1d0d8c95835128ce255de16f0b34743417 100644 (file)
@@ -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
index 946ad162cf9d83791da185d4b7a4a62d9ad6af49..d1fbc7f2dd54531544dbbe78303623a9fc76384b 100644 (file)
@@ -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];
 };
index ece21407c2358dc1cbad7e21f32863ebce1cc04d..856eb4eec1f33dd5b7e377cca0ebb3dd18e943a9 100644 (file)
@@ -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;
index 907cfa69ffef3682b6bd046b3c1fc839a7e2e73e..0384d2c103445074f4cd27dca4f56e7bbd23b889 100644 (file)
@@ -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;
-}
index a4af2e67435287d833a64423c9c66ee3cac20366..4b62bb956d08778579c2dc77311e94dc149c9423 100644 (file)
@@ -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 */