From 51601d487eaddf41da63f4b9d4dd2609f5978980 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 29 Oct 2013 14:59:42 +0000 Subject: [PATCH] mDNSResponder-522.1.11.tar.gz --- Clients/dns-sd.c | 612 ++- Clients/dnsctl.c | 177 + .../mDNSNetMonitor.vcxproj | 4 + .../mDNSNetMonitor.vcxproj.filters | 12 + Makefile | 5 +- mDNSCore/CryptoAlg.c | 26 +- mDNSCore/CryptoAlg.h | 7 +- mDNSCore/DNSCommon.c | 576 ++- mDNSCore/DNSCommon.h | 15 +- mDNSCore/DNSDigest.c | 3 - mDNSCore/anonymous.c | 597 +++ mDNSCore/anonymous.h | 31 + mDNSCore/dnsproxy.c | 836 ++++ mDNSCore/dnsproxy.h | 30 + mDNSCore/dnssec.c | 1370 ++++- mDNSCore/dnssec.h | 58 +- mDNSCore/mDNS.c | 4443 +++++++++++++---- mDNSCore/mDNSDebug.h | 2 + mDNSCore/mDNSEmbeddedAPI.h | 649 ++- mDNSCore/nsec.c | 559 ++- mDNSCore/nsec.h | 9 +- mDNSCore/nsec3.c | 769 +++ mDNSCore/nsec3.h | 28 + mDNSCore/uDNS.c | 1435 +++++- mDNSCore/uDNS.h | 34 +- mDNSMacOSX/BonjourEvents.c | 9 +- mDNSMacOSX/CUPolicy.c | 91 + mDNSMacOSX/CryptoSupport.c | 292 +- mDNSMacOSX/DNSProxySupport.c | 543 ++ mDNSMacOSX/DNSSECSupport.c | 645 +++ mDNSMacOSX/DNSSECSupport.h | 24 + mDNSMacOSX/DNSServiceDiscovery.c | 2 +- mDNSMacOSX/LaunchDaemonInfo.helper.plist | 2 + mDNSMacOSX/LaunchDaemonInfo.plist | 3 +- mDNSMacOSX/LegacyNATTraversal.c | 14 +- mDNSMacOSX/Private/dns_services.c | 212 + mDNSMacOSX/Private/dns_services.h | 124 + mDNSMacOSX/Private/dns_xpc.h | 33 + mDNSMacOSX/Private/xpc_services.c | 255 + mDNSMacOSX/Private/xpc_services.h | 21 + mDNSMacOSX/README.privsep | 10 +- mDNSMacOSX/VPNService.c | 35 + mDNSMacOSX/com.apple.networking.mDNSResponder | 1 + mDNSMacOSX/daemon.c | 580 ++- mDNSMacOSX/dnsctl-entitlements.plist | 8 + mDNSMacOSX/helper-error.h | 2 - mDNSMacOSX/helper-main.c | 68 +- mDNSMacOSX/helper-stubs.c | 381 +- mDNSMacOSX/helper.c | 409 +- mDNSMacOSX/helper.h | 17 +- mDNSMacOSX/helpermsg.defs | 35 +- mDNSMacOSX/mDNSMacOSX.c | 2634 ++++++---- mDNSMacOSX/mDNSMacOSX.h | 109 +- mDNSMacOSX/mDNSResponder-entitlements.plist | 14 + mDNSMacOSX/mDNSResponder.sb | 23 +- .../mDNSResponder.xcodeproj/project.pbxproj | 430 +- mDNSMacOSX/mDNSResponderLogging.mobileconfig | 57 + mDNSPosix/Client.c | 2 +- mDNSPosix/Identify.c | 5 +- mDNSPosix/Makefile | 4 +- mDNSPosix/NetMonitor.c | 5 +- mDNSPosix/mDNSPosix.c | 103 +- mDNSShared/PlatformCommon.c | 5 +- mDNSShared/dns_sd.h | 250 +- mDNSShared/dnsextd.c | 8 +- mDNSShared/dnssd_clientshim.c | 20 +- mDNSShared/dnssd_clientstub.c | 316 +- mDNSShared/dnssd_ipc.h | 2 + mDNSShared/mDNSDebug.c | 4 +- mDNSShared/uds_daemon.c | 2114 ++++++-- mDNSShared/uds_daemon.h | 17 +- mDNSWindows/DLLX/DLLX.idl | 6 +- mDNSWindows/DLLX/DLLX.vcxproj | 2 +- mDNSWindows/Secret.c | 4 +- mDNSWindows/Secret.h | 6 +- mDNSWindows/SystemService/Service.vcxproj | 4 + .../SystemService/Service.vcxproj.filters | 12 + mDNSWindows/mDNSWin32.c | 100 +- 78 files changed, 18144 insertions(+), 4215 deletions(-) create mode 100644 Clients/dnsctl.c create mode 100644 mDNSCore/anonymous.c create mode 100644 mDNSCore/anonymous.h create mode 100644 mDNSCore/dnsproxy.c create mode 100644 mDNSCore/dnsproxy.h create mode 100644 mDNSCore/nsec3.c create mode 100644 mDNSCore/nsec3.h create mode 100644 mDNSMacOSX/CUPolicy.c create mode 100644 mDNSMacOSX/DNSProxySupport.c create mode 100644 mDNSMacOSX/DNSSECSupport.c create mode 100644 mDNSMacOSX/DNSSECSupport.h create mode 100644 mDNSMacOSX/Private/dns_services.c create mode 100644 mDNSMacOSX/Private/dns_services.h create mode 100644 mDNSMacOSX/Private/dns_xpc.h create mode 100644 mDNSMacOSX/Private/xpc_services.c create mode 100644 mDNSMacOSX/Private/xpc_services.h create mode 100644 mDNSMacOSX/VPNService.c create mode 100644 mDNSMacOSX/com.apple.networking.mDNSResponder create mode 100644 mDNSMacOSX/dnsctl-entitlements.plist create mode 100644 mDNSMacOSX/mDNSResponderLogging.mobileconfig diff --git a/Clients/dns-sd.c b/Clients/dns-sd.c index 146562f..8d5e75f 100644 --- a/Clients/dns-sd.c +++ b/Clients/dns-sd.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2008 Apple Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Inc. All rights reserved. * * Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. * ("Apple") in consideration of your agreement to the following terms, and your @@ -181,6 +181,10 @@ static const char kFilePathSep = '/'; #include "../mDNSShared/dnssd_clientstub.c" #endif +#if _DNS_SD_LIBDISPATCH +#include +#endif + // The "+0" is to cope with the case where _DNS_SD_H is defined but empty (e.g. on Mac OS X 10.4 and earlier) #if _DNS_SD_H+0 >= 116 #define HAS_NAT_PMP_API 1 @@ -192,6 +196,41 @@ static const char kFilePathSep = '/'; //************************************************************************************************************* // Globals +#define DS_FIXED_SIZE 4 +typedef struct +{ + unsigned short keyTag; + unsigned char alg; + unsigned char digestType; + unsigned char *digest; +} rdataDS; + +#define DNSKEY_FIXED_SIZE 4 +typedef struct +{ + unsigned short flags; + unsigned char proto; + unsigned char alg; + unsigned char *data; +} rdataDNSKey; + +//size of rdataRRSIG excluding signerName and signature (which are variable fields) +#define RRSIG_FIXED_SIZE 18 +typedef struct +{ + unsigned short typeCovered; + unsigned char alg; + unsigned char labels; + unsigned int origTTL; + unsigned int sigExpireTime; + unsigned int sigInceptTime; + unsigned short keyTag; + char signerName[256]; + //unsigned char *signature +} rdataRRSig; + +#define RR_TYPE_SIZE 16 + typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16; static int operation; @@ -228,6 +267,13 @@ static volatile int timeOut = LONG_TIME; //************************************************************************************************************* // Supporting Utility Functions +static uint16_t GetRRClass(const char *s) +{ + if (!strcasecmp(s, "IN")) + return kDNSServiceClass_IN; + else + return(atoi(s)); +} static uint16_t GetRRType(const char *s) { @@ -286,6 +332,95 @@ static uint16_t GetRRType(const char *s) else return(atoi(s)); } +static char *DNSTypeName(unsigned short rr_type) +{ + switch (rr_type) + { + case kDNSServiceType_A: return("Addr"); + case kDNSServiceType_NS: return("NS"); + case kDNSServiceType_MX: return("MX"); + case kDNSServiceType_CNAME: return("CNAME"); + case kDNSServiceType_SOA: return("SOA"); + case kDNSServiceType_PTR: return("PTR"); + case kDNSServiceType_AAAA: return("AAAA"); + case kDNSServiceType_NSEC: return("NSEC"); + case kDNSServiceType_TSIG: return("TSIG"); + case kDNSServiceType_RRSIG: return("RRSIG"); + case kDNSServiceType_DNSKEY: return("DNSKEY"); + case kDNSServiceType_DS: return("DS"); + default: + { + static char buffer[RR_TYPE_SIZE]; + snprintf(buffer, sizeof(buffer), "TYPE%d", rr_type); + return(buffer); + } + } +} + +static unsigned short swap16(unsigned short x) +{ + unsigned char *ptr = (unsigned char *)&x; + return (unsigned short)((unsigned short)ptr[0] << 8 | ptr[1]); +} + +static unsigned int swap32(unsigned int x) +{ + unsigned char *ptr = (unsigned char *)&x; + return (unsigned int)((unsigned int)ptr[0] << 24 | (unsigned int)ptr[1] << 16 | (unsigned int)ptr[2] << 8 | ptr[3]); +} +static unsigned int keytag(unsigned char *key, unsigned int keysize) +{ + unsigned long ac; + unsigned int i; + + for (ac = 0, i = 0; i < keysize; ++i) + ac += (i & 1) ? key[i] : key[i] << 8; + ac += (ac >> 16) & 0xFFFF; + return ac & 0xFFFF; +} + +static void base64Encode(char *buffer, int buflen, void *rdata, unsigned int 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 +} + #if HAS_NAT_PMP_API | HAS_ADDRINFO_API static DNSServiceProtocol GetProtocol(const char *s) { @@ -301,6 +436,7 @@ static DNSServiceProtocol GetProtocol(const char *s) } #endif + //************************************************************************************************************* // Sample callback functions for each of the operation types @@ -331,40 +467,63 @@ static void printtimestamp(void) printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); } +// formating time to RFC 4034 format +static void FormatTime(unsigned long te, unsigned char *buf, int bufsize) +{ + struct tm tmTime; +#ifdef _WIN32 + __time32_t t = (__time32_t) te; + _gmtime32_s(&tmTime, &t); +#else + // Time since epoch : strftime takes "tm". Convert seconds to "tm" using + // gmtime_r first and then use strftime + time_t t = (time_t)te; + gmtime_r(&t, &tmTime); +#endif + strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime); +} + static void print_usage(const char *arg0, int print_all) { - fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", arg0); - fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", arg0); - fprintf(stderr, "%s -R [...] (Register a service)\n", arg0); - fprintf(stderr, "%s -B (Browse for services instances)\n", arg0); - fprintf(stderr, "%s -L (Look up a service instance)\n", arg0); - fprintf(stderr, "%s -P [...] (Proxy)\n", arg0); - fprintf(stderr, "%s -q (Generic query for any record type)\n", arg0); - fprintf(stderr, "%s -Z (Output results in Zone File format)\n", arg0); + fprintf(stderr, "%s -E (Enumerate recommended registration domains)\n", arg0); + fprintf(stderr, "%s -F (Enumerate recommended browsing domains)\n", arg0); + fprintf(stderr, "%s -R [...] (Register a service)\n", arg0); + fprintf(stderr, "%s -B (Browse for services instances)\n", arg0); + fprintf(stderr, "%s -L (Look up a service instance)\n", arg0); + fprintf(stderr, "%s -P [...] (Proxy)\n", arg0); + fprintf(stderr, "%s -q (Generic query for any record type)\n", arg0); + fprintf(stderr, "%s -D (Validate query for any record type with DNSSEC)\n", arg0); + fprintf(stderr, "%s -Z (Output results in Zone File format)\n", arg0); #if HAS_ADDRINFO_API - fprintf(stderr, "%s -G v4/v6/v4v6 (Get address information for hostname)\n", arg0); + fprintf(stderr, "%s -G v4/v6/v4v6 (Get address information for hostname)\n", arg0); + fprintf(stderr, "%s -g v4/v6/v4v6 (Validate address info for hostname with DNSSEC)\n", arg0); #endif - fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", arg0); + fprintf(stderr, "%s -V (Get version of currently running daemon / system service)\n", arg0); if (print_all) //Print all available options for dns-sd tool { - fprintf(stderr, "%s -C (Query; reconfirming each result)\n", arg0); + fprintf(stderr, "%s -C (Query; reconfirming each result)\n", arg0); #if HAS_NAT_PMP_API - fprintf(stderr, "%s -X udp/tcp/udptcp (NAT Port Mapping)\n", arg0); + fprintf(stderr, "%s -X udp/tcp/udptcp (NAT Port Mapping)\n", arg0); #endif - fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", arg0); - fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); - fprintf(stderr, "%s -N (Test adding a large NULL record)\n", arg0); - fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); - fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", arg0); - fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); - fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", arg0); - fprintf(stderr, "%s -i (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0); - fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); - fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); - fprintf(stderr, "%s -includep2p (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0); - fprintf(stderr, "%s -includeAWDL (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0); - fprintf(stderr, "%s -tc (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0); + fprintf(stderr, "%s -A (Test Adding/Updating/Deleting a record)\n", arg0); + fprintf(stderr, "%s -U (Test updating a TXT record)\n", arg0); + fprintf(stderr, "%s -N (Test adding a large NULL record)\n", arg0); + fprintf(stderr, "%s -T (Test creating a large TXT record)\n", arg0); + fprintf(stderr, "%s -M (Test creating a registration with multiple TXT records)\n", arg0); + fprintf(stderr, "%s -I (Test registering and then immediately updating TXT record)\n", arg0); + fprintf(stderr, "%s -S (Test multiple operations on a shared socket)\n", arg0); + fprintf(stderr, "%s -i (Run dns-sd cmd on a specific interface (en0/en1)\n", arg0); + fprintf(stderr, "%s -lo (Run dns-sd cmd using local only interface)\n", arg0); + fprintf(stderr, "%s -p2p (Use kDNSServiceInterfaceIndexP2P)\n", arg0); + fprintf(stderr, "%s -includep2p (Set kDNSServiceFlagsIncludeP2P flag)\n", arg0); + fprintf(stderr, "%s -includeAWDL (Set kDNSServiceFlagsIncludeAWDL flag)\n", arg0); + fprintf(stderr, "%s -optional (Set kDNSServiceFlagsValidateOptional flag)\n", arg0); + fprintf(stderr, "%s -tc (Set kDNSServiceFlagsBackgroundTrafficClass flag)\n", arg0); + fprintf(stderr, "%s -unicastResponse (Set kDNSServiceFlagsUnicastResponse flag)\n", arg0); + fprintf(stderr, "%s -t1 (Set kDNSServiceFlagsThresholdOne flag)\n", arg0); + fprintf(stderr, "%s -tFinder (Set kDNSServiceFlagsThresholdFinder flag)\n", arg0); + fprintf(stderr, "%s -timeout (Set kDNSServiceFlagsTimeout flag)\n", arg0); } } @@ -535,10 +694,13 @@ static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags fl (void)context; // Unused EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-25s %s\n", "Domain", "Service Type", "Instance Name"); + if (num_printed++ == 0) printf("Timestamp A/R Flags if %-20s %-20s %s\n", "Domain", "Service Type", "Instance Name"); printtimestamp(); - if (errorCode) printf("Error code %d\n", errorCode); - else printf("%s%6X%3d %-25s %-25s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName); + if (errorCode) + printf("Error code %d\n", errorCode); + else + printf("%s %8X %3d %-20s %-20s %s\n", + op, flags, ifIndex, replyDomain, replyType, replyName); if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); // To test selective cancellation of operations of shared sockets, @@ -707,78 +869,247 @@ static int snprintd(char *p, int max, const unsigned char **rd) { const char *const buf = p; const char *const end = p + max; - while (**rd) { p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); *rd += 1 + **rd; } + while (**rd) + { + p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); + *rd += 1 + **rd; + } *rd += 1; // Advance over the final zero byte return(p-buf); } +static void ParseDNSSECRecords(uint16_t rrtype, char *rdb, char *p, unsigned const char *rd, uint16_t rdlen) +{ + int rdb_size = 1000; + switch (rrtype) + { + case kDNSServiceType_DS: + { + unsigned char *ptr; + int i; + rdataDS *rrds = (rdataDS *)rd; + p += snprintf(p, rdb + rdb_size - p, "%d %d %d ", + rrds->alg, swap16(rrds->keyTag), rrds->digestType); + ptr = (unsigned char *)(rd + DS_FIXED_SIZE); + for (i = 0; i < (rdlen - DS_FIXED_SIZE); i++) + p += snprintf(p, rdb + rdb_size - p, "%x", ptr[i]); + break; + } + + case kDNSServiceType_DNSKEY: + { + rdataDNSKey *rrkey = (rdataDNSKey *)rd; + 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; + } + + case kDNSServiceType_NSEC: + { + unsigned char *next = (unsigned char *)rd; + int len, bitmaplen; + int win, wlen, type; + unsigned char *bmap; + char *l = NULL; + + l = p; + p += snprintd(p, rdb + rdb_size - p, &rd); + len = p - l + 1; + + bitmaplen = rdlen - len; + bmap = (unsigned char *)((unsigned char *)next + len); + + while (bitmaplen > 0) + { + int i; + + if (bitmaplen < 3) + { + printf("Case NSEC: malformed nsec, bitmaplen %d short\n", bitmaplen); + break; + } + + win = *bmap++; + wlen = *bmap++; + bitmaplen -= 2; + if (bitmaplen < wlen || wlen < 1 || wlen > 32) + { + printf("Case NSEC: malformed nsec, bitmaplen %d wlen %d\n", bitmaplen, wlen); + break; + } + if (win < 0 || win >= 256) + { + printf("Case NSEC: malformed nsec, bad window win %d\n", win); + break; + } + type = win * 256; + for (i = 0; i < wlen * 8; i++) + { + if (bmap[i>>3] & (128 >> (i&7))) + p += snprintf(p, rdb + rdb_size - p, " %s ", DNSTypeName(type + i)); + } + bmap += wlen; + bitmaplen -= wlen; + } + break; + } + + case kDNSServiceType_RRSIG: + { + rdataRRSig *rrsig = (rdataRRSig *)rd; + unsigned char expTimeBuf[64]; + unsigned char inceptTimeBuf[64]; + unsigned long inceptClock; + unsigned long expClock; + const unsigned char *q = NULL; + char *k = NULL; + int len; + + expClock = (unsigned long)swap32(rrsig->sigExpireTime); + FormatTime(expClock, expTimeBuf, sizeof(expTimeBuf)); + + inceptClock = (unsigned long)swap32(rrsig->sigInceptTime); + FormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf)); + + p += snprintf(p, rdb + rdb_size - p, " %-7s %d %d %d %s %s %7d ", + DNSTypeName(swap16(rrsig->typeCovered)), rrsig->alg, rrsig->labels, swap32(rrsig->origTTL), + expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag)); + + q = (const unsigned char *)&rrsig->signerName; + k = p; + p += snprintd(p, rdb + rdb_size - p, &q); + len = p - k + 1; + + base64Encode(p, rdb + rdb_size - p, (unsigned char *)(rd + len + RRSIG_FIXED_SIZE), rdlen - (len + RRSIG_FIXED_SIZE)); + break; + } + } + return; +} + static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context) { char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; const unsigned char *rd = rdata; const unsigned char *end = (const unsigned char *) rdata + rdlen; - char rdb[1000] = "", *p = rdb; + char rdb[1000] = "0.0.0.0", *p = rdb; int unknowntype = 0; + char dnssec_status[15] = "Unknown"; + char rr_type[RR_TYPE_SIZE]; + char rr_class[3]; + DNSServiceFlags check_flags = flags;//local flags for dnssec status checking (void)sdref; // Unused - (void)flags; // Unused (void)ifIndex; // Unused (void)ttl; // Unused (void)context; // Unused EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C"); + if (num_printed++ == 0) + { + 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"); + } printtimestamp(); - if (!errorCode) + switch (rrclass) { - switch (rrtype) - { - case kDNSServiceType_A: - snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); + case kDNSServiceClass_IN: + strncpy(rr_class, "IN", sizeof(rr_class)); break; - - case kDNSServiceType_NS: - case kDNSServiceType_CNAME: - case kDNSServiceType_PTR: - case kDNSServiceType_DNAME: - p += snprintd(p, sizeof(rdb), &rd); + default: + snprintf(rr_class, sizeof(rr_class), "%d", rrclass); break; + } + strncpy(rr_type, DNSTypeName(rrtype), sizeof(rr_type)); - case kDNSServiceType_SOA: - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname - p += snprintf(p, rdb + sizeof(rdb) - p, " "); - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname - p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", + if (!errorCode) //to avoid printing garbage in rdata + { + if (!(check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional))) + { + switch (rrtype) + { + case kDNSServiceType_A: + snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); + break; + + case kDNSServiceType_NS: + case kDNSServiceType_CNAME: + case kDNSServiceType_PTR: + case kDNSServiceType_DNAME: + p += snprintd(p, sizeof(rdb), &rd); + break; + + case kDNSServiceType_SOA: + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // mname + p += snprintf(p, rdb + sizeof(rdb) - p, " "); + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // rname + p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d", ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4])); - break; + break; - case kDNSServiceType_AAAA: - snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", - rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], - rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); - break; + case kDNSServiceType_AAAA: + snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X", + rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7], + rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]); + break; - case kDNSServiceType_SRV: - p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port + case kDNSServiceType_SRV: + p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ", // priority, weight, port ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4))); - rd += 6; - p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host - break; - - default: snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); unknowntype = 1; break; + rd += 6; + p += snprintd(p, rdb + sizeof(rdb) - p, &rd); // target host + break; + + case kDNSServiceType_DS: + case kDNSServiceType_DNSKEY: + case kDNSServiceType_NSEC: + case kDNSServiceType_RRSIG: + ParseDNSSECRecords(rrtype, rdb, p, rd, rdlen); + break; + + default: + snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); + unknowntype = 1; + break; + } + } + else + { + strncpy(rdb, "----", sizeof(rdb)); + //Clear all o/p bits, and then check for dnssec status + check_flags &= ~kDNSServiceOutputFlags; + if (check_flags & kDNSServiceFlagsSecure) + strncpy(dnssec_status, "Secure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsInsecure) + strncpy(dnssec_status, "Insecure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsIndeterminate) + strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsBogus) + strncpy(dnssec_status, "Bogus", sizeof(dnssec_status)); } } - printf("%s%6X%3d %-30s%4d%4d %s", op, flags, ifIndex, fullname, rrtype, rrclass, rdb); - if (unknowntype) while (rd < end) printf(" %02X", *rd++); + 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); + if (unknowntype) + { + while (rd < end) + printf(" %02X", *rd++); + } if (errorCode) { - if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record"); + if (errorCode == kDNSServiceErr_NoSuchRecord) + printf(" No Such Record"); else if (errorCode == kDNSServiceErr_Timeout) { - printf("No Such Record\n"); + printf(" No Such Record\n"); printf("Query Timed Out\n"); exit(1); } @@ -789,7 +1120,8 @@ static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, if (flags & kDNSServiceFlagsAdd) DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata); - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + if (!(flags & kDNSServiceFlagsMoreComing)) + fflush(stdout); } #if HAS_NAT_PMP_API @@ -817,17 +1149,26 @@ static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceF #endif #if HAS_ADDRINFO_API -static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context) +static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context) { char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv"; char addr[256] = ""; - (void) sdref; - (void) context; + char dnssec_status[15] = "Unknown"; + DNSServiceFlags check_flags = flags; + (void) sdref; + (void) context; + EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode); - if (num_printed++ == 0) printf("Timestamp A/R Flags if %-25s %-44s %s\n", "Hostname", "Address", "TTL"); + if (num_printed++ == 0) + { + 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"); + } printtimestamp(); - + if (address && address->sa_family == AF_INET) { const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr; @@ -840,20 +1181,42 @@ static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, const unsigned char *b = (const unsigned char * )&s6->sin6_addr; if (!if_indextoname(s6->sin6_scope_id, if_name)) snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id); - snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s", - b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], - b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name); + snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s", + b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7], + b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name); } - printf("%s%6X%3d %-25s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl); + //go through this only if you have a dnssec validation status + if (!errorCode && (check_flags & (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional))) + { + strncpy(addr, "----", sizeof(addr)); + //Clear all o/p bits, and then check for dnssec status + check_flags &= ~kDNSServiceOutputFlags; + if (check_flags & kDNSServiceFlagsSecure) + strncpy(dnssec_status, "Secure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsInsecure) + strncpy(dnssec_status, "Insecure", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsIndeterminate) + strncpy(dnssec_status, "Indeterminate", sizeof(dnssec_status)); + else if (check_flags & kDNSServiceFlagsBogus) + strncpy(dnssec_status, "Bogus", sizeof(dnssec_status)); + } + + 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); if (errorCode) { - if (errorCode == kDNSServiceErr_NoSuchRecord) printf(" No Such Record"); - else printf(" Error code %d", errorCode); + if (errorCode == kDNSServiceErr_NoSuchRecord) + printf(" No Such Record"); + else + printf(" Error code %d", errorCode); } printf("\n"); - if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout); + if (!(flags & kDNSServiceFlagsMoreComing)) + fflush(stdout); } #endif @@ -1071,6 +1434,7 @@ int main(int argc, char **argv) char buffer[TypeBufferSize], *typ, *dom; int opi; DNSServiceFlags flags = 0; + int optional = 0; // Extract the program name from argv[0], which by convention contains the path to this executable. // Note that this is just a voluntary convention, not enforced by the kernel -- @@ -1132,6 +1496,52 @@ int main(int argc, char **argv) printf("Setting kDNSServiceFlagsBackgroundTrafficClass\n"); } + if (argc > 1 && !strcasecmp(argv[1], "-t1")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsThresholdOne; + printf("Setting kDNSServiceFlagsThresholdOne\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-tFinder")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsThresholdFinder; + printf("Setting kDNSServiceFlagsThresholdFinder\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-wo")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsWakeOnlyService; + printf("Setting kDNSServiceFlagsWakeOnlyService\n"); + } + + if (argc > 1 && !strcasecmp(argv[1], "-unicastResponse")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsUnicastResponse; + printf("Setting kDNSServiceFlagsUnicastResponse\n"); + } + if (argc > 1 && !strcasecmp(argv[1], "-timeout")) + { + argc--; + argv++; + flags |= kDNSServiceFlagsTimeout; + printf("Setting kDNSServiceFlagsTimeout\n"); + } + if (argc > 1 && !strcasecmp(argv[1], "-optional")) + { + argc--; + argv++; + optional = 1; + printf("Setting DNSSEC optional flag\n"); + } + if (argc > 2 && !strcmp(argv[1], "-i")) { opinterface = if_nametoindex(argv[2]); @@ -1142,12 +1552,12 @@ int main(int argc, char **argv) } if (argc < 2) goto Fail; // Minimum command line is the command name and one argument - operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISVHh" + operation = getfirstoption(argc, argv, "EFBZLlRPQqCAUNTMISVHhD" #if HAS_NAT_PMP_API "X" #endif #if HAS_ADDRINFO_API - "G" + "Gg" #endif , &opi); if (operation == -1) goto Fail; @@ -1207,6 +1617,7 @@ int main(int argc, char **argv) err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags); break; + case 'P': if (argc < opi+6) goto Fail; err = DNSServiceCreateConnection(&client_pa); if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); } @@ -1215,18 +1626,28 @@ int main(int argc, char **argv) err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags); break; - case 't': + case 'D': case 'q': case 'Q': case 'C': { uint16_t rrtype, rrclass; flags |= kDNSServiceFlagsReturnIntermediates; - if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable; - if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout); - if (argc < opi+1) goto Fail; + if (operation == 'q') + flags |= kDNSServiceFlagsSuppressUnusable; + if (argc < opi+1) + goto Fail; rrtype = (argc <= opi+1) ? kDNSServiceType_A : GetRRType(argv[opi+1]); - rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]); - if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery; + rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : GetRRClass(argv[opi+2]); + if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) + flags |= kDNSServiceFlagsLongLivedQuery; + if (operation == 'D') + { + flags |= kDNSServiceFlagsSuppressUnusable; + if (optional) + flags |= kDNSServiceFlagsValidateOptional; + else + flags |= kDNSServiceFlagsValidate; + } err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL); break; } @@ -1293,10 +1714,21 @@ int main(int argc, char **argv) #endif #if HAS_ADDRINFO_API + case 'g': case 'G': { flags |= kDNSServiceFlagsReturnIntermediates; - if (argc != opi+2) goto Fail; - else err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); + if (operation == 'g') + { + flags |= kDNSServiceFlagsSuppressUnusable; + if (optional) + flags |= kDNSServiceFlagsValidateOptional; + else + flags |= kDNSServiceFlagsValidate; + } + if (argc != opi+2) + goto Fail; + else + err = DNSServiceGetAddrInfo(&client, flags, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL); break; } #endif @@ -1340,7 +1772,7 @@ int main(int argc, char **argv) uint32_t size = sizeof(v); err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size); if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err); - else printf("Currently running daemon (system service) is version %d.%d\n", v / 10000, v / 100 % 100); + else printf("Currently running daemon (system service) is version %d.%d.%d\n", v / 10000, v / 100 % 100, v % 100); exit(0); } diff --git a/Clients/dnsctl.c b/Clients/dnsctl.c new file mode 100644 index 0000000..f040e1f --- /dev/null +++ b/Clients/dnsctl.c @@ -0,0 +1,177 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * dnsctl.c + * Command-line tool using libdns_services.dylib + * + * To build only this tool, copy and paste the following on the command line: + * On Apple 64bit Platforms ONLY OSX/iOS: + * clang -Wall dnsctl.c /usr/lib/libdns_services.dylib -o dnsctl + * + */ + +#include +#include +#include +#include +#include // if_nametoindex() + +#include +#include "dns_services.h" + +//************************************************************************************************************* +// Globals: +//************************************************************************************************************* + +static const char kFilePathSep = '/'; +static DNSXConnRef ClientRef = NULL; + +//************************************************************************************************************* +// Utility Funcs: +//************************************************************************************************************* + +static void printtimestamp(void) +{ + struct tm tm; + int ms; + static char date[16]; + static char new_date[16]; + struct timeval tv; + gettimeofday(&tv, NULL); + localtime_r((time_t*)&tv.tv_sec, &tm); + ms = tv.tv_usec/1000; + strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm); + //display date only if it has changed + if (strncmp(date, new_date, sizeof(new_date))) + { + printf("DATE: ---%s---\n", new_date); + strncpy(date, new_date, sizeof(date)); + } + printf("%2d:%02d:%02d.%03d ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms); +} + +static void print_usage(const char *arg0) +{ + fprintf(stderr, "%s USAGE: \n", arg0); + fprintf(stderr, "%s -DP Enable DNS Proxy with Default Parameters \n", arg0); + fprintf(stderr, "%s -DP [-o ] [-i ] Enable DNS Proxy \n", arg0); +} + +//************************************************************************************************************* +// CallBack Funcs: +//************************************************************************************************************* + +// DNSXEnableProxy Callback from the Daemon +static void dnsproxy_reply(DNSXConnRef connRef, DNSXErrorType errCode) +{ + (void) connRef; + printtimestamp(); + switch (errCode) + { + case kDNSX_NoError : printf(" SUCCESS \n"); break; + case kDNSX_DictError : printf(" DICT ERROR \n"); break; + case kDNSX_DaemonNotRunning : printf(" NO DAEMON \n"); + DNSXRefDeAlloc(ClientRef); break; + case kDNSX_Engaged : printf(" ENGAGED \n"); + DNSXRefDeAlloc(ClientRef); break; + case kDNSX_UnknownErr : + default : printf("UNKNOWN ERR \n"); + DNSXRefDeAlloc(ClientRef); break; + } + +} + +//************************************************************************************************************* + +int main(int argc, char **argv) +{ + DNSXErrorType err; + + // Default i/p intf is lo0 and o/p intf is primary interface + IfIndex Ipintfs[MaxInputIf] = {1, 0, 0, 0, 0}; + IfIndex Opintf = kDNSIfindexAny; + + // Extract program name from argv[0], which by convention contains the path to this executable + const char *a0 = strrchr(argv[0], kFilePathSep) + 1; + if (a0 == (const char *)1) + a0 = argv[0]; + + // Must run as root + if (0 != geteuid()) + { + fprintf(stderr, "%s MUST run as root!!\n", a0); + exit(-1); + } + if ((sizeof(argv) == 8)) + printf("dnsctl running in 64-bit mode\n"); + else if ((sizeof(argv) == 4)) + printf("dnsctl running in 32-bit mode\n"); + + // expects atleast one argument + if (argc < 2) + goto Usage; + + if ( !strcmp(argv[1], "-DP") || !strcmp(argv[1], "-dp") ) + { + if (argc == 2) + { + printtimestamp(); + printf("Proceeding to Enable DNSProxy on mDNSResponder with Default Parameters\n"); + dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL); + err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply); + } + else if (argc > 2) + { + argc--; + argv++; + if (!strcmp(argv[1], "-o")) + { + Opintf = if_nametoindex(argv[2]); + if (!Opintf) + Opintf = atoi(argv[2]); + if (!Opintf) + { + fprintf(stderr, "Could not parse o/p interface [%s]: Passing default primary \n", argv[2]); + Opintf = kDNSIfindexAny; + } + argc -= 2; + argv += 2; + } + if (argc > 2 && !strcmp(argv[1], "-i")) + { + int i; + argc--; + argv++; + for (i = 0; i < MaxInputIf && argc > 1; i++) + { + Ipintfs[i] = if_nametoindex(argv[1]); + if (!Ipintfs[i]) + Ipintfs[i] = atoi(argv[1]); + if (!Ipintfs[i]) + { + fprintf(stderr, "Could not parse i/p interface [%s]: Passing default lo0 \n", argv[2]); + Ipintfs[i] = 1; + } + argc--; + argv++; + } + } + printtimestamp(); + printf("Proceeding to Enable DNSProxy on mDNSResponder \n"); + dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL); + err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply); + } + } + else + { + goto Usage; + } + + dispatch_main(); + +Usage: + print_usage(a0); + return 0; +} + diff --git a/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj b/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj index c714dea..89a27a4 100755 --- a/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj +++ b/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj @@ -195,6 +195,8 @@ xcopy /I/Y "$(TargetPath)" + + @@ -209,6 +211,8 @@ xcopy /I/Y "$(TargetPath)" + + diff --git a/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj.filters b/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj.filters index 8a60f08..b9a25be 100755 --- a/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj.filters +++ b/Clients/mDNSNetMonitor.VisualStudio/mDNSNetMonitor.vcxproj.filters @@ -51,6 +51,12 @@ Source Files + + Source Files + + + Source Files + @@ -89,6 +95,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/Makefile b/Makefile index 016b5ee..7d78460 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ include $(MAKEFILEPATH)/pb_makefiles/platform.make -MVERS = "mDNSResponder-379.38.1" +MVERS = "mDNSResponder-522.1.11" DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig" VER = @@ -42,5 +42,8 @@ installsrc: installhdrs:: cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries $(VER) +java: + cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target libjdns_sd.jnilib $(VER) + clean:: echo clean diff --git a/mDNSCore/CryptoAlg.c b/mDNSCore/CryptoAlg.c index 0fee771..38533fc 100644 --- a/mDNSCore/CryptoAlg.c +++ b/mDNSCore/CryptoAlg.c @@ -183,7 +183,7 @@ mDNSexport mDNSu32 AlgLength(AlgContext *ctx) return 0; } -mDNSexport mStatus AlgAdd(AlgContext *ctx, void *data, mDNSu32 len) +mDNSexport mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len) { AlgFuncs *func = mDNSNULL; @@ -254,3 +254,27 @@ mDNSexport mDNSu8* AlgEncode(AlgContext *ctx) else return mDNSNULL; } + +mDNSexport mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len) +{ + AlgFuncs *func = mDNSNULL; + + if (ctx->type == CRYPTO_ALG) + func = CryptoAlgFuncs[ctx->alg]; + else if (ctx->type == DIGEST_ALG) + func = DigestAlgFuncs[ctx->alg]; + else if (ctx->type == ENC_ALG) + func = EncAlgFuncs[ctx->alg]; + + // This should never happen as AlgCreate would have failed + if (!func) + { + LogMsg("AlgEncode: ERROR!! func is NULL"); + return mDNSNULL; + } + + if (func->Final) + return (func->Final(ctx, data, len)); + else + return mStatus_BadParamErr; +} diff --git a/mDNSCore/CryptoAlg.h b/mDNSCore/CryptoAlg.h index 4ed6879..6cb3dc9 100644 --- a/mDNSCore/CryptoAlg.h +++ b/mDNSCore/CryptoAlg.h @@ -36,11 +36,13 @@ typedef struct mStatus (*Create)(AlgContext *ctx); mStatus (*Destroy)(AlgContext *ctx); mDNSu32 (*Length)(AlgContext *ctx); - mStatus (*Add)(AlgContext *ctx, void *data, mDNSu32 len); + mStatus (*Add)(AlgContext *ctx, const void *data, mDNSu32 len); // Verify the ctx using the key and compare it against signature/siglen mStatus (*Verify)(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); // Encode the data and return the encoded data mDNSu8* (*Encode)(AlgContext *ctx); + // Return the finalized data in data whose length is len (used by hash algorithms) + mStatus (*Final)(AlgContext *ctx, void *data, mDNSu32 len); } AlgFuncs; mDNSexport mStatus DigestAlgInit(mDNSu8 digestType, AlgFuncs *func); @@ -51,8 +53,9 @@ mDNSexport mStatus EncAlgInit(mDNSu8 algType, AlgFuncs *func); extern AlgContext *AlgCreate(AlgType type, mDNSu8 alg); extern mStatus AlgDestroy(AlgContext *ctx); extern mDNSu32 AlgLength(AlgContext *ctx); -extern mStatus AlgAdd(AlgContext *ctx, void *data, mDNSu32 len); +extern mStatus AlgAdd(AlgContext *ctx, const void *data, mDNSu32 len); extern mStatus AlgVerify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen); extern mDNSu8* AlgEncode(AlgContext *ctx); +extern mStatus AlgFinal(AlgContext *ctx, void *data, mDNSu32 len); #endif // __CRYPTO_ALG_H diff --git a/mDNSCore/DNSCommon.c b/mDNSCore/DNSCommon.c index 99429ea..c8fcc89 100644 --- a/mDNSCore/DNSCommon.c +++ b/mDNSCore/DNSCommon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ #define mDNS_InstantiateInlines 1 #include "DNSCommon.h" #include "CryptoAlg.h" +#include "anonymous.h" // Disable certain benign warnings with Microsoft compilers #if (defined(_MSC_VER)) @@ -41,6 +42,7 @@ mDNSexport const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)-1; mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2; mDNSexport const mDNSInterfaceID mDNSInterface_Unicast = (mDNSInterfaceID)-3; mDNSexport const mDNSInterfaceID mDNSInterface_P2P = (mDNSInterfaceID)-4; +mDNSexport const mDNSInterfaceID uDNSInterfaceMark = (mDNSInterfaceID)-5; // Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of // Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP @@ -88,7 +90,7 @@ mDNSexport const mDNSEthAddr onesEthAddr = { { 255, 255, 255, 255, 255, 25 mDNSexport const mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} }; mDNSexport const mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } }; -mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP Annoucements +mDNSexport const mDNSv4Addr AllHosts_v4 = { { 224, 0, 0, 1 } }; // For NAT-PMP & PCP Annoucements mDNSexport const mDNSv6Addr AllHosts_v6 = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } }; mDNSexport const mDNSv6Addr NDP_prefix = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104 mDNSexport const mDNSEthAddr AllHosts_v6_Eth = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } }; @@ -115,13 +117,34 @@ mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } }; #endif // return true for RFC1918 private addresses -mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr) +mDNSexport mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr) { return ((addr->b[0] == 10) || // 10/8 prefix (addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) || // 172.16/12 (addr->b[0] == 192 && addr->b[1] == 168)); // 192.168/16 } +mDNSexport void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out) +{ + out->l[0] = 0; + out->l[1] = 0; + out->w[4] = 0; + out->w[5] = 0xffff; + out->b[12] = in->b[0]; + out->b[13] = in->b[1]; + out->b[14] = in->b[2]; + out->b[15] = in->b[3]; +} + +mDNSexport mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr* out) +{ + if (in->l[0] != 0 || in->l[1] != 0 || in->w[4] != 0 || in->w[5] != 0xffff) + return mDNSfalse; + + out->NotAnInteger = in->l[3]; + return mDNStrue; +} + mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf) { while (intf && !intf->InterfaceActive) intf = intf->next; @@ -163,6 +186,8 @@ mDNSexport char *DNSTypeName(mDNSu16 rrtype) case kDNSType_SRV: return("SRV"); case kDNSType_OPT: return("OPT"); case kDNSType_NSEC: return("NSEC"); + case kDNSType_NSEC3: return("NSEC3"); + case kDNSType_NSEC3PARAM: return("NSEC3PARAM"); case kDNSType_TSIG: return("TSIG"); case kDNSType_RRSIG: return("RRSIG"); case kDNSType_DNSKEY: return("DNSKEY"); @@ -199,11 +224,12 @@ mDNSlocal char *DNSSECDigestName(mDNSu8 digest) { case SHA1_DIGEST_TYPE: return "SHA1"; case SHA256_DIGEST_TYPE: return "SHA256"; - default: { + default: + { static char digbuffer[16]; mDNS_snprintf(digbuffer, sizeof(digbuffer), "DIG%d", digest); return(digbuffer); - } + } } } @@ -240,27 +266,100 @@ mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize) return ac & 0xFFFF; } -mDNSlocal int base64Encode(char *buffer, int blen, mDNSu8 *data, int len) +mDNSexport int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg) { AlgContext *ctx; mDNSu8 *outputBuffer; int length; - ctx = AlgCreate(ENC_ALG, ENC_BASE64); + ctx = AlgCreate(ENC_ALG, encAlg); if (!ctx) { - LogMsg("base64Encode: AlgCreate failed\n"); + LogMsg("baseEncode: AlgCreate failed\n"); return 0; } AlgAdd(ctx, data, len); outputBuffer = AlgEncode(ctx); length = 0; if (outputBuffer) - length = mDNS_snprintf(buffer, blen, " %s", outputBuffer); + { + // Note: don't include any spaces in the format string below. This + // is also used by NSEC3 code for proving non-existence where it + // needs the base32 encoding without any spaces etc. + length = mDNS_snprintf(buffer, blen, "%s", outputBuffer); + } AlgDestroy(ctx); return length; } +mDNSlocal void PrintTypeBitmap(const mDNSu8 *bmap, int bitmaplen, char *const buffer, mDNSu32 length) +{ + int win, wlen, type; + + while (bitmaplen > 0) + { + int i; + + if (bitmaplen < 3) + { + LogMsg("PrintTypeBitmap: malformed bitmap, bitmaplen %d short", bitmaplen); + break; + } + + win = *bmap++; + wlen = *bmap++; + bitmaplen -= 2; + if (bitmaplen < wlen || wlen < 1 || wlen > 32) + { + LogInfo("PrintTypeBitmap: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen); + break; + } + if (win < 0 || win >= 256) + { + LogInfo("PrintTypeBitmap: malformed nsec, bad window win %d", win); + break; + } + type = win * 256; + for (i = 0; i < wlen * 8; i++) + { + if (bmap[i>>3] & (128 >> (i&7))) + length += mDNS_snprintf(buffer+length, (MaxMsg - 1) - length, "%s ", DNSTypeName(type + i)); + } + bmap += wlen; + bitmaplen -= wlen; + } +} + +// Parse the fields beyond the base header. NSEC3 should have been validated. +mDNSexport void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap) +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data; + mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + int hlen; + + if (salt) + { + if (nsec3->saltLength) + *salt = p; + else + *salt = mDNSNULL; + } + p += nsec3->saltLength; + // p is pointing at hashLength + hlen = (int)*p; + if (hashLength) + *hashLength = hlen; + p++; + if (nxtName) + *nxtName = p; + p += hlen; + if (bitmaplen) + *bitmaplen = rr->rdlength - (int)(p - rdb->data); + if (bitmap) + *bitmap = p; +} + // Note slight bug: this code uses the rdlength from the ResourceRecord object, to display // the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as // long as this routine is only used for debugging messages, it probably isn't a big problem. @@ -329,6 +428,10 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b); } break; + case kDNSOpt_Trace: + length += mDNS_snprintf(buffer+length, RemSpc, " Platform %d", opt->u.tracer.platf); + length += mDNS_snprintf(buffer+length, RemSpc, " mDNSVers %d", opt->u.tracer.mDNSv); + break; default: length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d", opt->opt); break; @@ -340,7 +443,6 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD case kDNSType_NSEC: { domainname *next = (domainname *)rd->data; int len, bitmaplen; - int win, wlen, type; mDNSu8 *bmap; len = DomainNameLength(next); bitmaplen = rr->rdlength - len; @@ -348,39 +450,45 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD if (UNICAST_NSEC(rr)) length += mDNS_snprintf(buffer+length, RemSpc, "%##s ", next->c); + PrintTypeBitmap(bmap, bitmaplen, buffer, length); - while (bitmaplen > 0) + } + break; + case kDNSType_NSEC3: { + rdataNSEC3 *nsec3 = (rdataNSEC3 *)rd->data; + const mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + int hashLength, bitmaplen, i; + + length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %d %d ", + DNSSECDigestName(nsec3->alg), nsec3->flags, swap16(nsec3->iterations)); + + if (!nsec3->saltLength) { - int i; - - if (bitmaplen < 3) - { - LogMsg("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d short", bitmaplen); - break; - } - - win = *bmap++; - wlen = *bmap++; - bitmaplen -= 2; - if (bitmaplen < wlen || wlen < 1 || wlen > 32) - { - LogInfo("GetRRDisplayString_rdb: malformed nsec, bitmaplen %d wlen %d", bitmaplen, wlen); - break; - } - if (win < 0 || win >= 256) - { - LogInfo("GetRRDisplayString_rdb: malformed nsec, bad window win %d", win); - break; - } - type = win * 256; - for (i = 0; i < wlen * 8; i++) + length += mDNS_snprintf(buffer+length, RemSpc, "-"); + } + else + { + for (i = 0; i < nsec3->saltLength; i++) { - if (bmap[i>>3] & (128 >> (i&7))) - length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(type + i)); + length += mDNS_snprintf(buffer+length, RemSpc, "%x", p[i]); } - bmap += wlen; - bitmaplen -= wlen; } + + // put a space at the end + length += mDNS_snprintf(buffer+length, RemSpc, " "); + + p += nsec3->saltLength; + // p is pointing at hashLength + hashLength = (int)*p++; + + length += baseEncode(buffer + length, RemSpc, p, hashLength, ENC_BASE32); + + // put a space at the end + length += mDNS_snprintf(buffer+length, RemSpc, " "); + + p += hashLength; + bitmaplen = rr->rdlength - (int)(p - rd->data); + PrintTypeBitmap(p, bitmaplen, buffer, length); } break; case kDNSType_RRSIG: { @@ -397,21 +505,21 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD inceptClock = (unsigned long)swap32(rrsig->sigInceptTime); mDNSPlatformFormatTime(inceptClock, inceptTimeBuf, sizeof(inceptTimeBuf)); - length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s", + length += mDNS_snprintf(buffer+length, RemSpc, "\t%s %s %d %d %s %s %d %##s ", DNSTypeName(swap16(rrsig->typeCovered)), DNSSECAlgName(rrsig->alg), rrsig->labels, swap32(rrsig->origTTL), expTimeBuf, inceptTimeBuf, swap16(rrsig->keyTag), ((domainname *)(&rrsig->signerName))->c); len = DomainNameLength((domainname *)&rrsig->signerName); - length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE), - rr->rdlength - (len + RRSIG_FIXED_SIZE)); + length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + len + RRSIG_FIXED_SIZE), + rr->rdlength - (len + RRSIG_FIXED_SIZE), ENC_BASE64); } break; case kDNSType_DNSKEY: { rdataDNSKey *rrkey = (rdataDNSKey *)rd->data; - length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u", swap16(rrkey->flags), rrkey->proto, + length += mDNS_snprintf(buffer+length, RemSpc, "\t%d %d %s %u ", swap16(rrkey->flags), rrkey->proto, DNSSECAlgName(rrkey->alg), (unsigned int)keytag((mDNSu8 *)rrkey, rr->rdlength)); - length += base64Encode(buffer + length, RemSpc, (mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE), - rr->rdlength - DNSKEY_FIXED_SIZE); + length += baseEncode(buffer + length, RemSpc, (const mDNSu8 *)(rd->data + DNSKEY_FIXED_SIZE), + rr->rdlength - DNSKEY_FIXED_SIZE, ENC_BASE64); } break; case kDNSType_DS: { @@ -885,7 +993,7 @@ mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn, const mDNSu8 *s2 = s1 + 1 + s1[0]; if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0) // and we have three and only three labels { - static const mDNSu8 SubTypeLabel[5] = "\x04_sub"; + static const mDNSu8 SubTypeLabel[5] = mDNSSubTypeLabel; src = s0; // Copy the first label len = *src; for (i=0; i <= len; i++) *dst++ = *src++; @@ -1022,6 +1130,83 @@ mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn, return(mDNStrue); } +mDNSexport mStatus DNSNameToLowerCase(domainname *d, domainname *result) +{ + const mDNSu8 *a = d->c; + mDNSu8 *b = result->c; + const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME; + int i, len; + + while (*a) + { + if (a + 1 + *a >= max) + { + LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name"); + return mStatus_BadParamErr; + } + len = *a++; + *b++ = len; + for (i = 0; i < len; i++) + { + mDNSu8 ac = *a++; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + *b++ = ac; + } + } + *b = 0; + + return mStatus_NoError; +} + +mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen, + const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen) +{ + AlgContext *ctx; + int i; + domainname lname; + mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + const mDNSu8 *digest; + int digestlen; + mDNSBool first = mDNStrue; + + if (DNSNameToLowerCase((domainname *)name, &lname) != mStatus_NoError) + { + LogMsg("NSEC3HashName: ERROR!! DNSNameToLowerCase failed"); + return mDNSNULL; + } + + digest = lname.c; + digestlen = DomainNameLength(&lname); + + // Note that it is "i <=". The first iteration is for digesting the name and salt. + // The iteration count does not include that. + for (i = 0; i <= swap16(nsec3->iterations); i++) + { + ctx = AlgCreate(DIGEST_ALG, nsec3->alg); + if (!ctx) + { + LogMsg("NSEC3HashName: ERROR!! Cannot allocate context"); + return mDNSNULL; + } + + AlgAdd(ctx, digest, digestlen); + if (nsec3->saltLength) + AlgAdd(ctx, p, nsec3->saltLength); + if (AnonDataLen) + AlgAdd(ctx, AnonData, AnonDataLen); + if (first) + { + first = mDNSfalse; + digest = hash; + digestlen = AlgLength(ctx); + } + AlgFinal(ctx, (void *)digest, digestlen); + AlgDestroy(ctx); + } + *dlen = digestlen; + return digest; +} + // Notes on UTF-8: // 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F // 10xxxxxx is a continuation byte of a multi-byte character @@ -1206,6 +1391,7 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->resrec.rrclass = kDNSClass_IN; rr->resrec.rroriginalttl = ttl; rr->resrec.rDNSServer = mDNSNULL; + rr->resrec.AnonInfo = mDNSNULL; // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal // rr->resrec.rdestimate = set in mDNS_Register_internal // rr->resrec.rdata = MUST be set by client @@ -1235,6 +1421,7 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD rr->TimeRcvd = 0; rr->TimeExpire = 0; rr->ARType = artype; + rr->AuthFlags = 0; // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal) // Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal) @@ -1282,10 +1469,15 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I q->RetryWithSearchDomains = mDNSfalse; q->TimeoutQuestion = 0; q->WakeOnResolve = 0; - q->UseBrackgroundTrafficClass = mDNSfalse; + q->UseBackgroundTrafficClass = mDNSfalse; q->ValidationRequired = 0; q->ValidatingResponse = 0; + q->ProxyQuestion = 0; q->qnameOrig = mDNSNULL; + q->AnonInfo = mDNSNULL; + q->pid = mDNSPlatformGetPID(); + q->DisallowPID = mDNSfalse; + q->ServiceID = -1; q->QuestionCallback = callback; q->QuestionContext = context; } @@ -1425,32 +1617,20 @@ mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBod } } -// Don't call this function if the resource record is not NSEC. It will return false -// which means that the type does not exist. -mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type) +mDNSexport mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type) { - const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; - mDNSu8 *nsec = (mDNSu8 *)rdb->data; - int len, bitmaplen; int win, wlen; - mDNSu8 *bmap; int wintype; - if (rr->rrtype != kDNSType_NSEC) return mDNSfalse; - - len = DomainNameLength((domainname *)nsec); - // The window that this type belongs to. NSEC has 256 windows that // comprises of 256 types. wintype = type >> 8; - bitmaplen = rr->rdlength - len; - bmap = nsec + len; while (bitmaplen > 0) { if (bitmaplen < 3) { - LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d short", bitmaplen); + LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d short", bitmaplen); return mDNSfalse; } @@ -1459,12 +1639,12 @@ mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 t bitmaplen -= 2; if (bitmaplen < wlen || wlen < 1 || wlen > 32) { - LogInfo("RRAssertsExistence: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win); + LogInfo("BitmapTypeCheck: malformed nsec, bitmaplen %d wlen %d, win %d", bitmaplen, wlen, win); return mDNSfalse; } if (win < 0 || win >= 256) { - LogInfo("RRAssertsExistence: malformed nsec, wlen %d", wlen); + LogInfo("BitmapTypeCheck: malformed nsec, wlen %d", wlen); return mDNSfalse; } if (win == wintype) @@ -1489,6 +1669,24 @@ mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 t return mDNSfalse; } +// Don't call this function if the resource record is not NSEC. It will return false +// which means that the type does not exist. +mDNSexport mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type) +{ + const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; + mDNSu8 *nsec = (mDNSu8 *)rdb->data; + int len, bitmaplen; + mDNSu8 *bmap; + + if (rr->rrtype != kDNSType_NSEC) return mDNSfalse; + + len = DomainNameLength((domainname *)nsec); + + bitmaplen = rr->rdlength - len; + bmap = nsec + len; + return (BitmapTypeCheck(bmap, bitmaplen, type)); +} + // Don't call this function if the resource record is not NSEC. It will return false // which means that the type exists. mDNSexport mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type) @@ -1509,6 +1707,22 @@ mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, c if (q->qtype == rr->rrtype) return mDNStrue; + // For DS and DNSKEY questions, the types should match i.e., don't answer using CNAME + // records as it answers any question type. + // + // - DS record comes from the parent zone where CNAME record cannot coexist and hence + // cannot possibly answer it. + // + // - For DNSKEY, one could potentially follow CNAME but there could be a DNSKEY at + // the "qname" itself. To keep it simple, we don't follow CNAME. + + if ((q->qtype == kDNSType_DS || q->qtype == kDNSType_DNSKEY) && (q->qtype != rr->rrtype)) + { + debugf("DNSSECRecordAnswersQuestion: %d type resource record matched question %##s (%s), ignoring", rr->rrtype, + q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + // If we are validating a response using DNSSEC, we might already have the records // for the "q->qtype" in the cache but we issued a query with DO bit set // to get the RRSIGs e.g., if you have two questions one of which does not require @@ -1519,14 +1733,16 @@ mDNSlocal mDNSBool DNSSECRecordAnswersQuestion(const ResourceRecord *const rr, c { const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; rdataRRSig *rrsig = (rdataRRSig *)rdb->data; - if (q->qtype == kDNSType_CNAME || swap16(rrsig->typeCovered) != q->qtype) + mDNSu16 typeCovered = swap16(rrsig->typeCovered); + debugf("DNSSECRecordAnswersQuestion: Matching RRSIG typeCovered %s", DNSTypeName(typeCovered)); + if (typeCovered != kDNSType_CNAME && typeCovered != q->qtype) { - debugf("DNSSECRecordAnswersQuestion: Question %##s (%s) did not match record %##s (RRSIG)", q->qname.c, - DNSTypeName(q->qtype), rr->name->c); + debugf("DNSSECRecordAnswersQuestion: RRSIG did not match question %##s (%s)", q->qname.c, + DNSTypeName(q->qtype)); return mDNSfalse; } - LogInfo("DNSSECRecordAnswersQuestion: Question %##s (%s) matched record %##s (RRSIG)", q->qname.c, - DNSTypeName(q->qtype), rr->name->c); + LogInfo("DNSSECRecordAnswersQuestion: RRSIG matched question %##s (%s)", q->qname.c, + DNSTypeName(q->qtype)); *checkType = mDNSfalse; return mDNStrue; } @@ -1581,6 +1797,11 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr // If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse); + // CNAME answers question of any type and a negative cache record should not prevent us from querying other + // valid types at the same name. + if (rr->rrtype == kDNSType_CNAME && rr->RecordType == kDNSRecordTypePacketNegative && rr->rrtype != q->qtype) + return mDNSfalse; + // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class. if (checkType && !RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); @@ -1590,6 +1811,9 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr return mDNSfalse; #endif // APPLE_OSX_mDNSResponder + if (!AnonInfoAnswersQuestion(rr, q)) + return mDNSfalse; + return(mDNStrue); } @@ -1674,6 +1898,9 @@ mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const D if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse); if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + if (!AnonInfoAnswersQuestion(rr, q)) + return mDNSfalse; + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); } @@ -1705,6 +1932,9 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse); + if (!AnonInfoAnswersQuestion(rr, q)) + return mDNSfalse; + return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname)); } @@ -2069,9 +2299,13 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS int len = 0; const rdataOPT *opt; const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength]; - for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt); - if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; } - + for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) + len += DNSOpt_Data_Space(opt); + if (ptr + len > limit) + { + LogMsg("ERROR: putOptRData - out of space"); + return mDNSNULL; + } for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) { const int space = DNSOpt_Data_Space(opt); @@ -2106,6 +2340,10 @@ mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNS } } break; + case kDNSOpt_Trace: + *ptr++ = opt->u.tracer.platf; + ptr = putVal16(ptr, opt->u.tracer.mDNSv); + break; } } return ptr; @@ -2207,14 +2445,24 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 * if (rr->RecordType == kDNSRecordTypeUnregistered) { - LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + LogMsg("PutResourceRecordTTLWithLimit ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(ptr); } - if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); } + if (!ptr) + { + LogMsg("PutResourceRecordTTLWithLimit ptr is null %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); + return(mDNSNULL); + } ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name); - if (!ptr || ptr + 10 >= limit) return(mDNSNULL); // If we're out-of-space, return mDNSNULL + // If we're out-of-space, return mDNSNULL + if (!ptr || ptr + 10 >= limit) + { + LogInfo("PutResourceRecordTTLWithLimit: can't put name, out of space %##s (%s), ptr %p, limit %p", rr->name->c, + DNSTypeName(rr->rrtype), ptr, limit); + return(mDNSNULL); + } ptr[0] = (mDNSu8)(rr->rrtype >> 8); ptr[1] = (mDNSu8)(rr->rrtype & 0xFF); ptr[2] = (mDNSu8)(rr->rrclass >> 8); @@ -2226,7 +2474,12 @@ mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 * // ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr); - if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); } + if (!endofrdata) + { + LogInfo("PutResourceRecordTTLWithLimit: Ran out of space in PutResourceRecord for %##s (%s), ptr %p, limit %p", rr->name->c, + DNSTypeName(rr->rrtype), ptr+10, limit); + return(mDNSNULL); + } // Go back and fill in the actual number of data bytes we wrote // (actualLength can be less than rdlength when domain name compression is used) @@ -2550,6 +2803,39 @@ mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 return(ptr + pktrdlength); } +// Sanity check whether the NSEC/NSEC3 bitmap is good +mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int len) +{ + int win, wlen; + + while (bmap < end) + { + if (len < 3) + { + LogInfo("SanityCheckBitMap: invalid length %d", len); + return mDNSNULL; + } + + win = *bmap++; + wlen = *bmap++; + len -= 2; + if (len < wlen || wlen < 1 || wlen > 32) + { + LogInfo("SanityCheckBitMap: invalid window length %d", wlen); + return mDNSNULL; + } + if (win < 0 || win >= 256) + { + LogInfo("SanityCheckBitMap: invalid window %d", win); + return mDNSNULL; + } + + bmap += wlen; + len -= wlen; + } + return (mDNSu8 *)bmap; +} + // This function is called with "msg" when we receive a DNS message and needs to parse a single resource record // pointed to by "ptr". Some resource records like SOA, SRV are converted to host order and also expanded // (domainnames are expanded to 255 bytes) when stored in memory. @@ -2782,7 +3068,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con int savelen, len; domainname name; const mDNSu8 *orig = ptr; - const mDNSu8 *save; // Make sure the data is parseable and within the limits. DNSSEC code looks at // the domain name in the end for a valid domainname. @@ -2822,7 +3107,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con goto fail; } - save = ptr; savelen = ptr - orig; // RFC 2915 states that name compression is not allowed for this field. But RFC 3597 @@ -2910,6 +3194,14 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con opt++; } break; + case kDNSOpt_Trace: + if (opt->optlen == DNSOpt_TraceData_Space - 4) + { + opt->u.tracer.platf = ptr[0]; + opt->u.tracer.mDNSv = (mDNSu16)((mDNSu16)ptr[1] << 8 | ptr[2]); + opt++; + } + break; } ptr += currentopt->optlen; } @@ -2920,7 +3212,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con case kDNSType_NSEC: { domainname name; - int win, wlen; int len = rdlength; int bmaplen, dlen; const mDNSu8 *orig = ptr; @@ -2950,32 +3241,9 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con len -= (ptr - orig); bmaplen = len; // Save the length of the bitmap bmap = ptr; - // Sanity check whether the bitmap is good - while (ptr < end) - { - if (len < 3) - { - LogInfo("SetRData: invalid length %d", len); - goto fail; - } - - win = *ptr++; - wlen = *ptr++; - len -= 2; - if (len < wlen || wlen < 1 || wlen > 32) - { - LogInfo("SetRData: invalid window length %d", wlen); - goto fail; - } - if (win < 0 || win >= 256) - { - LogInfo("SetRData: invalid window %d", win); - goto fail; - } - - ptr += wlen; - len -= wlen; - } + ptr = SanityCheckBitMap(bmap, end, len); + if (!ptr) + goto fail; if (ptr != end) { LogInfo("SetRData: Malformed NSEC length not right"); @@ -2997,6 +3265,56 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con mDNSPlatformMemCopy(rdb->data + dlen, bmap, bmaplen); break; } + case kDNSType_NSEC3: + { + rdataNSEC3 *nsec3 = (rdataNSEC3 *)ptr; + mDNSu8 *p = (mDNSu8 *)&nsec3->salt; + int hashLength, bitmaplen; + + if (rdlength < NSEC3_FIXED_SIZE + 1) + { + LogInfo("SetRData: NSEC3 too small length %d", rdlength); + goto fail; + } + if (nsec3->alg != SHA1_DIGEST_TYPE) + { + LogInfo("SetRData: nsec3 alg %d not supported", nsec3->alg); + goto fail; + } + if (swap16(nsec3->iterations) > NSEC3_MAX_ITERATIONS) + { + LogInfo("SetRData: nsec3 iteration count %d too big", swap16(nsec3->iterations)); + goto fail; + } + p += nsec3->saltLength; + // There should at least be one byte beyond saltLength + if (p >= end) + { + LogInfo("SetRData: nsec3 too small, at saltlength %d, p %p, end %p", nsec3->saltLength, p, end); + goto fail; + } + // p is pointing at hashLength + hashLength = (int)*p++; + if (!hashLength) + { + LogInfo("SetRData: hashLength zero"); + goto fail; + } + p += hashLength; + if (p > end) + { + LogInfo("SetRData: nsec3 too small, at hashLength %d, p %p, end %p", hashLength, p, end); + goto fail; + } + + bitmaplen = rdlength - (int)(p - ptr); + p = SanityCheckBitMap(p, end, bitmaplen); + if (!p) + goto fail; + rr->resrec.rdlength = rdlength; + mDNSPlatformMemCopy(rdb->data, ptr, rdlength); + break; + } case kDNSType_TKEY: case kDNSType_TSIG: { @@ -3088,7 +3406,6 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con mDNSPlatformMemCopy(rdb->data, ptr, rdlength); break; } - default: debugf("SetRData: Warning! Reading resource type %d (%s) as opaque data", rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype)); @@ -3352,7 +3669,7 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t 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" : "Receiv")] = 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)) @@ -3415,6 +3732,14 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS mDNSu8 *newend; mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData; +#if APPLE_OSX_mDNSResponder + // maintain outbound packet statistics + if (mDNSOpaque16IsZero(msg->h.id)) + m->MulticastPacketsSent++; + else + m->UnicastPacketsSent++; +#endif // APPLE_OSX_mDNSResponder + // Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData) { @@ -3440,12 +3765,41 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS { mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg); mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) }; - long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); // Should do scatter/gather here -- this is probably going out as two packets - if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; } + char *buf; + long nsent; + + // Try to send them in one packet if we can allocate enough memory + buf = mDNSPlatformMemAllocate(msglen + 2); + if (buf) + { + buf[0] = lenbuf[0]; + buf[1] = lenbuf[1]; + mDNSPlatformMemCopy(buf+2, msg, msglen); + nsent = mDNSPlatformWriteTCP(sock, buf, msglen+2); + if (nsent != (msglen + 2)) + { + LogMsg("mDNSSendDNSMessage: write message failed %d/%d", nsent, msglen); + status = mStatus_ConnFailed; + } + mDNSPlatformMemFree(buf); + } else { - nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen); - if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; } + nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2); + if (nsent != 2) + { + LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); + status = mStatus_ConnFailed; + } + else + { + nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen); + if (nsent != msglen) + { + LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); + status = mStatus_ConnFailed; + } + } } } } @@ -3546,7 +3900,6 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck; if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA; - if (m->clearIgnoreNA && (e - m->clearIgnoreNA > 0)) e = m->clearIgnoreNA; // NextScheduledSPRetry only valid when DelaySleep not set if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; @@ -3625,8 +3978,7 @@ mDNSexport void ShowTaskSchedulingError(mDNS *const m) LogMsg("Task Scheduling Error: m->NextScheduledProbe %d", m->timenow - m->NextScheduledProbe); if (m->timenow - m->NextScheduledResponse >= 0) LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse); - if (m->clearIgnoreNA && m->timenow - m->clearIgnoreNA >= 0) - LogMsg("Task Scheduling Error: m->clearIgnoreNA %d", m->timenow - m->clearIgnoreNA); + mDNS_Unlock(m); } diff --git a/mDNSCore/DNSCommon.h b/mDNSCore/DNSCommon.h index fe8ccba..b92f5a9 100644 --- a/mDNSCore/DNSCommon.h +++ b/mDNSCore/DNSCommon.h @@ -101,6 +101,8 @@ extern mDNSu32 mDNSRandom(mDNSu32 max); // Returns pseudo-random result from #pragma mark - Domain Name Utility Functions #endif +#define mDNSSubTypeLabel "\x04_sub" + #define mDNSIsDigit(X) ((X) >= '0' && (X) <= '9') #define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z') #define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z') @@ -169,6 +171,7 @@ extern mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q); extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate); extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd); +extern mStatus DNSNameToLowerCase(domainname *d, domainname *result); #define GetRRDomainNameTarget(RR) ( \ ((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name : \ @@ -204,9 +207,9 @@ extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, #define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl) // The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg, -// and assume a local variable 'OwnerRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER option at the end +// and assume local variables 'OwnerRecordSpace' & 'TraceRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER/TRACER option at the end #define PutRR_OS_TTL(ptr, count, rr, ttl) \ - PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace) + PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace) #define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl) @@ -221,7 +224,12 @@ extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease); extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit); extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit); -mDNSexport mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit); +extern mDNSu8 *putDNSSECOption(DNSMessage *msg, mDNSu8 *end, mDNSu8 *limit); +extern int baseEncode(char *buffer, int blen, const mDNSu8 *data, int len, int encAlg); +extern void NSEC3Parse(const ResourceRecord *const rr, mDNSu8 **salt, int *hashLength, mDNSu8 **nxtName, int *bitmaplen, mDNSu8 **bitmap); + +extern const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3, const mDNSu8 *AnonData, int AnonDataLen, + const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen); // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK @@ -255,6 +263,7 @@ extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *trans const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end); extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type); extern mDNSBool RRAssertsExistence(const ResourceRecord *const rr, mDNSu16 type); +extern mDNSBool BitmapTypeCheck(mDNSu8 *bmap, int bitmaplen, mDNSu16 type); extern mDNSu16 swap16(mDNSu16 x); extern mDNSu32 swap32(mDNSu32 x); diff --git a/mDNSCore/DNSDigest.c b/mDNSCore/DNSDigest.c index 1ad8d0c..33798d3 100644 --- a/mDNSCore/DNSDigest.c +++ b/mDNSCore/DNSDigest.c @@ -1426,7 +1426,6 @@ mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeC mDNSs32 then; mDNSu8 thisDigest[MD5_LEN]; mDNSu8 thatDigest[MD5_LEN]; - mDNSu32 macsize; mDNSOpaque16 buf; mDNSu8 utc48[6]; mDNSs32 delta; @@ -1490,8 +1489,6 @@ mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeC // MAC size - macsize = (mDNSu32) NToH16(ptr); - ptr += sizeof(mDNSu16); // MAC diff --git a/mDNSCore/anonymous.c b/mDNSCore/anonymous.c new file mode 100644 index 0000000..94b102e --- /dev/null +++ b/mDNSCore/anonymous.c @@ -0,0 +1,597 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mDNSEmbeddedAPI.h" +#include "CryptoAlg.h" +#include "anonymous.h" +#include "DNSCommon.h" + +// Define ANONYMOUS_DISABLED to remove all the anonymous functionality +// and use the stub functions implemented later in this file. + +#ifndef ANONYMOUS_DISABLED + +#define ANON_NSEC3_ITERATIONS 1 + +mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonData, int len, mDNSu32 salt) +{ + const mDNSu8 *ptr; + rdataNSEC3 *nsec3 = (rdataNSEC3 *)rr->rdata->u.data; + mDNSu8 *tmp, *nxt; + unsigned short iter = ANON_NSEC3_ITERATIONS; + int hlen; + const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + + // Construct the RDATA first and construct the owner name based on that. + ptr = (const mDNSu8 *)&salt; + debugf("InitializeNSEC3Record: %x%x%x%x, name %##s", ptr[0], ptr[1], ptr[2], ptr[3], rr->name->c); + + // Set the RDATA + nsec3->alg = SHA1_DIGEST_TYPE; + nsec3->flags = 0; + nsec3->iterations = swap16(iter); + nsec3->saltLength = 4; + tmp = (mDNSu8 *)&nsec3->salt; + *tmp++ = ptr[0]; + *tmp++ = ptr[1]; + *tmp++ = ptr[2]; + *tmp++ = ptr[3]; + + // hashLength, nxt, bitmap + *tmp++ = SHA1_HASH_LENGTH; // hash length + nxt = tmp; + tmp += SHA1_HASH_LENGTH; + *tmp++ = 0; // window number + *tmp++ = NSEC_MCAST_WINDOW_SIZE; // window length + mDNSPlatformMemZero(tmp, NSEC_MCAST_WINDOW_SIZE); + tmp[kDNSType_PTR >> 3] |= 128 >> (kDNSType_PTR & 7); + + // Hash the base service name + salt + AnonData + if (!NSEC3HashName(rr->name, nsec3, AnonData, len, hashName, &hlen)) + { + LogMsg("InitializeNSEC3Record: NSEC3HashName failed for ##s", rr->name->c); + return mDNSfalse; + } + if (hlen != SHA1_HASH_LENGTH) + { + LogMsg("InitializeNSEC3Record: hlen wrong %d", hlen); + return mDNSfalse; + } + mDNSPlatformMemCopy(nxt, hashName, hlen); + + return mDNStrue; +} + +mDNSlocal ResourceRecord *ConstructNSEC3Record(const domainname *service, const mDNSu8 *AnonData, int len, mDNSu32 salt) +{ + ResourceRecord *rr; + int dlen; + domainname *name; + + // We are just allocating an RData which has StandardAuthRDSize + if (StandardAuthRDSize < MCAST_NSEC3_RDLENGTH) + { + LogMsg("ConstructNSEC3Record: StandardAuthRDSize %d smaller than MCAST_NSEC3_RDLENGTH %d", StandardAuthRDSize, MCAST_NSEC3_RDLENGTH); + return mDNSNULL; + } + + dlen = DomainNameLength(service); + + // Allocate space for the name and RData. + rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + dlen + sizeof(RData)); + if (!rr) + return mDNSNULL; + name = (domainname *)((mDNSu8 *)rr + sizeof(ResourceRecord)); + rr->RecordType = kDNSRecordTypePacketAuth; + rr->InterfaceID = mDNSInterface_Any; + rr->name = (const domainname *)name; + rr->rrtype = kDNSType_NSEC3; + rr->rrclass = kDNSClass_IN; + rr->rroriginalttl = kStandardTTL; + rr->rDNSServer = mDNSNULL; + rr->rdlength = MCAST_NSEC3_RDLENGTH; + rr->rdestimate = MCAST_NSEC3_RDLENGTH; + rr->rdata = (RData *)((mDNSu8 *)rr->name + dlen); + + AssignDomainName(name, service); + if (!InitializeNSEC3Record(rr, AnonData, len, salt)) + { + mDNSPlatformMemFree(rr); + return mDNSNULL; + } + return rr; +} + +mDNSlocal ResourceRecord *CopyNSEC3ResourceRecord(AnonymousInfo *si, const ResourceRecord *rr) +{ + int len; + domainname *name; + ResourceRecord *nsec3rr; + + if (rr->rdlength < MCAST_NSEC3_RDLENGTH) + { + LogMsg("CopyNSEC3ResourceRecord: rdlength %d smaller than MCAST_NSEC3_RDLENGTH %d", rr->rdlength, MCAST_NSEC3_RDLENGTH); + return mDNSNULL; + } + // Allocate space for the name and the rdata along with the ResourceRecord + len = DomainNameLength(rr->name); + nsec3rr = mDNSPlatformMemAllocate(sizeof(ResourceRecord) + len + sizeof(RData)); + if (!nsec3rr) + return mDNSNULL; + + *nsec3rr = *rr; + name = (domainname *)((mDNSu8 *)nsec3rr + sizeof(ResourceRecord)); + nsec3rr->name = (const domainname *)name; + AssignDomainName(name, rr->name); + + nsec3rr->rdata = (RData *)((mDNSu8 *)nsec3rr->name + len); + mDNSPlatformMemCopy(nsec3rr->rdata->u.data, rr->rdata->u.data, rr->rdlength); + + si->nsec3RR = nsec3rr; + + return nsec3rr; +} + +// When a service is started or a browse is started with the Anonymous data, we allocate a new random +// number and based on that allocate a new NSEC3 resource record whose hash is a function of random number (salt) and +// the anonymous data. +// +// If we receive a packet with the NSEC3 option, we need to cache that along with the resource record so that we can +// check against the question to see whether it answers them or not. In that case, we pass the "rr" that we received. +mDNSexport AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *data, int len, const ResourceRecord *rr) +{ + AnonymousInfo *ai; + ai = (AnonymousInfo *)mDNSPlatformMemAllocate(sizeof(AnonymousInfo)); + if (!ai) + { + return mDNSNULL; + } + mDNSPlatformMemZero(ai, sizeof(AnonymousInfo)); + if (rr) + { + if (!CopyNSEC3ResourceRecord(ai, rr)) + { + mDNSPlatformMemFree(ai); + return mDNSNULL; + } + return ai; + } + ai->salt = mDNSRandom(0xFFFFFFFF); + ai->AnonData = mDNSPlatformMemAllocate(len); + if (!ai->AnonData) + { + mDNSPlatformMemFree(ai); + return mDNSNULL; + } + ai->AnonDataLen = len; + mDNSPlatformMemCopy(ai->AnonData, data, len); + ai->nsec3RR = ConstructNSEC3Record(service, data, len, ai->salt); + if (!ai->nsec3RR) + { + mDNSPlatformMemFree(ai); + return mDNSNULL; + } + return ai; +} + +mDNSexport void FreeAnonInfo(AnonymousInfo *ai) +{ + if (ai->nsec3RR) + mDNSPlatformMemFree(ai->nsec3RR); + if (ai->AnonData) + mDNSPlatformMemFree(ai->AnonData); + mDNSPlatformMemFree(ai); +} + +mDNSexport void ReInitAnonInfo(AnonymousInfo **AnonInfo, const domainname *name) +{ + if (*AnonInfo) + { + AnonymousInfo *ai = *AnonInfo; + *AnonInfo = AllocateAnonInfo(name, ai->AnonData, ai->AnonDataLen, mDNSNULL); + if (!(*AnonInfo)) + *AnonInfo = ai; + else + FreeAnonInfo(ai); + } +} + +// This function should be used only if you know that the question and +// the resource record belongs to the same set. The main usage is +// in ProcessQuery where we find the question to be part of the same +// set as the resource record, but it needs the AnonData to be +// initialized so that it can walk the cache records to see if they +// answer the question. +mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) +{ + if (!q->AnonInfo || !rr->AnonInfo) + { + LogMsg("SetAnonData: question %##s(%p), rr %##s(%p), NULL", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); + return; + } + + debugf("SetAnonData: question %##s(%p), rr %##s(%p)", q->qname.c, q->AnonInfo, rr->name->c, rr->AnonInfo); + if (ForQuestion) + { + if (!q->AnonInfo->AnonData) + { + q->AnonInfo->AnonData = mDNSPlatformMemAllocate(rr->AnonInfo->AnonDataLen); + if (!q->AnonInfo->AnonData) + return; + } + mDNSPlatformMemCopy(q->AnonInfo->AnonData, rr->AnonInfo->AnonData, rr->AnonInfo->AnonDataLen); + q->AnonInfo->AnonDataLen = rr->AnonInfo->AnonDataLen; + } + else + { + if (!rr->AnonInfo->AnonData) + { + rr->AnonInfo->AnonData = mDNSPlatformMemAllocate(q->AnonInfo->AnonDataLen); + if (!rr->AnonInfo->AnonData) + return; + } + mDNSPlatformMemCopy(rr->AnonInfo->AnonData, q->AnonInfo->AnonData, q->AnonInfo->AnonDataLen); + rr->AnonInfo->AnonDataLen = q->AnonInfo->AnonDataLen; + } +} + +// returns -1 if the caller should ignore the result +// returns 1 if the record answers the question +// returns 0 if the record does not answer the question +mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + mDNSexport mDNS mDNSStorage; + ResourceRecord *nsec3RR; + int i; + AnonymousInfo *qai, *rai; + mDNSu8 *AnonData; + int AnonDataLen; + rdataNSEC3 *nsec3; + int hlen; + const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + int nxtLength; + mDNSu8 *nxtName; + + debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c); + + // Currently only PTR records can have anonymous information + if (q->qtype != kDNSType_PTR) + { + return -1; + } + + // We allow anonymous questions to be answered by both normal services (without the + // anonymous information) and anonymous services that are part of the same set. And + // normal questions discover normal services and all anonymous services. + // + // The three cases have been enumerated clearly even though they all behave the + // same way. + if (!q->AnonInfo) + { + debugf("AnonInfoAnswersQuestion: not a anonymous type question"); + if (!rr->AnonInfo) + { + // case 1 + return -1; + } + else + { + // case 2 + debugf("AnonInfoAnswersQuestion: Question %##s not answered using anonymous record %##s", q->qname.c, rr->name->c); + return -1; + } + } + else + { + // case 3 + if (!rr->AnonInfo) + { + debugf("AnonInfoAnswersQuestion: not a anonymous type record"); + return -1; + } + } + + // case 4: We have the anonymous information both in the question and the record. We need + // two sets of information to validate. + // + // 1) Anonymous data that identifies the set/group + // 2) NSEC3 record that contains the hash and the salt + // + // If the question is a remote one, it does not have the anonymous information to validate (just + // the NSEC3 record) and hence the anonymous data should come from the local resource record. If the + // question is local, it can come from either of them and if there is a mismatch between the + // question and record, it won't validate. + + qai = q->AnonInfo; + rai = rr->AnonInfo; + + if (qai->AnonData && rai->AnonData) + { + // Before a cache record is created, if there is a matching question i.e., part + // of the same set, then when the cache is created we also set the anonymous + // information. Otherwise, the cache record contains just the NSEC3 record and we + // won't be here for that case. + // + // It is also possible that a local question is matched against the local AuthRecord + // as that is also the case for which the AnonData would be non-NULL for both. + // We match questions against AuthRecords (rather than the cache) for LocalOnly case and + // to see whether a .local query should be suppressed or not. The latter never happens + // because PTR queries are never suppressed. + + // If they don't belong to the same anonymous set, then no point in validating. + if ((qai->AnonDataLen != rai->AnonDataLen) || + mDNSPlatformMemCmp(qai->AnonData, rai->AnonData, qai->AnonDataLen) != 0) + { + debugf("AnonInfoAnswersQuestion: AnonData mis-match for record %s question %##s ", + RRDisplayString(&mDNSStorage, rr), q->qname.c); + return 0; + } + // AnonData matches i.e they belong to the same group and the same service. + LogInfo("AnonInfoAnswersQuestion: Answering qname %##s, rname %##s, without validation", q->qname.c, + rr->name->c); + return 1; + } + else + { + debugf("AnonInfoAnswersQuestion: question %p, record %p", qai->AnonData, rai->AnonData); + } + + if (qai->AnonData) + { + // If there is AnonData, then this is a local question. The + // NSEC3 RR comes from the resource record which could be part + // of the cache or local auth record. The cache entry could + // be from a remote host or created when we heard our own + // announcements. In any case, we use that to see if it matches + // the question. + AnonData = qai->AnonData; + AnonDataLen = qai->AnonDataLen; + nsec3RR = rai->nsec3RR; + } + else + { + // Remote question or hearing our own question back + AnonData = rai->AnonData; + AnonDataLen = rai->AnonDataLen; + nsec3RR = qai->nsec3RR; + } + + if (!AnonData || !nsec3RR) + { + // AnonData can be NULL for the cache entry and if we are hearing our own question back, AnonData is NULL for + // that too and we can end up here for that case. + debugf("AnonInfoAnswersQuestion: AnonData %p or nsec3RR %p, NULL for question %##s, record %s", AnonData, nsec3RR, + q->qname.c, RRDisplayString(&mDNSStorage, rr)); + return 0; + } + debugf("AnonInfoAnswersQuestion: Validating question %##s, ResourceRecord %s", q->qname.c, RRDisplayString(&mDNSStorage, nsec3RR)); + + + nsec3 = (rdataNSEC3 *)nsec3RR->rdata->u.data; + + if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen)) + { + LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for ##s", nsec3RR->name->c); + return mDNSfalse; + } + if (hlen != SHA1_HASH_LENGTH) + { + LogMsg("AnonInfoAnswersQuestion: hlen wrong %d", hlen); + return mDNSfalse; + } + + NSEC3Parse(nsec3RR, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL); + + if (hlen != nxtLength) + { + LogMsg("AnonInfoAnswersQuestion: ERROR!! hlen %d not same as nxtLength %d", hlen, nxtLength); + return mDNSfalse; + } + + for (i = 0; i < nxtLength; i++) + { + if (nxtName[i] != hashName[i]) + { + debugf("AnonInfoAnswersQuestion: mismatch output %x, digest %x, i %d", nxtName[i+1], hashName[i], i); + return 0; + } + } + LogInfo("AnonInfoAnswersQuestion: ResourceRecord %s matched question %##s (%s)", RRDisplayString(&mDNSStorage, nsec3RR), q->qname.c, DNSTypeName(q->qtype)); + return 1; +} + +// Find a matching NSEC3 record for the name. We parse the questions and the records in the packet in order. +// Similarly we also parse the NSEC3 records in order and this mapping to the questions and records +// respectively. +mDNSlocal CacheRecord *FindMatchingNSEC3ForName(mDNS *const m, CacheRecord **nsec3, const domainname *name) +{ + CacheRecord *cr; + CacheRecord **prev = nsec3; + + (void) m; + + for (cr = *nsec3; cr; cr = cr->next) + { + if (SameDomainName(cr->resrec.name, name)) + { + debugf("FindMatchingNSEC3ForName: NSEC3 record %s matched %##s", CRDisplayString(m, cr), name->c); + *prev = cr->next; + cr->next = mDNSNULL; + return cr; + } + prev = &cr->next; + } + return mDNSNULL; +} + +mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) +{ + CacheRecord *nsec3CR; + + if (q->qtype != kDNSType_PTR) + return; + + nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, &q->qname); + if (nsec3CR) + { + q->AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); + if (q->AnonInfo) + { + debugf("InitializeAnonInfoForQuestion: Found a matching NSEC3 record %s, for %##s (%s)", + RRDisplayString(m, q->AnonInfo->nsec3RR), q->qname.c, DNSTypeName(q->qtype)); + } + ReleaseCacheRecord(m, nsec3CR); + } +} + +mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) +{ + CacheRecord *nsec3CR; + + if (!(*McastNSEC3Records)) + return; + + // If already initialized or not a PTR type, we don't have to do anything + if (cr->resrec.AnonInfo || cr->resrec.rrtype != kDNSType_PTR) + return; + + nsec3CR = FindMatchingNSEC3ForName(m, McastNSEC3Records, cr->resrec.name); + if (nsec3CR) + { + cr->resrec.AnonInfo = AllocateAnonInfo(mDNSNULL, mDNSNULL, 0, &nsec3CR->resrec); + if (cr->resrec.AnonInfo) + { + debugf("InitializeAnonInfoForCR: Found a matching NSEC3 record %s, for %##s (%s)", + RRDisplayString(m, cr->resrec.AnonInfo->nsec3RR), cr->resrec.name->c, + DNSTypeName(cr->resrec.rrtype)); + } + ReleaseCacheRecord(m, nsec3CR); + } +} + +mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) +{ + // if a1 is NULL and a2 is not NULL AND vice-versa + // return false as there is a change. + if ((a1 != mDNSNULL) != (a2 != mDNSNULL)) + return mDNSfalse; + + // Both could be NULL or non-NULL + if (a1 && a2) + { + // The caller already verified that the owner name is the same. + // Check whether the RData is same. + if (!IdenticalSameNameRecord(a1->nsec3RR, a2->nsec3RR)) + { + debugf("IdenticalAnonInfo: nsec3RR mismatch"); + return mDNSfalse; + } + } + return mDNStrue; +} + +mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) +{ + AnonymousInfo *aifrom = crfrom->resrec.AnonInfo; + AnonymousInfo *aito = crto->resrec.AnonInfo; + + (void) m; + + if (!aifrom) + return; + + if (aito) + { + crto->resrec.AnonInfo = aifrom; + FreeAnonInfo(aito); + crfrom->resrec.AnonInfo = mDNSNULL; + } + else + { + FreeAnonInfo(aifrom); + crfrom->resrec.AnonInfo = mDNSNULL; + } +} + +#else // !ANONYMOUS_DISABLED + +mDNSexport void ReInitAnonInfo(AnonymousInfo **si, const domainname *name) +{ + (void)si; + (void)name; +} + +mDNSexport AnonymousInfo * AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr) +{ + (void)service; + (void)AnonData; + (void)len; + (void)rr; + + return mDNSNULL; +} + +mDNSexport void FreeAnonInfo(AnonymousInfo *ai) +{ + (void)ai; +} + +mDNSexport void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion) +{ + (void)q; + (void)rr; + (void)ForQuestion; +} + +mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) +{ + (void)rr; + (void)q; + + return mDNSfalse; +} + +mDNSexport void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q) +{ + (void)m; + (void)McastNSEC3Records; + (void)q; +} + +mDNSexport void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr) +{ + (void)m; + (void)McastNSEC3Records; + (void)cr; +} + +mDNSexport void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom) +{ + (void)m; + (void)crto; + (void)crfrom; +} + +mDNSexport mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2) +{ + (void)a1; + (void)a2; + + return mDNStrue; +} + +#endif // !ANONYMOUS_DISABLED diff --git a/mDNSCore/anonymous.h b/mDNSCore/anonymous.h new file mode 100644 index 0000000..2f2b4f8 --- /dev/null +++ b/mDNSCore/anonymous.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ANONYMOUS_H_ +#define __ANONYMOUS_H_ + +extern void ReInitAnonInfo(AnonymousInfo **si, const domainname *name); +extern AnonymousInfo *AllocateAnonInfo(const domainname *service, const mDNSu8 *AnonData, int len, const ResourceRecord *rr); +extern void FreeAnonInfo(AnonymousInfo *ai); +extern void SetAnonData(DNSQuestion *q, ResourceRecord *rr, mDNSBool ForQuestion); +extern int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); +extern void InitializeAnonInfoForCR(mDNS *const m, CacheRecord **McastNSEC3Records, CacheRecord *cr); +extern void InitializeAnonInfoForQuestion(mDNS *const m, CacheRecord **McastNSEC3Records, DNSQuestion *q); +extern void CopyAnonInfoForCR(mDNS *const m, CacheRecord *crto, CacheRecord *crfrom); +extern mDNSBool IdenticalAnonInfo(AnonymousInfo *a1, AnonymousInfo *a2); + +#endif diff --git a/mDNSCore/dnsproxy.c b/mDNSCore/dnsproxy.c new file mode 100644 index 0000000..11bacc1 --- /dev/null +++ b/mDNSCore/dnsproxy.c @@ -0,0 +1,836 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dnsproxy.h" + +#ifndef UNICAST_DISABLED + +// Implementation Notes +// +// DNS Proxy listens on port 53 (UDPv4v6 & TCPv4v6) for DNS queries. It handles only +// the "Query" opcode of the DNS protocol described in RFC 1035. For all other opcodes, it returns +// "Not Implemented" error. The platform interface mDNSPlatformInitDNSProxySkts +// sets up the sockets and whenever it receives a packet, it calls ProxyTCPCallback or ProxyUDPCallback +// defined here. For TCP socket, the platform does the "accept" and only sends the received packets +// on the newly accepted socket. A single UDP socket (per address family) is used to send/recv +// requests/responses from all clients. For TCP, there is one socket per request. Hence, there is some +// extra state that needs to be disposed at the end. +// +// When a DNS request is received, ProxyCallbackCommon checks for malformed packet etc. and also checks +// for duplicates, before creating DNSProxyClient state and starting a question with the "core" +// (mDNS_StartQuery). When the callback for the question happens, it gathers all the necessary +// resource records, constructs a response and sends it back to the client. +// +// - Question callback is called with only one resource record at a time. We need all the resource +// records to construct the response. Hence, we lookup all the records ourselves. +// +// - The response may not fit the client's buffer size. In that case, we need to set the truncate bit +// and the client would retry using TCP. +// +// - The client may have set the DNSSEC OK bit in the EDNS0 option and that means we also have to +// return the RRSIGs or the NSEC records with the RRSIGs in the Additional section. We need to +// ask the "core" to fetch the DNSSEC records and do the validation if the CD bit is not set. +// +// Once the response is sent to the client, the client state is disposed. When there is no response +// from the "core", it eventually times out and we will not find any answers in the cache and we send a +// "NXDomain" response back. Thus, we don't need any special timers to reap the client state in the case +// of errors. + +typedef struct DNSProxyClient_struct DNSProxyClient; + +struct DNSProxyClient_struct { + + DNSProxyClient *next; + mDNSAddr addr; // Client's IP address + mDNSIPPort port; // Client's port number + mDNSOpaque16 msgid; // DNS msg id + mDNSInterfaceID interfaceID; // Interface on which we received the request + void *socket; // Return socket + mDNSBool tcp; // TCP or UDP ? + mDNSOpaque16 requestFlags; // second 16 bit word in the DNSMessageHeader of the request + mDNSu8 *optRR; // EDNS0 option + mDNSu16 optLen; // Total Length of the EDNS0 option + mDNSu16 rcvBufSize; // How much can the client receive ? + mDNSBool DNSSECOK; // DNSSEC OK ? + void *context; // Platform context to be disposed if non-NULL + domainname qname; // q->qname can't be used for duplicate check + DNSQuestion q; // as it can change underneath us for CNAMEs +}; + +#define MIN_DNS_MESSAGE_SIZE 512 +DNSProxyClient *DNSProxyClients; + +mDNSlocal void FreeDNSProxyClient(DNSProxyClient *pc) +{ + if (pc->optRR) + mDNSPlatformMemFree(pc->optRR); + mDNSPlatformMemFree(pc); +} + +mDNSlocal mDNSBool ParseEDNS0(DNSProxyClient *pc, const mDNSu8 *ptr, int length, const mDNSu8 *limit) +{ + mDNSu16 rrtype, rrclass; + mDNSu8 rcode, version; + mDNSu16 flag; + + if (ptr + length > limit) + { + LogInfo("ParseEDNS0: Not enough space in the packet"); + return mDNSfalse; + } + // Skip the root label + ptr++; + rrtype = (mDNSu16) ((mDNSu16)ptr[0] << 8 | ptr[1]); + if (rrtype != kDNSType_OPT) + { + LogInfo("ParseEDNS0: Not the right type %d", rrtype); + return mDNSfalse; + } + rrclass = (mDNSu16) ((mDNSu16)ptr[2] << 8 | ptr[3]); + rcode = ptr[4]; + version = ptr[5]; + flag = (mDNSu16) ((mDNSu16)ptr[6] << 8 | ptr[7]); + + debugf("rrtype is %s, length is %d, rcode %d, version %d, flag 0x%x", DNSTypeName(rrtype), rrclass, rcode, version, flag); + pc->rcvBufSize = rrclass; + pc->DNSSECOK = ptr[6] & 0x80; + + return mDNStrue; +} + +mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) +{ + DNSProxyClient *pc = (DNSProxyClient *)q->QuestionContext; + + (void) msg; + + h->flags = pc->requestFlags; + if (pc->optRR) + { + if (ptr + pc->optLen > limit) + { + LogInfo("DNSProxySetAttributes: Cannot set EDNS0 option start %p, OptLen %d, end %p", ptr, pc->optLen, limit); + return ptr; + } + h->numAdditionals++; + mDNSPlatformMemCopy(ptr, pc->optRR, pc->optLen); + ptr += pc->optLen; + } + return ptr; +} + +mDNSlocal mDNSu8 *AddEDNS0Option(mDNS *const m, mDNSu8 *ptr, mDNSu8 *limit) +{ + int len = 4096; + + if (ptr + 11 > limit) + { + LogInfo("AddEDNS0Option: not enough space"); + return mDNSNULL; + } + m->omsg.h.numAdditionals++; + ptr[0] = 0; + ptr[1] = (mDNSu8) (kDNSType_OPT >> 8); + ptr[2] = (mDNSu8) (kDNSType_OPT & 0xFF); + ptr[3] = (mDNSu8) (len >> 8); + ptr[4] = (mDNSu8) (len & 0xFF); + ptr[5] = 0; // rcode + ptr[6] = 0; // version + ptr[7] = 0; + ptr[8] = 0; // flags + ptr[9] = 0; // rdlength + ptr[10] = 0; // rdlength + + debugf("AddEDNS0 option"); + + return (ptr + 11); +} + +// Currently RD and CD bit should be copied if present in the request or cleared if +// not present in the request. RD bit is normally set in the response and hence the +// cache reflects the right value. CD bit behaves differently. If the CD bit is set +// the first time, the cache retains it, if it is present in response (assuming the +// upstream server does it right). Next time through we should not use the cached +// value of the CD bit blindly. It depends on whether it was in the request or not. +mDNSlocal mDNSOpaque16 SetResponseFlags(DNSProxyClient *pc, const mDNSOpaque16 responseFlags) +{ + mDNSOpaque16 rFlags = responseFlags; + + if (pc->requestFlags.b[0] & kDNSFlag0_RD) + rFlags.b[0] |= kDNSFlag0_RD; + else + rFlags.b[0] &= ~kDNSFlag0_RD; + + if (pc->requestFlags.b[1] & kDNSFlag1_CD) + rFlags.b[1] |= kDNSFlag1_CD; + else + rFlags.b[1] &= ~kDNSFlag1_CD; + + return rFlags; +} + +mDNSlocal mDNSu8 *AddResourceRecords(mDNS *const m, DNSProxyClient *pc, mDNSu8 **prevptr, mStatus *error) +{ + mDNSu32 slot; + CacheGroup *cg; + CacheRecord *cr; + int len = sizeof(DNSMessageHeader); + mDNSu8 *orig = m->omsg.data; + mDNSBool first = mDNStrue; + mDNSu8 *ptr = mDNSNULL; + mDNSs32 now; + mDNSs32 ttl; + CacheRecord *nsec = mDNSNULL; + CacheRecord *soa = mDNSNULL; + CacheRecord *cname = mDNSNULL; + mDNSu8 *limit; + + *error = mStatus_NoError; + *prevptr = mDNSNULL; + + mDNS_Lock(m); + now = m->timenow; + mDNS_Unlock(m); + + if (!pc->tcp) + { + if (!pc->rcvBufSize) + { + limit = m->omsg.data + MIN_DNS_MESSAGE_SIZE; + } + else + { + limit = (pc->rcvBufSize > AbsoluteMaxDNSMessageData ? m->omsg.data + AbsoluteMaxDNSMessageData : m->omsg.data + pc->rcvBufSize); + } + } + else + { + // For TCP, limit is not determined by EDNS0 but by 16 bit rdlength field and + // AbsoluteMaxDNSMessageData is smaller than 64k. + limit = m->omsg.data + AbsoluteMaxDNSMessageData; + } + LogInfo("AddResourceRecords: Limit is %d", limit - m->omsg.data); + + if (!SameDomainName(&pc->qname, &pc->q.qname)) + { + AssignDomainName(&pc->q.qname, &pc->qname); + pc->q.qnamehash = DomainNameHashValue(&pc->q.qname); + } + +again: + nsec = soa = cname = mDNSNULL; + slot = HashSlot(&pc->q.qname); + + cg = CacheGroupForName(m, slot, pc->q.qnamehash, &pc->q.qname); + if (!cg) + { + LogInfo("AddResourceRecords: CacheGroup not found"); + *error = mStatus_NoSuchRecord; + return mDNSNULL; + } + // Set ValidatingResponse so that you can get RRSIGs also matching + // the question + if (pc->DNSSECOK) + pc->q.ValidatingResponse = 1; + for (cr = cg->members; cr; cr = cr->next) + { + if (SameNameRecordAnswersQuestion(&cr->resrec, &pc->q)) + { + if (first) + { + // If this is the first time, initialize the header and the question. + // This code needs to be here so that we can use the responseFlags from the + // cache record + mDNSOpaque16 responseFlags = SetResponseFlags(pc, cr->responseFlags); + InitializeDNSMessage(&m->omsg.h, pc->msgid, responseFlags); + ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass); + if (!ptr) + { + LogInfo("AddResourceRecords: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + return mDNSNULL; + } + first = mDNSfalse; + } + // - For NegativeAnswers there is nothing to add + // - If DNSSECOK is set, we also automatically lookup the RRSIGs which + // will also be returned. If the client is explicitly looking up + // a DNSSEC record (e.g., DNSKEY, DS) we should return the response. + // DNSSECOK bit only influences whether we add the RRSIG or not. + if (cr->resrec.RecordType != kDNSRecordTypePacketNegative) + { + LogInfo("AddResourceRecords: Answering question with %s", CRDisplayString(m, cr)); + ttl = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAnswers, &cr->resrec, ttl, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + // If we have nsecs (wildcard expanded answer or negative response), add them + // in the additional section below if the DNSSECOK bit is set + if (pc->DNSSECOK && cr->nsec) + { + LogInfo("AddResourceRecords: nsec set for %s", CRDisplayString(m ,cr)); + nsec = cr->nsec; + } + if (cr->soa) + { + LogInfo("AddResourceRecords: soa set for %s", CRDisplayString(m ,cr)); + soa = cr->soa; + } + // If we are using CNAME to answer a question and CNAME is not the type we + // are looking for, note down the CNAME record so that we can follow them + // later. Before we follow the CNAME, print the RRSIGs and any nsec (wildcard + // expanded) if any. + if ((pc->q.qtype != cr->resrec.rrtype) && cr->resrec.rrtype == kDNSType_CNAME) + { + LogInfo("AddResourceRecords: cname set for %s", CRDisplayString(m ,cr)); + cname = cr; + } + } + } + // Along with the nsec records, we also cache the SOA record. For non-DNSSEC question, we need + // to send the SOA back. Normally we either cache the SOA record (non-DNSSEC question) pointed + // to by "cr->soa" or the NSEC/SOA records along with their RRSIGs (DNSSEC question) pointed to + // by "cr->nsec". Two cases: + // + // - if we issue a DNSSEC question followed by non-DNSSEC question for the same name, + // we only have the nsec records and we need to filter the SOA record alone for the + // non-DNSSEC questions. + // + // - if we issue a non-DNSSEC question followed by DNSSEC question for the same name, + // the "core" flushes the cache entry and re-issue the question with EDNS0/DOK bit and + // in this case we return all the DNSSEC records we have. + for (; nsec; nsec = nsec->next) + { + if (!pc->DNSSECOK && DNSSECRecordType(nsec->resrec.rrtype)) + continue; + LogInfo("AddResourceRecords:NSEC Answering question with %s", CRDisplayString(m, nsec)); + ttl = nsec->resrec.rroriginalttl - (now - nsec->TimeRcvd) / mDNSPlatformOneSecond; + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &nsec->resrec, ttl, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + if (soa) + { + LogInfo("AddResourceRecords: SOA Answering question with %s", CRDisplayString(m, soa)); + ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.numAuthorities, &soa->resrec, soa->resrec.rroriginalttl, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + if (cname) + { + AssignDomainName(&pc->q.qname, &cname->resrec.rdata->u.name); + pc->q.qnamehash = DomainNameHashValue(&pc->q.qname); + goto again; + } + if (!ptr) + { + LogInfo("AddResourceRecords: Did not find any valid ResourceRecords"); + *error = mStatus_NoSuchRecord; + return mDNSNULL; + } + if (pc->rcvBufSize) + { + ptr = AddEDNS0Option(m, ptr, limit); + if (!ptr) + { + *prevptr = orig; + return mDNSNULL; + } + len += (ptr - orig); + orig = ptr; + } + LogInfo("AddResourceRecord: Added %d bytes to the packet", len); + return ptr; +} + +mDNSlocal void ProxyClientCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + DNSProxyClient *pc = question->QuestionContext; + DNSProxyClient **ppc = &DNSProxyClients; + mDNSu8 *ptr; + mDNSu8 *prevptr; + mStatus error; + + if (!AddRecord) + return; + + LogInfo("ProxyClientCallback: ResourceRecord %s", RRDisplayString(m, answer)); + + // We asked for validation and not timed out yet, then wait for the DNSSEC result. + // We have to set the AD bit in the response if it is secure which can't be done + // till we get the DNSSEC result back (indicated by QC_dnssec). + if (question->ValidationRequired) + { + mDNSs32 now; + + mDNS_Lock(m); + now = m->timenow; + mDNS_Unlock(m); + if (((now - question->StopTime) < 0) && AddRecord != QC_dnssec) + { + LogInfo("ProxyClientCallback: No DNSSEC answer yet for Question %##s (%s), AddRecord %d, answer %s", question->qname.c, + DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer)); + return; + } + } + + if (answer->RecordType != kDNSRecordTypePacketNegative) + { + if (answer->rrtype != question->qtype) + { + // Wait till we get called for the real response + LogInfo("ProxyClientCallback: Received %s, not answering yet", RRDisplayString(m, answer)); + return; + } + } + ptr = AddResourceRecords(m, pc, &prevptr, &error); + if (!ptr) + { + LogInfo("ProxyClientCallback: AddResourceRecords NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + if (error == mStatus_NoError && prevptr) + { + // No space to add the record. Set the Truncate bit for UDP. + // + // TBD: For TCP, we need to send the rest of the data. But finding out what is left + // is harder. We should allocate enough buffer in the first place to send all + // of the data. + if (!pc->tcp) + { + m->omsg.h.flags.b[0] |= kDNSFlag0_TC; + ptr = prevptr; + } + else + { + LogInfo("ProxyClientCallback: ERROR!! Not enough space to return in TCP for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + ptr = prevptr; + } + } + else + { + mDNSOpaque16 flags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery, kDNSFlag1_RC_ServFail } }; + // We could not find the record for some reason. Return a response, so that the client + // is not waiting forever. + LogInfo("ProxyClientCallback: No response"); + if (!mDNSOpaque16IsZero(pc->q.responseFlags)) + flags = pc->q.responseFlags; + InitializeDNSMessage(&m->omsg.h, pc->msgid, flags); + ptr = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &pc->qname, pc->q.qtype, pc->q.qclass); + if (!ptr) + { + LogInfo("ProxyClientCallback: putQuestion NULL for %##s (%s)", &pc->qname.c, DNSTypeName(pc->q.qtype)); + goto done; + } + } + } + if (question->ValidationRequired) + { + if (question->ValidationState == DNSSECValDone && question->ValidationStatus == DNSSEC_Secure) + { + LogInfo("ProxyClientCallback: Setting AD bit for Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + m->omsg.h.flags.b[1] |= kDNSFlag1_AD; + } + else + { + // If some external resolver sets the AD bit and we did not validate the response securely, don't set + // the AD bit. It is possible that we did not see all the records that the upstream resolver saw or + // a buggy implementation somewhere. + if (m->omsg.h.flags.b[1] & kDNSFlag1_AD) + { + LogInfo("ProxyClientCallback: AD bit set in the response for response that was not validated locally %##s (%s)", + question->qname.c, DNSTypeName(question->qtype)); + m->omsg.h.flags.b[1] &= ~kDNSFlag1_AD; + } + } + } + + if (!pc->tcp) + { + mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, (UDPSocket *)pc->socket, &pc->addr, pc->port, mDNSNULL, mDNSNULL, mDNSfalse); + } + else + { + mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &pc->addr, pc->port, (TCPSocket *)pc->socket, mDNSNULL, mDNSfalse); + } + +done: + mDNS_StopQuery(m, question); + + while (*ppc && *ppc != pc) + ppc=&(*ppc)->next; + if (!*ppc) + { + LogMsg("ProxyClientCallback: question %##s (%s) not found", question->qname.c, DNSTypeName(question->qtype)); + return; + } + *ppc = pc->next; + mDNSPlatformDisposeProxyContext(pc->context); + FreeDNSProxyClient(pc); +} + +mDNSlocal void SendError(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *dstaddr, + const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context, mDNSu8 rcode) +{ + int pktlen = (int)(end - (mDNSu8 *)pkt); + DNSMessage *msg = (DNSMessage *)pkt; + + (void) InterfaceID; + + // RFC 1035 requires that we copy the question back and RFC 2136 is okay with sending nothing + // in the body or send back whatever we get for updates. It is easy to return whatever we get + // in the question back to the responder. We return as much as we can fit in our standard + // output packet. + if (pktlen > AbsoluteMaxDNSMessageData) + pktlen = AbsoluteMaxDNSMessageData; + + mDNSPlatformMemCopy(&m->omsg.h, &msg->h, sizeof(DNSMessageHeader)); + m->omsg.h.flags.b[0] |= kDNSFlag0_QR_Response; + m->omsg.h.flags.b[1] = rcode; + mDNSPlatformMemCopy(m->omsg.data, (mDNSu8 *)&msg->h.numQuestions, pktlen); + if (!tcp) + { + mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, socket, dstaddr, dstport, mDNSNULL, mDNSNULL, + mDNSfalse); + } + else + { + mDNSSendDNSMessage(m, &m->omsg, (mDNSu8 *)&m->omsg + pktlen, mDNSInterface_Any, mDNSNULL, dstaddr, dstport, (TCPSocket *)socket, + mDNSNULL, mDNSfalse); + } + mDNSPlatformDisposeProxyContext(context); +} + +mDNSlocal DNSQuestion *IsDuplicateClient(const mDNS *const m, const mDNSAddr *const addr, const mDNSIPPort port, const mDNSOpaque16 id, + const DNSQuestion *const question) +{ + DNSProxyClient *pc; + + (void) m; // unused + + for (pc = DNSProxyClients; pc; pc = pc->next) + { + if (mDNSSameAddress(&pc->addr, addr) && + mDNSSameIPPort(pc->port, port) && + mDNSSameOpaque16(pc->msgid, id) && + pc->q.qtype == question->qtype && + pc->q.qclass == question->qclass && + SameDomainName(&pc->qname, &question->qname)) + { + LogInfo("IsDuplicateClient: Found a duplicate client in the list"); + return(&pc->q); + } + } + return(mDNSNULL); +} + +mDNSlocal mDNSBool CheckDNSProxyIpIntf(const mDNS *const m, mDNSInterfaceID InterfaceID) +{ + int i; + mDNSu32 ip_ifindex = (mDNSu32)(unsigned long)InterfaceID; + + LogInfo("CheckDNSProxyIpIntf: Stored Input Interface List: [%d] [%d] [%d] [%d] [%d]", m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], + m->dp_ipintf[3], m->dp_ipintf[4]); + + for (i = 0; i < MaxIp; i++) + { + if (ip_ifindex == m->dp_ipintf[i]) + return mDNStrue; + } + return mDNSfalse; + +} + +mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, mDNSBool tcp, void *context) +{ + DNSMessage *msg = (DNSMessage *)pkt; + mDNSu8 QR_OP; + const mDNSu8 *ptr; + DNSQuestion q, *qptr; + DNSProxyClient *pc; + const mDNSu8 *optRR; + int optLen = 0; + DNSProxyClient **ppc = &DNSProxyClients; + + (void) dstaddr; + (void) dstport; + + debugf("ProxyCallbackCommon: DNS Query coming from InterfaceID %p", InterfaceID); + // Ignore if the DNS Query is not from a Valid Input InterfaceID + if (!CheckDNSProxyIpIntf(m, InterfaceID)) + return; + + if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) + { + debugf("ProxyCallbackCommon: DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + return; + } + + QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask); + if (QR_OP != kDNSFlag0_QR_Query) + { + LogInfo("ProxyCallbackCommon: Not a query(%d) for pkt from %#a:%d", QR_OP, srcaddr, mDNSVal16(srcport)); + SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_NotImpl); + return; + } + + // Read the integer parts which are in IETF byte-order (MSB first, LSB second) + ptr = (mDNSu8 *)&msg->h.numQuestions; + msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]); + msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]); + msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]); + msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]); + + if (msg->h.numQuestions != 1 || msg->h.numAnswers || msg->h.numAuthorities) + { + LogInfo("ProxyCallbackCommon: Malformed pkt from %#a:%d, Q:%d, An:%d, Au:%d", srcaddr, mDNSVal16(srcport), + msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities); + SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); + return; + } + ptr = msg->data; + ptr = getQuestion(msg, ptr, end, InterfaceID, &q); + if (!ptr) + { + LogInfo("ProxyCallbackCommon: Question cannot be parsed for pkt from %#a:%d", srcaddr, mDNSVal16(srcport)); + SendError(m, socket, pkt, end, srcaddr, srcport, InterfaceID, tcp, context, kDNSFlag1_RC_FormErr); + return; + } + else + { + LogInfo("ProxyCallbackCommon: Question %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + } + ptr = LocateOptRR(msg, end, 0); + if (ptr) + { + optRR = ptr; + ptr = skipResourceRecord(msg, ptr, end); + // Be liberal and ignore the EDNS0 option if we can't parse it properly + if (!ptr) + { + LogInfo("ProxyCallbackCommon: EDNS0 cannot be parsed for pkt from %#a:%d, ignoring", srcaddr, mDNSVal16(srcport)); + } + else + { + optLen = ptr - optRR; + LogInfo("ProxyCallbackCommon: EDNS0 opt length %d present in Question %##s (%s)", optLen, q.qname.c, DNSTypeName(q.qtype)); + } + } + else + { + LogInfo("ProxyCallbackCommon: EDNS0 opt not present in Question %##s (%s), ptr %p", q.qname.c, DNSTypeName(q.qtype), ptr); + } + + qptr = IsDuplicateClient(m, srcaddr, srcport, msg->h.id, &q); + if (qptr) + { + LogInfo("ProxyCallbackCommon: Found a duplicate for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + return; + } + pc = mDNSPlatformMemAllocate(sizeof(DNSProxyClient)); + if (!pc) + { + LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + return; + } + mDNSPlatformMemZero(pc, sizeof(DNSProxyClient)); + pc->addr = *srcaddr; + pc->port = srcport; + pc->msgid = msg->h.id; + pc->interfaceID = InterfaceID; // input interface + pc->socket = socket; + pc->tcp = tcp; + pc->requestFlags = msg->h.flags; + pc->context = context; + AssignDomainName(&pc->qname, &q.qname); + if (optRR) + { + if (!ParseEDNS0(pc, optRR, optLen, end)) + { + LogInfo("ProxyCallbackCommon: Invalid EDNS0 option for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + } + else + { + pc->optRR = mDNSPlatformMemAllocate(optLen); + if (!pc->optRR) + { + LogMsg("ProxyCallbackCommon: Memory failure for pkt from %#a:%d, ignoring this", srcaddr, mDNSVal16(srcport)); + FreeDNSProxyClient(pc); + return; + } + mDNSPlatformMemCopy(pc->optRR, optRR, optLen); + pc->optLen = optLen; + } + } + + debugf("ProxyCallbackCommon: DNS Query forwarding to interface index %d", m->dp_opintf); + mDNS_SetupQuestion(&pc->q, (mDNSInterfaceID)(unsigned long)m->dp_opintf, &q.qname, q.qtype, ProxyClientCallback, pc); + pc->q.TimeoutQuestion = 1; + // Even though we don't care about intermediate responses, set ReturnIntermed so that + // we get the negative responses + pc->q.ReturnIntermed = mDNStrue; + pc->q.ProxyQuestion = mDNStrue; + pc->q.ProxyDNSSECOK = pc->DNSSECOK; + pc->q.responseFlags = zeroID; + if (pc->DNSSECOK) + { + if (!(msg->h.flags.b[1] & kDNSFlag1_CD) && pc->q.qtype != kDNSType_RRSIG && pc->q.qtype != kDNSQType_ANY) + { + LogInfo("ProxyCallbackCommon: Setting Validation required bit for %#a:%d, validating %##s (%s)", srcaddr, mDNSVal16(srcport), + q.qname.c, DNSTypeName(q.qtype)); + pc->q.ValidationRequired = DNSSEC_VALIDATION_SECURE; + } + else + { + LogInfo("ProxyCallbackCommon: CD bit not set OR not a valid type for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport), + q.qname.c, DNSTypeName(q.qtype)); + } + } + else + { + LogInfo("ProxyCallbackCommon: DNSSEC OK bit not set for %#a:%d, not validating %##s (%s)", srcaddr, mDNSVal16(srcport), + q.qname.c, DNSTypeName(q.qtype)); + } + + while (*ppc) + ppc = &((*ppc)->next); + *ppc = pc; + + mDNS_StartQuery(m, &pc->q); +} + +mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + LogInfo("ProxyUDPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNSfalse, context); +} + +mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + LogInfo("ProxyTCPCallback: DNS Message from %#a:%d to %#a:%d length %d", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt); + // If the connection was closed from the other side, locate the client + // state and free it. + if ((end - (mDNSu8 *)pkt) == 0) + { + DNSProxyClient **ppc = &DNSProxyClients; + DNSProxyClient **prevpc; + + prevpc = ppc; + while (*ppc && (*ppc)->socket != socket) + { + prevpc = ppc; + ppc=&(*ppc)->next; + } + if (!*ppc) + { + mDNSPlatformDisposeProxyContext(socket); + LogMsg("ProxyTCPCallback: socket cannot be found"); + return; + } + *prevpc = (*ppc)->next; + LogInfo("ProxyTCPCallback: free"); + mDNSPlatformDisposeProxyContext(socket); + FreeDNSProxyClient(*ppc); + return; + } + ProxyCallbackCommon(m, socket, pkt, end, srcaddr, srcport, dstaddr, dstport, InterfaceID, mDNStrue, context); +} + +mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf) +{ + int i; + + // Store DNSProxy Interface fields in mDNS struct + for (i = 0; i < MaxIp; i++) + m->dp_ipintf[i] = IpIfArr[i]; + m->dp_opintf = OpIf; + + LogInfo("DNSProxyInit Storing interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0], + m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); +} + +mDNSexport void DNSProxyTerminate(mDNS *const m) +{ + int i; + + // Clear DNSProxy Interface fields from mDNS struct + for (i = 0; i < MaxIp; i++) + m->dp_ipintf[i] = 0; + m->dp_opintf = 0; + + LogInfo("DNSProxyTerminate Cleared interface list: Input [%d, %d, %d, %d, %d] Output [%d]", m->dp_ipintf[0], + m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); +} +#else // UNICAST_DISABLED + +mDNSexport void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + (void) m; + (void) socket; + (void) pkt; + (void) end; + (void) srcaddr; + (void) srcport; + (void) dstaddr; + (void) dstport; + (void) InterfaceID; + (void) context; +} + +mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context) +{ + (void) m; + (void) socket; + (void) pkt; + (void) end; + (void) srcaddr; + (void) srcport; + (void) dstaddr; + (void) dstport; + (void) InterfaceID; + (void) context; +} + +mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf) +{ + (void) m; + (void) IpIfArr; + (void) OpIf; +} +extern void DNSProxyTerminate(mDNS *const m) +{ + (void) m; +} + + +#endif // UNICAST_DISABLED diff --git a/mDNSCore/dnsproxy.h b/mDNSCore/dnsproxy.h new file mode 100644 index 0000000..ed46a12 --- /dev/null +++ b/mDNSCore/dnsproxy.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __DNS_PROXY_H +#define __DNS_PROXY_H + +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" + +extern void ProxyUDPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); +extern void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); +extern void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf); +extern void DNSProxyTerminate(mDNS *const m); + +#endif // __DNS_PROXY_H diff --git a/mDNSCore/dnssec.c b/mDNSCore/dnssec.c index 5e582de..c83b841 100644 --- a/mDNSCore/dnssec.c +++ b/mDNSCore/dnssec.c @@ -15,10 +15,17 @@ * limitations under the License. */ #include "mDNSEmbeddedAPI.h" +#include "DNSSECSupport.h" #include "DNSCommon.h" #include "dnssec.h" #include "CryptoAlg.h" #include "nsec.h" +#include "nsec3.h" + +// Define DNSSEC_DISABLED to remove all the DNSSEC functionality +// and use the stub functions implemented later in this file. + +#ifndef DNSSEC_DISABLED //#define DNSSEC_DEBUG @@ -65,12 +72,20 @@ // is done, DNSSECPositiveValidationCB or DNSSECNegativeValidationCB will be called which will then deliver the // validation results to the original question that started the validation. // +// Insecure proofs are done when the verification ends up bogus. The flow would look like this +// +// VerifySignature -> StartDNSSECVerification - GetAllRRSetsForVerification -> FinishDNSSECVerification -> DNSSECValidationCB +// {DNSSECPositiveValidationCB, DNSSECNegativeValidationCB} -> ProveInsecure -> VerifySignaure -> +// +// ProveInsecure finds the break in trust in a top-down fashion. +// // Forward declaration mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv); mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv); mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv); -mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); +mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status); +mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from); // Currently we use this to convert a RRVerifier to resource record so that we can // use the standard DNS utility functions @@ -80,6 +95,9 @@ LargeCacheRecord largerec; // removed in the future. #define MAX_RECURSE_COUNT 10 +// TTL (in seconds) when the DNSSEC status is Bogus +#define RR_BOGUS_TTL 60 + // RFC 4034 Appendix B: Get the keyid of a DNS KEY. It is not transmitted // explicitly on the wire. // @@ -103,7 +121,7 @@ mDNSlocal mDNSu32 keytag(mDNSu8 *key, mDNSu32 keysize) return ac & 0xFFFF; } -mDNSlocal int DNSMemCmp(mDNSu8 *const m1, mDNSu8 *const m2, int len) +mDNSexport int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len) { int res; @@ -113,32 +131,96 @@ mDNSlocal int DNSMemCmp(mDNSu8 *const m1, mDNSu8 *const m2, int len) return 0; } -mDNSlocal mStatus DNSNameToLowerCase(domainname *d, domainname *result) +// RFC 4034: +// +// Section 6.1: +// +// For the purposes of DNS security, owner names are ordered by treating +// individual labels as unsigned left-justified octet strings. The +// absence of a octet sorts before a zero value octet, and uppercase +// US-ASCII letters are treated as if they were lowercase US-ASCII +// letters. +// +// To compute the canonical ordering of a set of DNS names, start by +// sorting the names according to their most significant (rightmost) +// labels. For names in which the most significant label is identical, +// continue sorting according to their next most significant label, and +// so forth. +// +// Returns 0 if the names are same +// Returns -1 if d1 < d2 +// Returns 1 if d1 > d2 +// +// subdomain is set if there is at least one label match (starting from the end) +// and d1 has more labels than d2 e.g., a.b.com is a subdomain of b.com +// +mDNSexport int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain) { - const mDNSu8 *a = d->c; - mDNSu8 *b = result->c; - const mDNSu8 *const max = d->c + MAX_DOMAIN_NAME; - int i, len; + int count, c1, c2; + int i, skip1, skip2; + + c1 = CountLabels(d1); + skip1 = c1 - 1; + c2 = CountLabels(d2); + skip2 = c2 - 1; - while (*a) + if (subdomain) *subdomain = 0; + + // Compare as many labels as possible starting from the rightmost + count = c1 < c2 ? c1 : c2; + for (i = count; i > 0; i--) { - if (a + 1 + *a >= max) - { - LogMsg("DNSNameToLowerCase: ERROR!! Malformed Domain name"); - return mStatus_BadParamErr; - } - len = *a++; - *b++ = len; - for (i = 0; i < len; i++) + mDNSu8 *a, *b; + int j, len, lena, lenb; + + a = (mDNSu8 *)SkipLeadingLabels(d1, skip1); + b = (mDNSu8 *)SkipLeadingLabels(d2, skip2); + lena = *a; + lenb = *b; + // Compare label by label. Note that "z" > "yak" because z > y, but z < za + // (lena - lenb check below) because 'za' has two characters. Hence compare the + // letters first and then compare the length of the label at the end. + len = lena < lenb ? lena : lenb; + a++; b++; + for (j = 0; j < len; j++) { mDNSu8 ac = *a++; + mDNSu8 bc = *b++; if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; - *b++ = ac; + if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; + if (ac != bc) + { + verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac, bc); + return ((ac < bc) ? -1 : 1); + } + } + if ((lena - lenb) != 0) + { + verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena, lenb); + return ((lena < lenb) ? -1 : 1); } + + // Continue with the next label + skip1--; + skip2--; } - *b = 0; + // We have compared label by label. Both of them are same if we are here. + // + // Two possibilities. + // + // 1) Both names have same number of labels. In that case, return zero. + // 2) The number of labels is not same. As zero label sorts before, names + // with more number of labels is greater. - return mStatus_NoError; + // a.b.com is a subdomain of b.com + if ((c1 > c2) && subdomain) + *subdomain = 1; + + verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1, c2); + if (c1 != c2) + return ((c1 < c2) ? -1 : 1); + else + return 0; } // Initialize the question enough so that it can be answered from the cache using SameNameRecordAnswersQuestion or @@ -146,7 +228,7 @@ mDNSlocal mStatus DNSNameToLowerCase(domainname *d, domainname *result) mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, mDNSu16 qtype, mDNSQuestionCallback *callback, void *context) { - LogOperation("InitializeQuestion: Called for %##s (%s)", qname->c, DNSTypeName(qtype)); + debugf("InitializeQuestion: Called for %##s (%s)", qname->c, DNSTypeName(qtype)); if (question->ThisQInterval != -1) mDNS_StopQuery(m, question); @@ -154,10 +236,13 @@ mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInt question->qnamehash = DomainNameHashValue(qname); question->ValidatingResponse = mDNStrue; + // Need to hold the lock, as GetServerForQuestion (its callers) references m->timenow. + mDNS_Lock(m); // We need to set the DNS server appropriately to match the question against the cache record. // Though not all callers of this function need it, we always do it to keep it simple. SetValidDNSServers(m, question); question->qDNSServer = GetServerForQuestion(m, question); + mDNS_Unlock(m); // Make it look like unicast question->TargetQID = onesID; @@ -168,7 +253,7 @@ mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInt } mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID, - DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback) + mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback) { DNSSECVerifier *dv; @@ -176,6 +261,8 @@ mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainnam if (!dv) { LogMsg("AllocateDNSSECVerifier: ERROR!! memory alloc failed"); return mDNSNULL; } mDNSPlatformMemZero(dv, sizeof(*dv)); + LogDNSSEC("AllocateDNSSECVerifier called %p", dv); + // Remember the question's name and type so that when we are done processing all // the verifications, we can trace the original question back AssignDomainName(&dv->origName, name); @@ -183,22 +270,87 @@ mDNSexport DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainnam dv->InterfaceID = InterfaceID; dv->DVCallback = dvcallback; dv->q.ThisQInterval = -1; - dv->ac = mDNSNULL; - dv->actail = &dv->ac; + ResetAuthChain(dv); + // These two are used for Insecure proof if we end up doing it. + // -Value of ValidationRequired so that we know whether this is a secure or insecure validation + // -InsecureProofDone tells us whether the proof has been done or not + dv->ValidationRequired = ValidationRequired; + dv->InsecureProofDone = 0; + dv->NumPackets = 0; + mDNS_Lock(m); + dv->StartTime = m->timenow; + mDNS_Unlock(m); // The verifier's question has to be initialized as some of the callers assume it InitializeQuestion(m, &dv->q, InterfaceID, name, rrtype, qcallback, dv); return dv; } -mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv) +mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae) +{ + RRVerifier *rvfrom, **rvto; + AuthChain **prev = mDNSNULL; + AuthChain *retac = mDNSNULL; + AuthChain *ac; + + + while (ae) + { + ac = mDNSPlatformMemAllocate(sizeof(AuthChain)); + if (!ac) + { + LogMsg("AuthChainCopy: AuthChain alloc failure"); + return mDNSfalse; + } + + ac->next = mDNSNULL; + + if (!retac) + retac = ac; + + rvfrom = ae->rrset; + rvto = &ac->rrset; + while (rvfrom) + { + *rvto = CopyRRVerifier(rvfrom); + rvfrom = rvfrom->next; + rvto = &((*rvto)->next); + } + + rvfrom = ae->rrsig; + rvto = &ac->rrsig; + while (rvfrom) + { + *rvto = CopyRRVerifier(rvfrom); + rvfrom = rvfrom->next; + rvto = &((*rvto)->next); + } + + rvfrom = ae->key; + rvto = &ac->key; + while (rvfrom) + { + *rvto = CopyRRVerifier(rvfrom); + rvfrom = rvfrom->next; + rvto = &((*rvto)->next); + } + + if (prev) + { + *prev = ac; + } + prev = &(ac->next); + ae = ae->next; + } + return retac; +} + +mDNSlocal void FreeDNSSECAuthChainInfo(AuthChain *ac) { RRVerifier *rrset; RRVerifier *next; - AuthChain *ac, *acnext; - - LogDNSSEC("FreeDNSSECAuthChain: called"); + AuthChain *acnext; - ac = dv->ac; + LogDNSSEC("FreeDNSSECAuthChainInfo: called"); while (ac) { @@ -233,7 +385,29 @@ mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv) mDNSPlatformMemFree(ac); ac = acnext; } - dv->ac = mDNSNULL; +} + +mDNSlocal void FreeDNSSECAuthChain(DNSSECVerifier *dv) +{ + if (dv->ac) + { + FreeDNSSECAuthChainInfo(dv->ac); + // if someone reuses the "dv", it will be initialized properly + ResetAuthChain(dv); + } + if (dv->saveac) + { + FreeDNSSECAuthChainInfo(dv->saveac); + dv->saveac = mDNSNULL; + } +} + +mDNSlocal void FreeAuthChain(mDNS *const m, void *context) +{ + AuthChain *ac = (AuthChain *)context; + (void) m; // unused + + FreeDNSSECAuthChainInfo(ac); } mDNSlocal void FreeDNSSECVerifierRRSets(DNSSECVerifier *dv) @@ -286,20 +460,26 @@ mDNSlocal void FreeDNSSECVerifierRRSets(DNSSECVerifier *dv) rrset = next; } dv->ds = mDNSNULL; - if (dv->pendingNSEC) + rrset = dv->pendingNSEC; + while (rrset) { - mDNSPlatformMemFree(dv->pendingNSEC); - dv->pendingNSEC = mDNSNULL; + next = rrset->next; + mDNSPlatformMemFree(rrset); + rrset = next; } + dv->pendingNSEC = mDNSNULL; } mDNSexport void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv) { LogDNSSEC("FreeDNSSECVerifier called %p", dv); - if (dv->q.ThisQInterval != -1) mDNS_StopQuery(m, &dv->q); + if (dv->q.ThisQInterval != -1) + mDNS_StopQuery(m, &dv->q); FreeDNSSECVerifierRRSets(dv); - if (dv->ctx) AlgDestroy(dv->ctx); - if (dv->ac) FreeDNSSECAuthChain(dv); + if (dv->ctx) + AlgDestroy(dv->ctx); + if (dv->ac || dv->saveac) + FreeDNSSECAuthChain(dv); if (dv->parent) { LogDNSSEC("FreeDNSSECVerifier freeing parent %p", dv->parent); @@ -308,6 +488,23 @@ mDNSexport void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv) mDNSPlatformMemFree(dv); } +mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from) +{ + RRVerifier *r; + + r = mDNSPlatformMemAllocate(sizeof (RRVerifier) + from->rdlength); + if (!r) + { + LogMsg("CopyRRVerifier: memory failure"); + return mDNSNULL; + } + mDNSPlatformMemCopy(r, from, sizeof(RRVerifier)); + r->next = mDNSNULL; + r->rdata = (mDNSu8*) ((mDNSu8 *)r + sizeof(RRVerifier)); + mDNSPlatformMemCopy(r->rdata, from->rdata, r->rdlength); + return r; +} + mDNSexport RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status) { RRVerifier *r; @@ -510,6 +707,7 @@ mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRec CacheGroup *cg; CacheRecord *cr; RRVerifier *rv; + mDNSBool expectRRSIG = mDNSfalse; *negcr = mDNSNULL; if (!dv->rrset) @@ -531,19 +729,30 @@ mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRec for (cr=cg->members; cr; cr=cr->next) { debugdnssec("CheckRRSIGForRRSet: checking the validity of rrsig"); - if (cr->resrec.rrtype != kDNSType_RRSIG) continue; + if (cr->resrec.rrtype != kDNSType_RRSIG) + { + // Check to see if we should expect RRSIGs for the type that we are looking for. + // We would expect RRSIGs, if we had previously issued the question with the + // EDNS0/DOK bit set. + if (cr->resrec.rrtype == dv->rrset->rrtype) + { + expectRRSIG = cr->CRDNSSECQuestion; + LogDNSSEC("CheckRRSIGForRRSet: %s RRSIG for %s", (expectRRSIG ? "Expecting" : "Not Expecting"), CRDisplayString(m, cr)); + } + continue; + } if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) { if (!(*negcr)) { LogDNSSEC("CheckRRSIGForRRSet: Negative cache record %s encountered for %##s (%s)", CRDisplayString(m, cr), - rv->name.c, rv->rrtype); + rv->name.c, DNSTypeName(rv->rrtype)); *negcr = cr; } else { LogMsg("CheckRRSIGForRRSet: ERROR!! Negative cache record %s already set for %##s (%s)", CRDisplayString(m, cr), - rv->name.c, rv->rrtype); + rv->name.c, DNSTypeName(rv->rrtype)); } continue; } @@ -553,11 +762,16 @@ mDNSlocal mStatus CheckRRSIGForRRSet(mDNS *const m, DNSSECVerifier *dv, CacheRec { // Encountered both RRSIG and negative CR LogMsg("CheckRRSIGForRRSet: ERROR!! Encountered negative cache record %s and RRSIG for %##s (%s)", - CRDisplayString(m, *negcr), rv->name.c, rv->rrtype); + CRDisplayString(m, *negcr), rv->name.c, DNSTypeName(rv->rrtype)); return mStatus_BadParamErr; } + // If we can't find RRSIGs, but we find a negative response then we need to validate that + // which the caller will do it. Otherwise, if we should be expecting RRSIGs to be in the + // cache already, then return error. if (dv->rrsig || *negcr) return mStatus_NoError; + else if (expectRRSIG) + return mStatus_BadParamErr; else return mStatus_NoSuchRecord; } @@ -675,6 +889,7 @@ mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecor CacheRecord *cr; rdataRRSig *rrsig; domainname *name; + mDNSBool expectRRSIG = mDNSfalse; *negcr = mDNSNULL; if (!dv->rrsig) @@ -700,7 +915,18 @@ mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecor } for (cr=cg->members; cr; cr=cr->next) { - if (cr->resrec.rrtype != kDNSType_RRSIG) continue; + if (cr->resrec.rrtype != kDNSType_RRSIG) + { + // Check to see if we should expect RRSIGs for the DNSKEY record that we are + // looking for. We would expect RRSIGs, if we had previously issued the question + // with the EDNS0/DOK bit set. + if (cr->resrec.rrtype == kDNSType_DNSKEY) + { + expectRRSIG = cr->CRDNSSECQuestion; + LogDNSSEC("CheckRRSIGForKey: %s RRSIG for %s", (expectRRSIG ? "Expecting" : "Not Expecting"), CRDisplayString(m, cr)); + } + continue; + } if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) { if (!(*negcr)) @@ -726,8 +952,12 @@ mDNSlocal mStatus CheckRRSIGForKey(mDNS *const m, DNSSECVerifier *dv, CacheRecor CRDisplayString(m, *negcr), name->c); return mStatus_BadParamErr; } + // If we can't find RRSIGs, but we find a negative response then we need to validate that + // which the caller will do it. Finally, make sure that we are not expecting RRSIGS. if (dv->rrsigKey || *negcr) return mStatus_NoError; + else if (expectRRSIG) + return mStatus_BadParamErr; else return mStatus_NoSuchRecord; } @@ -848,7 +1078,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv if (!dv->rrset) { LogMsg("GetAllRRSetsForVerification: ERROR!! rrset NULL"); - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return mDNSfalse; } @@ -866,7 +1096,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv err = CheckRRSIGForRRSet(m, dv, &negcr); if (err != mStatus_NoSuchRecord && err != mStatus_NoError) { - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return mDNSfalse; } // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs @@ -877,7 +1107,6 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv // We may not have the NSECS if the previous query was a non-DNSSEC query if (negcr && negcr->nsec) { - dv->DVCallback = DNSSECNegativeValidationCB; ValidateWithNSECS(m, dv, negcr); return mDNSfalse; } @@ -900,15 +1129,16 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv // Note: For this to work, the core needs to deliver RRSIGs when they are added to the cache even if the // "qtype" is not RRSIG. debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for RRSET"); + dv->NumPackets++; mDNS_StartQuery(m, &dv->q); return mDNSfalse; } - // if we found the RRSIG, then fall through to find the DNSKEY + // if we found the RRSIG, then fall through to find the DNSKEY case RRVS_key: err = CheckKeyForRRSIG(m, dv, &negcr); if (err != mStatus_NoSuchRecord && err != mStatus_NoError) { - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return mDNSfalse; } // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs @@ -918,7 +1148,6 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv // We may not have the NSECS if the previous query was a non-DNSSEC query if (negcr && negcr->nsec) { - dv->DVCallback = DNSSECNegativeValidationCB; ValidateWithNSECS(m, dv, negcr); return mDNSfalse; } @@ -927,6 +1156,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv if (!dv->key) { debugdnssec("GetAllRRSetsForVerification: Fetching DNSKEY for RRSET"); + dv->NumPackets++; mDNS_StartQuery(m, &dv->q); return mDNSfalse; } @@ -936,7 +1166,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv // if we are falling through, then it is okay if we don't find the record if (err != mStatus_NoSuchRecord && err != mStatus_NoError) { - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return mDNSfalse; } // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs @@ -946,15 +1176,15 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv // We may not have the NSECS if the previous query was a non-DNSSEC query if (negcr && negcr->nsec) { - dv->DVCallback = DNSSECNegativeValidationCB; ValidateWithNSECS(m, dv, negcr); return mDNSfalse; } dv->next = RRVS_ds; - debugdnssec("VerifySigCallback: RRVS_rrsig_key %p", dv->rrsigKey); + debugdnssec("GetAllRRSetsForVerification: RRVS_rrsig_key %p", dv->rrsigKey); if (!dv->rrsigKey) { debugdnssec("GetAllRRSetsForVerification: Fetching RRSIGS for DNSKEY"); + dv->NumPackets++; mDNS_StartQuery(m, &dv->q); return mDNSfalse; } @@ -968,7 +1198,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv err = CheckDSForKey(m, dv, &negcr); if (err != mStatus_NoSuchRecord && err != mStatus_NoError) { - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return mDNSfalse; } // Need to initialize the question as if we end up in ValidateWithNSECS below, the nsec proofs @@ -977,7 +1207,6 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv // We may not have the NSECS if the previous query was a non-DNSSEC query if (negcr && negcr->nsec) { - dv->DVCallback = DNSSECNegativeValidationCB; ValidateWithNSECS(m, dv, negcr); return mDNSfalse; } @@ -994,6 +1223,7 @@ mDNSlocal mDNSBool GetAllRRSetsForVerification(mDNS *const m, DNSSECVerifier *dv return mDNSfalse; } debugdnssec("GetAllRRSetsForVerification: Fetching DS"); + dv->NumPackets++; mDNS_StartQuery(m, &dv->q); return mDNSfalse; } @@ -1696,10 +1926,10 @@ mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrse dv->ctx = AlgCreate(CRYPTO_ALG, rrsig->alg); if (!dv->ctx) { - LogMsg("ValidateSignatureWithKey: ERROR!! No algorithm support for %d", rrsig->alg); + LogDNSSEC("ValidateSignatureWithKey: ERROR!! No algorithm support for %d", rrsig->alg); return mDNSfalse; } - AlgAdd(dv->ctx, (mDNSu8 *)rrsig, RRSIG_FIXED_SIZE); + AlgAdd(dv->ctx, (const mDNSu8 *)rrsig, RRSIG_FIXED_SIZE); AlgAdd(dv->ctx, signerName.c, sigNameLen); if (labels - rrsig->labels > 0) @@ -1756,7 +1986,9 @@ mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrse { ptr->rdata = mDNSPlatformMemAllocate(ptr->rdlength); if (ptr->rdata) + { mDNSPlatformMemCopy(ptr->rdata, tmp->rdata, tmp->rdlength); + } else { for (i = 0; i < nrrsets; i++) @@ -1791,16 +2023,16 @@ mDNSlocal mDNSBool ValidateSignatureWithKey(DNSSECVerifier *dv, RRVerifier *rrse } // Add the fixed part - AlgAdd(dv->ctx, fixedPart, fixedPartLen); + AlgAdd(dv->ctx, (const mDNSu8 *)fixedPart, fixedPartLen); // Add the rdlength rdlen = swap16(p->rdlength); - AlgAdd(dv->ctx, (mDNSu8 *)&rdlen, sizeof(mDNSu16)); + AlgAdd(dv->ctx, (const mDNSu8 *)&rdlen, sizeof(mDNSu16)); ConvertRDATAToCanonical(p->rrtype, p->rdlength, p->rdata); PrintVarSignInfo(rdlen, p->rdata); - AlgAdd(dv->ctx, p->rdata, p->rdlength); + AlgAdd(dv->ctx, (const mDNSu8 *)p->rdata, p->rdlength); } // free the memory as we don't need it anymore for (i = 0; i < nrrsets; i++) @@ -1960,7 +2192,7 @@ mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv) digestLen = dsv->rdlength - DS_FIXED_SIZE; AlgAdd(dv->ctx, name.c, DomainNameLength(&name)); - AlgAdd(dv->ctx, key, keyv->rdlength); + AlgAdd(dv->ctx, (const mDNSu8 *)key, keyv->rdlength); algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen); AlgDestroy(dv->ctx); @@ -2099,7 +2331,7 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu { DNSQuestion question; CacheRecord *rr; - RRVerifier *rv; + RRVerifier *rrsigv; rdataRRSig *rrsig; mDNSu32 slot; CacheGroup *cg; @@ -2109,25 +2341,27 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu domainname *qname; mDNSu16 qtype; CacheRecord *rrsigRR; + mDNSs32 now; debugdnssec("SetTTLRRSet called"); - // TBD: Just handle secure for now - if (status != DNSSEC_Secure) return; - - // check to make sure we built a AuthChain as part of verification - if (!dv->ac || !dv->ac->rrset || !dv->ac->rrsig || !dv->ac->key) + if (status == DNSSEC_Insecure || status == DNSSEC_Indeterminate) { - LogMsg("SetTTLRRSet: ERROR!! NULL element in chain"); - FreeDNSSECVerifier(m, dv); + LogDNSSEC("SetTTLRRSET: not setting ttl for status %s", DNSSECStatusName(status)); return; } + mDNS_Lock(m); + now = m->timenow; + mDNS_Unlock(m); + mDNSPlatformMemZero(&question, sizeof(DNSQuestion)); rrTTL = rrsigTTL = rrsigOrigTTL = rrsigTimeTTL = 0; // 1. Locate the rrset name and get its TTL (take the first one as a representative - // of the rrset). + // of the rrset). Ideally, we should set the TTL on the first validation. Instead, + // we do it whenever we validate which happens whenever a ValidationRequired question + // finishes validation. qname = &dv->origName; qtype = dv->origType; @@ -2136,11 +2370,18 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu slot = HashSlot(&question.qname); cg = CacheGroupForName(m, slot, question.qnamehash, &question.qname); - if (!cg) { LogMsg("SetTTLRRSet cg NULL"); return; } + if (!cg) + { + LogMsg("SetTTLRRSet cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + return; + } + for (rr = cg->members; rr; rr = rr->next) if (SameNameRecordAnswersQuestion(&rr->resrec, &question)) { - rrTTL = rr->resrec.rroriginalttl; + // originalttl is never touched. The actual TTL is derived based on when it was + // received. + rrTTL = rr->resrec.rroriginalttl - (now - rr->TimeRcvd)/mDNSPlatformOneSecond; break; } @@ -2155,63 +2396,75 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu // 2. Get the RRSIG ttl. For NSEC records we need to get the NSEC record's TTL as // the negative cache record that we created may not be right. - rv = dv->ac->rrsig; - rrsig = (rdataRRSig *)rv->rdata; - sigNameLen = DomainNameLength((domainname *)&rrsig->signerName); - // pointer to signature and the length - ptr = (mDNSu8 *)(rv->rdata + sigNameLen + RRSIG_FIXED_SIZE); - len = rv->rdlength - RRSIG_FIXED_SIZE - sigNameLen; + if (dv->ac && dv->ac->rrsig) + { + rrsigv = dv->ac->rrsig; + rrsig = (rdataRRSig *)rrsigv->rdata; + sigNameLen = DomainNameLength((domainname *)&rrsig->signerName); + // pointer to signature and the length + ptr = (mDNSu8 *)(rrsigv->rdata + sigNameLen + RRSIG_FIXED_SIZE); + len = rrsigv->rdlength - RRSIG_FIXED_SIZE - sigNameLen; + } + else + { + rrsigv = mDNSNULL; + rrsig = mDNSNULL; + ptr = mDNSNULL; + sigNameLen = len = 0; + } rrsigRR = mDNSNULL; - if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && status == DNSSEC_Secure) { CacheRecord *ncr; rrTTL = 0; for (ncr = rr->nsec; ncr; ncr = ncr->next) { - if (ncr->resrec.rrtype == kDNSType_NSEC) + if (ncr->resrec.rrtype == kDNSType_NSEC || ncr->resrec.rrtype == kDNSType_NSEC3) { - rrTTL = ncr->resrec.rroriginalttl; + rrTTL = ncr->resrec.rroriginalttl - (now - ncr->TimeRcvd)/mDNSPlatformOneSecond; debugdnssec("SetTTLRRSet: NSEC TTL %u", rrTTL); } // Note: we can't use dv->origName here as the NSEC record's RRSIG may not match // the original name - if (ncr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(ncr->resrec.name, &rv->name)) + if (rrsigv && ncr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(ncr->resrec.name, &rrsigv->name)) { RDataBody2 *rdb = (RDataBody2 *)ncr->resrec.rdata->u.data; rdataRRSig *sig = (rdataRRSig *)rdb->data; - if (rv->rdlength != ncr->resrec.rdlength) + if (rrsigv->rdlength != ncr->resrec.rdlength) { debugdnssec("SetTTLRRSet length mismatch"); continue; } - if (mDNSPlatformMemSame(sig, rrsig, rv->rdlength)) + if (mDNSPlatformMemSame(sig, rrsig, rrsigv->rdlength)) { - rrsigTTL = ncr->resrec.rroriginalttl; - rrsigOrigTTL = swap32(rrsig->origTTL); + mDNSu32 remain = (now - ncr->TimeRcvd)/mDNSPlatformOneSecond; + rrsigTTL = ncr->resrec.rroriginalttl - remain; + rrsigOrigTTL = swap32(rrsig->origTTL) - remain; rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime); } } - if (rrTTL && rrsigTTL) break; + if (rrTTL && (!rrsigv || rrsigTTL)) break; } } - else + else if (rrsigv) { // Look for the matching RRSIG so that we can get its TTL for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) - if (rr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(rr->resrec.name, &rv->name)) + if (rr->resrec.rrtype == kDNSType_RRSIG && SameDomainName(rr->resrec.name, &rrsigv->name)) { RDataBody2 *rdb = (RDataBody2 *)rr->resrec.rdata->u.data; rdataRRSig *sig = (rdataRRSig *)rdb->data; - if (rv->rdlength != rr->resrec.rdlength) + if (rrsigv->rdlength != rr->resrec.rdlength) { debugdnssec("SetTTLRRSet length mismatch"); continue; } - if (mDNSPlatformMemSame(sig, rrsig, rv->rdlength)) + if (mDNSPlatformMemSame(sig, rrsig, rrsigv->rdlength)) { - rrsigTTL = rr->resrec.rroriginalttl; - rrsigOrigTTL = swap32(rrsig->origTTL); + mDNSu32 remain = (now - rr->TimeRcvd)/mDNSPlatformOneSecond; + rrsigTTL = rr->resrec.rroriginalttl - remain; + rrsigOrigTTL = swap32(rrsig->origTTL) - remain; rrsigTimeTTL = swap32(rrsig->sigExpireTime) - swap32(rrsig->sigInceptTime); rrsigRR = rr; break; @@ -2219,29 +2472,41 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu } } - if (!rrTTL || !rrsigTTL || !rrsigOrigTTL || !rrsigTimeTTL) + // It is possible that there are no RRSIGs and in that case it is not an error + // to find the rrsigTTL. + if (!rrTTL || (rrsigv && (!rrsigTTL || !rrsigOrigTTL || !rrsigTimeTTL))) { - LogMsg("SetTTLRRSet: ERROR!! Bad TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", + LogDNSSEC("SetTTLRRSet: ERROR!! Bad TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype)); return; } - else + LogDNSSEC("SetTTLRRSet: TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", + rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype)); + + if (status == DNSSEC_Bogus) { - LogDNSSEC("SetTTLRRSet: TTL rrtl %u, rrsigTTL %u, rrsigOrigTTL %u, rrsigTimeTTL %u for %##s (%s)", - rrTTL, rrsigTTL, rrsigOrigTTL, rrsigTimeTTL, qname->c, DNSTypeName(qtype)); + rrTTL = RR_BOGUS_TTL; + LogDNSSEC("SetTTLRRSet: setting to bogus TTL %d", rrTTL); } - if (rrsigTTL < rrTTL) - rrTTL = rrsigTTL; - if (rrsigOrigTTL < rrTTL) - rrTTL = rrsigOrigTTL; - if (rrsigTimeTTL < rrTTL) - rrTTL = rrsigTimeTTL; + if (rrsigv) + { + if (rrsigTTL < rrTTL) + rrTTL = rrsigTTL; + if (rrsigOrigTTL < rrTTL) + rrTTL = rrsigOrigTTL; + if (rrsigTimeTTL < rrTTL) + rrTTL = rrsigTimeTTL; + } // Set the rrsig's TTL. For NSEC records, rrsigRR is NULL which means it expires when // the negative cache record expires. if (rrsigRR) + { rrsigRR->resrec.rroriginalttl = rrTTL; + rrsigRR->TimeRcvd = now; + rrsigRR->UnansweredQueries = 0; + } // Find the RRset and set its TTL for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) @@ -2251,6 +2516,8 @@ mDNSlocal void SetTTLRRSet(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus statu LogDNSSEC("SetTTLRRSet: Setting the TTL %d for %s, question %##s (%s)", rrTTL, CRDisplayString(m, rr), question.qname.c, DNSTypeName(rr->resrec.rrtype)); rr->resrec.rroriginalttl = rrTTL; + rr->TimeRcvd = now; + rr->UnansweredQueries = 0; SetNextCacheCheckTimeForRecord(m, rr); } } @@ -2281,7 +2548,7 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) // unlinks the resultKey and resultRRSig if (!AuthChainAdd(dv, resultKey, resultRRSig)) { - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } // The callback will be called when NSEC verification is done. @@ -2302,7 +2569,7 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) // unlinks the resultKey and resultRRSig if (!AuthChainAdd(dv, resultKey, resultRRSig)) { - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } FreeDNSSECVerifierRRSets(dv); @@ -2318,21 +2585,22 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) else { LogDNSSEC("FinishDNSSECVerification: ValidateDS failed %##s (%s)", dv->rrset->name.c, DNSTypeName(dv->rrset->rrtype)); - dv->DVCallback(m, dv, DNSSEC_Insecure); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } } else { LogDNSSEC("FinishDNSSECVerification: Could not validate the rrset %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); - dv->DVCallback(m, dv, DNSSEC_Insecure); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } } -mDNSexport void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) +mDNSexport void StartDNSSECVerification(mDNS *const m, void *context) { mDNSBool done; + DNSSECVerifier *dv = (DNSSECVerifier *)context; done = GetAllRRSetsForVerification(m, dv); if (done) @@ -2347,7 +2615,7 @@ mDNSexport void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) else debugdnssec("StartDNSSECVerification: all rdata sets not available for sig verification next %d", dv->next); } -mDNSlocal char *DNSSECStatusName(DNSSECStatus status) +mDNSexport char *DNSSECStatusName(DNSSECStatus status) { switch (status) { @@ -2361,7 +2629,7 @@ mDNSlocal char *DNSSECStatusName(DNSSECStatus status) // We could not use GenerateNegativeResponse as it assumes m->CurrentQuestion to be set. Even if // we change that, we needs to fix its callers and so on. It is much simpler to call the callback. -mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSECStatus status) +mDNSlocal void DeliverDNSSECStatus(mDNS *const m, DNSSECVerifier *dv, ResourceRecord *answer, DNSSECStatus status) { // Can't use m->CurrentQuestion as it may already be in use @@ -2369,6 +2637,12 @@ mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSEC LogMsg("DeliverDNSSECStatus: ERROR!! m->ValidationQuestion already set: %##s (%s)", m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype)); + BumpDNSSECStats(m, kStatsActionSet, kStatsTypeStatus, status); + BumpDNSSECStats(m, kStatsActionSet, kStatsTypeExtraPackets, dv->NumPackets); + mDNS_Lock(m); + BumpDNSSECStats(m, kStatsActionSet, kStatsTypeLatency, m->timenow - dv->StartTime); + mDNS_Unlock(m); + m->ValidationQuestion = m->Questions; while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions) { @@ -2389,9 +2663,16 @@ mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSEC { LogDNSSEC("DeliverDNSSECStatus: Generating dnssec status %s for %##s (%s)", DNSSECStatusName(status), q->qname.c, DNSTypeName(q->qtype)); - if (q->QuestionCallback) q->QuestionCallback(m, q, &largerec.r.resrec, QC_dnssec); + if (q->QuestionCallback) + { + if (q->DNSSECAuthInfo) + FreeDNSSECAuthChainInfo((AuthChain *)q->DNSSECAuthInfo); + q->DNSSECAuthInfo = AuthChainCopy(dv->ac); + q->DAIFreeCallback = FreeAuthChain; + q->QuestionCallback(m, q, &largerec.r.resrec, QC_dnssec); + } } - else + else if (FollowCNAME(q, answer, QC_add)) { LogDNSSEC("DeliverDNSSECStatus: Following CNAME dnssec status %s for %##s (%s)", DNSSECStatusName(status), q->qname.c, DNSTypeName(q->qtype)); @@ -2406,18 +2687,118 @@ mDNSlocal void DeliverDNSSECStatus(mDNS *const m, ResourceRecord *answer, DNSSEC m->ValidationQuestion = mDNSNULL; } -mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +// There is no work to be done if we could not validate DNSSEC (as the actual response for +// the query has already been delivered) except in the case of CNAMEs where we did not follow +// CNAMEs until we finished the DNSSEC processing. +mDNSlocal void DNSSECNoResponse(mDNS *const m, DNSSECVerifier *dv) { - RRVerifier *rrset; - RRVerifier *rv; CacheGroup *cg; CacheRecord *cr; mDNSu32 slot, namehash; + ResourceRecord *answer = mDNSNULL; + + LogDNSSEC("DNSSECNoResponse: called"); + + if (dv->ValidationRequired != DNSSEC_VALIDATION_SECURE_OPTIONAL) + { + LogMsg("DNSSECNoResponse: ERROR!! ValidationRequired incorrect %d", dv->ValidationRequired); + return; + } + + BumpDNSSECStats(m, kStatsActionSet, kStatsTypeStatus, DNSSEC_NoResponse); + + slot = HashSlot(&dv->origName); + namehash = DomainNameHashValue(&dv->origName); + + cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); + if (!cg) + { + LogDNSSEC("DNSSECNoResponse: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + goto done; + } + + InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); + + // We don't have to reset ValidatingResponse (unlike in DeliverDNSSECStatus) as there are no + // RRSIGs that can match the original question + for (cr = cg->members; cr; cr = cr->next) + { + if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) + { + answer = &cr->resrec; + break; + } + } + + // It is not an error for things to disappear underneath + if (!answer) + { + LogDNSSEC("DNSSECNoResponse: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); + goto done; + } + if (answer->rrtype == kDNSType_RRSIG) + { + LogDNSSEC("DNSSECNoResponse: RRSIG present for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); + goto done; + } + + // Can't use m->CurrentQuestion as it may already be in use + if (m->ValidationQuestion) + LogMsg("DNSSECNoResponse: ERROR!! m->ValidationQuestion already set: %##s (%s)", + m->ValidationQuestion->qname.c, DNSTypeName(m->ValidationQuestion->qtype)); + + m->ValidationQuestion = m->Questions; + while (m->ValidationQuestion && m->ValidationQuestion != m->NewQuestions) + { + DNSQuestion *q = m->ValidationQuestion; + + if (q->ValidatingResponse || !q->ValidationRequired || + (q->ValidationState != DNSSECValInProgress) || !ResourceRecordAnswersQuestion(answer, q)) + { + m->ValidationQuestion = q->next; + continue; + } + + // If we could not validate e.g., zone was not signed or bad delegation etc., + // disable validation. Ideally, for long outstanding questions, we should try again when + // we switch networks. But for now, keep it simple. + // + // Note: If we followed a CNAME with no dnssec protection, it is even more important that + // we disable validation as we don't want to deliver a "secure" dnssec response later e.g., + // it is possible that the CNAME is not secure but the address records are secure. In this + // case, we don't want to deliver the secure response later as we followed a CNAME that was + // not protected with DNSSEC. + + q->ValidationRequired = 0; + q->ValidationState = DNSSECValNotRequired; + + if (FollowCNAME(q, answer, QC_add)) + { + LogDNSSEC("DNSSECNoResponse: Following CNAME for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + mDNS_Lock(m); + AnswerQuestionByFollowingCNAME(m, q, answer); + mDNS_Unlock(m); + } + + if (m->ValidationQuestion == q) // If m->ValidationQuestion was not auto-advanced, do it ourselves now + m->ValidationQuestion = q->next; + } + m->ValidationQuestion = mDNSNULL; + +done: + FreeDNSSECVerifier(m, dv); +} + +mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status) +{ + RRVerifier *rrset; + RRVerifier *rv; + CacheRecord *cr; mDNSu16 rrtype, rrclass; CacheRecord *const lrr = &largerec.r; - ResourceRecord *answer = mDNSNULL; - LogDNSSEC("DNSSECPositiveValidationCB: called status %s", DNSSECStatusName(status)); + LogDNSSEC("DNSSECPositiveValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType)); // // 1. Check to see if the rrset that was validated is the same as in cache. If they are not same, @@ -2431,31 +2812,13 @@ mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS // // 3. If we are answering with CNAME, it is time to follow the CNAME if the response is secure - slot = HashSlot(&dv->origName); - namehash = DomainNameHashValue(&dv->origName); - - cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); - if (!cg) - { - LogDNSSEC("DNSSECPositiveValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); - goto done; - } - if (!dv->ac) + if (!dv->ac || status == DNSSEC_Insecure) { - // If we don't have the AuthChain, it means we could not validate the rrset. Locate the - // original question based on dv->origName, dv->origType. - InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); - // Need to be reset ValidatingResponse as we are looking for the cache record that would answer - // the original question - dv->q.ValidatingResponse = mDNSfalse; - for (cr = cg->members; cr; cr = cr->next) - { - if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) - { - answer = &cr->resrec; - break; - } - } + // For Insecure status, the auth chain contains information about the trust + // chain starting from the known trust anchor. The rrsets are not related to + // the origName like in Bogus or Secure. + if (!answer) + LogMsg("DNSSECPositiveValidationCB: ERROR: answer NULL"); } else { @@ -2528,66 +2891,55 @@ mDNSlocal void DNSSECPositiveValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS // It is not an error for things to disappear underneath if (!answer) { - LogDNSSEC("DNSSECPositiveValidationCB: answer NULL"); + LogDNSSEC("DNSSECPositiveValidationCB: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); goto done; } - DeliverDNSSECStatus(m, answer, status); + DeliverDNSSECStatus(m, dv, answer, status); SetTTLRRSet(m, dv, status); done: FreeDNSSECVerifier(m, dv); } -mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status) { RRVerifier *rv; - CacheGroup *cg; CacheRecord *cr; - mDNSu32 slot, namehash; mDNSu16 rrtype, rrclass; - ResourceRecord *answer = mDNSNULL; AuthChain *ac; - LogDNSSEC("DNSSECNegativeValidationCB: called %s", DNSSECStatusName(status)); + LogDNSSEC("DNSSECNegativeValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType)); + + if (dv->parent) + { + // When NSEC/NSEC3s validation is completed, it calls the parent's DVCallback with the + // parent DNSSECVerifier which is the original one that started the verification. It itself + // should not have a parent. If the NSEC/NSEC3 validation results in another NSEC/NSEC3 + // validation, it should chain up via the dv->parent all the way to the top. + LogMsg("DNSSECNegativeValidationCB: ERROR!! dv->parent is set for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + goto done; + } - // 1. Locate the negative cache record and check the cached NSEC records to see if it matches the - // NSECs that were valiated. If the cached NSECS changed while the validation was in progress, + // 1. Locate the negative cache record and check the cached NSEC/NSEC3 records to see if it matches the + // NSEC/NSEC3s that were valiated. If the cached NSEC/NSEC3s changed while the validation was in progress, // we ignore the validation results. // // 2. Walk the question list to find the matching question. The original question that started // the DNSSEC verification may or may not be there. As long as there is a matching question // and waiting for the response, deliver the response. // - slot = HashSlot(&dv->origName); - namehash = DomainNameHashValue(&dv->origName); - - cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); - if (!cg) + if (!dv->ac || status == DNSSEC_Insecure) { - LogDNSSEC("DNSSECNegativeValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); - goto done; + // For Insecure status, the auth chain contains information about the trust + // chain starting from the known trust anchor. The rrsets are not related to + // the origName like in Bogus or Secure. + if (!answer) + LogMsg("DNSSECNegativeValidationCB: ERROR: answer NULL"); } - if (!dv->ac) + else { - // If we don't have the AuthChain, it means we could not validate the rrset. Locate the - // original question based on dv->origName, dv->origType. - InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); - // Need to be reset ValidatingResponse as we are looking for the cache record that would answer - // the original question - dv->q.ValidatingResponse = mDNSfalse; - for (cr = cg->members; cr; cr = cr->next) - { - if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) - { - answer = &cr->resrec; - break; - } - } - } - else - { - if (!dv->ac->rrset) + if (!dv->ac->rrset) { LogMsg("DNSSECNegativeValidationCB: ERROR!! Validated RRSET NULL"); goto done; @@ -2600,8 +2952,11 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS { for (rv = ac->rrset; rv; rv = rv->next) { - if (rv->rrtype == kDNSType_NSEC) + if (rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3) + { + LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) marking zero", rv, rv->name.c, DNSTypeName(rv->rrtype)); rv->found = 0; + } } } @@ -2615,19 +2970,19 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS for (ncr = cr->nsec; ncr; ncr = ncr->next) { // We have RRSIGs for the NSECs cached there too - if (ncr->resrec.rrtype != kDNSType_NSEC) + if (ncr->resrec.rrtype != kDNSType_NSEC && ncr->resrec.rrtype != kDNSType_NSEC3) continue; for (ac = dv->ac; ac; ac = ac->next) { for (rv = ac->rrset; rv; rv = rv->next) { - if (rv->rrtype == kDNSType_NSEC && rv->rdlength == ncr->resrec.rdlength && + if ((rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3) && rv->rdlength == ncr->resrec.rdlength && rv->rdatahash == ncr->resrec.rdatahash) { if (SameDomainName(ncr->resrec.name, &rv->name) && SameRDataBody(&ncr->resrec, (const RDataBody *)rv->rdata, SameDomainName)) { - LogDNSSEC("DNSSECNegativeValidationCB: setting found %s", CRDisplayString(m, ncr)); + LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) marking one", rv, rv->name.c, DNSTypeName(rv->rrtype)); answer = &cr->resrec; rv->found = 1; break; @@ -2651,12 +3006,12 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS { for (rv = ac->rrset; rv; rv = rv->next) { - if (rv->rrtype == kDNSType_NSEC) + if (rv->rrtype == kDNSType_NSEC || rv->rrtype == kDNSType_NSEC3) { if (!rv->found) { // We had more elements in the validated set, re-validate - LogDNSSEC("DNSSECNegativeValidationCB: Record %##s (%s) not found in the cache", rv->name.c, DNSTypeName(rv->rrtype)); + LogDNSSEC("DNSSECNegativeValidationCB: Record %p %##s (%s) not found in the cache", rv, rv->name.c, DNSTypeName(rv->rrtype)); goto done; } rv->found = 0; @@ -2668,26 +3023,83 @@ mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, DNS // It is not an error for things to disappear underneath if (!answer) { - LogDNSSEC("DNSSECNegativeValidationCB: answer NULL"); + LogDNSSEC("DNSSECNegativeValidationCB: answer NULL for %##s, %s", dv->origName.c, DNSTypeName(dv->origType)); goto done; } - DeliverDNSSECStatus(m, answer, status); + DeliverDNSSECStatus(m, dv, answer, status); SetTTLRRSet(m, dv, status); done: FreeDNSSECVerifier(m, dv); } +mDNSlocal void DNSSECValidationCB(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +{ + mDNSu32 slot, namehash; + CacheGroup *cg; + CacheRecord *cr; + + LogDNSSEC("DNSSECValidationCB: called %s for %##s (%s)", DNSSECStatusName(status), dv->origName.c, DNSTypeName(dv->origType)); + + // Currently, if we receive anything other than secure, we abort DNSSEC validation for + // the optional case. + if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL && status != DNSSEC_Secure) + { + DNSSECNoResponse(m, dv); + return; + } + + if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE && !dv->InsecureProofDone && status == DNSSEC_Bogus) + { + dv->InsecureProofDone = 1; + ProveInsecure(m, dv, mDNSNULL, mDNSNULL); + return; + } + slot = HashSlot(&dv->origName); + namehash = DomainNameHashValue(&dv->origName); + + cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, &dv->origName); + if (!cg) + { + LogDNSSEC("DNSSECValidationCB: cg NULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); + FreeDNSSECVerifier(m, dv); + return; + } + InitializeQuestion(m, &dv->q, dv->InterfaceID, &dv->origName, dv->origType, mDNSNULL, mDNSNULL); + // Need to be reset ValidatingResponse as we are looking for the cache record that would answer + // the original question + dv->q.ValidatingResponse = mDNSfalse; + for (cr = cg->members; cr; cr = cr->next) + { + if (SameNameRecordAnswersQuestion(&cr->resrec, &dv->q)) + { + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) + DNSSECNegativeValidationCB(m, dv, cg, &cr->resrec, status); + else + DNSSECPositiveValidationCB(m, dv, cg, &cr->resrec, status); + return; + } + } +} + mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) { mDNSu32 slot = HashSlot(&q->qname); CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); CacheRecord *rr; + mDNSBool first = mDNSfalse; + static mDNSBool TrustAnchorsUpdated = mDNSfalse; LogDNSSEC("VerifySignature called for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (!TrustAnchorsUpdated) + { + TrustAnchorsUpdated = mDNStrue; + UpdateTrustAnchors(m); + } if (!dv) { + first = mDNStrue; if (!q->qDNSServer || q->qDNSServer->cellIntf) { LogDNSSEC("VerifySignature: Disabled"); @@ -2695,8 +3107,12 @@ mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion * } // We assume that the verifier's question has been initialized here so that ValidateWithNSECS below // knows what it has prove the non-existence of. - dv = AllocateDNSSECVerifier(m, &q->qname, q->qtype, q->InterfaceID, DNSSECPositiveValidationCB, VerifySigCallback); - if (!dv) { LogMsg("VerifySignature: ERROR!! memory alloc failed"); return; } + dv = AllocateDNSSECVerifier(m, &q->qname, q->qtype, q->InterfaceID, q->ValidationRequired, DNSSECValidationCB, VerifySigCallback); + if (!dv) + { + LogMsg("VerifySignature: ERROR!! memory alloc failed"); + return; + } } // If we find a CNAME response to the question, remember what qtype @@ -2732,13 +3148,12 @@ mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion * // questions. Later as part of validating the response, we might get a NSEC response. if (rr->resrec.RecordType == kDNSRecordTypePacketNegative && DNSSECQuestion(q)) { - dv->DVCallback = DNSSECNegativeValidationCB; // If we can't find the NSEC, we can't validate. This can happens if we are // behind a non-DNSSEC aware CPE/server. if (!rr->nsec) { LogDNSSEC("VerifySignature: No nsecs found for %s", CRDisplayString(m, rr)); - dv->DVCallback(m, dv, DNSSEC_Insecure); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } ValidateWithNSECS(m, dv, rr); @@ -2747,21 +3162,29 @@ mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion * if (AddRRSetToVerifier(dv, &rr->resrec, mDNSNULL, RRVS_rr) != mStatus_NoError) { - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } } if (!dv->rrset) { LogMsg("VerifySignature: rrset mDNSNULL for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } dv->next = RRVS_rrsig; - StartDNSSECVerification(m, dv); + // Delay this so that the mDNS "core" can deliver all the results before + // we can deliver the dnssec result + if (first) + { + mDNSPlatformDispatchAsync(m, dv, StartDNSSECVerification); + } + else + { + StartDNSSECVerification(m, dv); + } } - mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv) { rdataRRSig *rrsig; @@ -2879,7 +3302,7 @@ mDNSlocal mStatus TrustedKey(mDNS *const m, DNSSECVerifier *dv) digestLen = ta->digestLen; AlgAdd(dv->ctx, name.c, DomainNameLength(&name)); - AlgAdd(dv->ctx, key, keyv->rdlength); + AlgAdd(dv->ctx, (const mDNSu8 *)key, keyv->rdlength); algRet = AlgVerify(dv->ctx, mDNSNULL, 0, digest, digestLen); AlgDestroy(dv->ctx); @@ -2931,16 +3354,40 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res debugdnssec("VerifySigCallback: AddRecord %d, dv %p", AddRecord, dv); - if (!AddRecord) return; + if (!AddRecord) + return; - LogDNSSEC("VerifySigCallback: Called with record %s", RRDisplayString(m, answer)); + // After the first ADD event, we should ideally stop the question. If we don't stop + // the question, we might get more callbacks and that can cause problems. For example, + // in the first callback, we could start a insecure proof and while that is in progress, + // if we get more callbacks, we will try to start another insecure proof. As we already + // started an insecure proof, we won't start another but terminate the verification + // process where we free the current DNSSECVerifier while the first insecure proof is + // still referencing it. + // + // But there are cases below which might return if we have not received the right answer + // yet e.g., no RRSIGs. In that case if the question is stopped, we will never get any + // callbacks again and also we leak "dv". Hence it is important that we either process + // the result or wait for more results. Note that the question eventually times out + // and cleans up the "dv" i.e., we don't wait forever. + + if (!answer) + { + LogDNSSEC("VerifySigCallback: Question %##s (%s) no dnssec response", question->qname.c, DNSTypeName(question->qtype)); + mDNS_StopQuery(m, question); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return; + } + LogDNSSEC("VerifySigCallback(%p): Called with record %s for question %##s (%s)", dv, RRDisplayString(m, answer), question->qname.c, + DNSTypeName(question->qtype)); mDNS_Lock(m); if ((m->timenow - question->StopTime) >= 0) { mDNS_Unlock(m); LogDNSSEC("VerifySigCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + mDNS_StopQuery(m, question); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } mDNS_Unlock(m); @@ -2950,14 +3397,15 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res CacheRecord *cr; LogDNSSEC("VerifySigCallback: Received a negative answer with record %s, AddRecord %d", RRDisplayString(m, answer), AddRecord); + mDNS_StopQuery(m, question); cr = NegativeCacheRecordForRR(m, answer); if (cr && cr->nsec) { - dv->DVCallback = DNSSECNegativeValidationCB; ValidateWithNSECS(m, dv, cr); } else { + LogDNSSEC("VerifySigCallback: Missing record (%s) Negative Cache Record %p", RRDisplayString(m, answer), cr); dv->DVCallback(m, dv, DNSSEC_Bogus); } @@ -2967,7 +3415,8 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res if (!dv->rrset) { LogMsg("VerifySigCallback: ERROR!! rrset NULL"); - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + mDNS_StopQuery(m, question); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } @@ -3018,6 +3467,7 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res LogDNSSEC("VerifySigCallback: RRVS_rrsig called with %s", RRDisplayString(m, answer)); return; } + mDNS_StopQuery(m, question); if (CheckRRSIGForRRSet(m, dv, &negcr) != mStatus_NoError) { LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c, @@ -3042,11 +3492,12 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res question->qtype); return; } + mDNS_StopQuery(m, question); if (CheckKeyForRRSIG(m, dv, &negcr) != mStatus_NoError) { LogDNSSEC("VerifySigCallback: Unable to find DNSKEY for %##s (%s), question %##s", dv->rrset->name.c, DNSTypeName(dv->rrset->rrtype), question->qname.c); - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } break; @@ -3068,6 +3519,7 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res question->qtype); return; } + mDNS_StopQuery(m, question); if (CheckRRSIGForKey(m, dv, &negcr) != mStatus_NoError) { LogDNSSEC("VerifySigCallback: Unable to find RRSIG for %##s (%s), question %##s", dv->rrset->name.c, @@ -3085,6 +3537,7 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res { LogDNSSEC("VerifySigCallback: RRVS_ds rrset type %s, %##s received before DS", DNSTypeName(rrtype), question->qname.c); } + mDNS_StopQuery(m, question); // It is not an error if we don't find the DS record as we could have // a trusted key. Or this is not a secure delegation which will be handled // below. @@ -3105,13 +3558,13 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res dv->DVCallback(m, dv, DNSSEC_Bogus); return; } - dv->DVCallback = DNSSECNegativeValidationCB; ValidateWithNSECS(m, dv, negcr); return; } break; default: LogDNSSEC("VerifySigCallback: ERROR!! default case rrset %##s question %##s", dv->rrset->name.c, question->qname.c); + mDNS_StopQuery(m, question); dv->DVCallback(m, dv, DNSSEC_Bogus); return; } @@ -3133,3 +3586,526 @@ mDNSlocal void VerifySigCallback(mDNS *const m, DNSQuestion *question, const Res } FinishDNSSECVerification(m, dv); } + +mDNSlocal TrustAnchor *FindTrustAnchor(mDNS *const m, const domainname *const name) +{ + TrustAnchor *ta; + TrustAnchor *matchTA = mDNSNULL; + TrustAnchor *rootTA = mDNSNULL; + int currmatch = 0; + int match; + mDNSu32 currTime = mDNSPlatformUTC(); + + for (ta = m->TrustAnchors; ta; ta = ta->next) + { + if (DNS_SERIAL_LT(ta->validUntil, currTime)) + { + LogDNSSEC("FindTrustAnchor: Expired: currentTime %d, ExpireTime %d", (int)currTime, ta->validUntil); + continue; + } + if (DNS_SERIAL_LT(currTime, ta->validFrom)) + { + LogDNSSEC("FindTrustAnchor: Future: currentTime %d, InceptTime %d", (int)currTime, ta->validFrom); + continue; + } + + if (SameDomainName((const domainname *)"\000", &ta->zone)) + rootTA = ta; + + match = CountLabelsMatch(&ta->zone, name); + if (match > currmatch) + { + currmatch = match; + matchTA = ta; + } + } + if (matchTA) + { + LogDNSSEC("FindTrustAnhcor: matched %##s", matchTA->zone.c); + return matchTA; + } + else if (rootTA) + { + LogDNSSEC("FindTrustAnhcor: matched rootTA %##s", rootTA->zone.c); + return rootTA; + } + else + { + LogDNSSEC("FindTrustAnhcor: No Trust Anchor"); + return mDNSNULL; + } +} + +mDNSlocal void DeliverInsecureProofResultAsync(mDNS *const m, void *context) +{ + InsecureContext *ic = (InsecureContext *)context; + ic->dv->DVCallback(m, ic->dv, ic->status); + if (ic->q.ThisQInterval != -1) + { + LogMsg("DeliverInsecureProofResultAsync: ERROR!! Question %##s (%s) not stopped already", ic->q.qname.c, DNSTypeName(ic->q.qtype)); + mDNS_StopQuery(m, &ic->q); + } + mDNSPlatformMemFree(ic); +} + +mDNSlocal void DeliverInsecureProofResult(mDNS *const m, InsecureContext *ic, DNSSECStatus status) +{ + // If the status is Bogus, restore the original auth chain before the insecure + // proof. + if (status == DNSSEC_Bogus) + { + LogDNSSEC("DeliverInsecureProofResult: Restoring the auth chain"); + if (ic->dv->ac) + { + FreeDNSSECAuthChainInfo(ic->dv->ac); + } + ResetAuthChain(ic->dv); + ic->dv->ac = ic->dv->saveac; + if (ic->dv->ac) + { + AuthChain *tmp = ic->dv->ac; + AuthChain **tail = &tmp->next; + while (tmp->next) + { + tail = &tmp->next; + tmp = tmp->next; + } + ic->dv->actail = tail; + } + ic->dv->saveac = mDNSNULL; + } + else if (ic->dv->saveac) + { + FreeDNSSECAuthChainInfo(ic->dv->saveac); + ic->dv->saveac = mDNSNULL; + } + ic->status = status; + // Stop the question before we schedule the block so that we don't receive additional + // callbacks again. Once the block runs, it will free the "ic" and you can't + // have another block queued up. This can happen if we receive a callback after we + // queue the block below. + if (ic->q.ThisQInterval != -1) + mDNS_StopQuery(m, &ic->q); + mDNSPlatformDispatchAsync(m, ic, DeliverInsecureProofResultAsync); +} + +mDNSlocal mDNSBool AlgorithmSupported(rdataDS *ds) +{ + switch(ds->digestType) + { + case SHA1_DIGEST_TYPE: + case SHA256_DIGEST_TYPE: + break; + default: + LogDNSSEC("AlgorithmSupported: Unsupported digest %d", ds->digestType); + return mDNSfalse; + } + + switch(ds->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + case CRYPTO_RSA_SHA256: + case CRYPTO_RSA_SHA512: + return mDNStrue; + default: + LogDNSSEC("AlgorithmSupported: Unsupported algorithm %d", ds->alg); + return mDNSfalse; + } +} + +// Note: This function is called when DNSSEC results are delivered (from DeliverDNSSECStatus) and we can't deliver DNSSEC result +// again within this function as "m->ValidationQuestion" is already in use. Hence we should dispatch off the delivery of insecure +// results asynchronously. +// +// Insecure proof callback can deliver either insecure or bogus, but never secure result. +mDNSlocal void ProveInsecureCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + InsecureContext *ic = (InsecureContext *)question->QuestionContext; + DNSSECVerifier *pdv = ic->dv; + AuthChain *ac; + + (void) answer; + + if (!AddRecord) + return; + + mDNS_Lock(m); + if ((m->timenow - question->StopTime) >= 0) + { + mDNS_Unlock(m); + LogDNSSEC("ProveInsecureCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); + DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); + return; + } + mDNS_Unlock(m); + + // We only need to handle the actual DNSSEC results and the ones that are secure. Anything else results in + // bogus. + if (AddRecord != QC_dnssec) + { + LogDNSSEC("ProveInsecureCallback: Question %##s (%s), AddRecord %d, answer %s", question->qname.c, + DNSTypeName(question->qtype), AddRecord, RRDisplayString(m, answer)); + return; + } + + LogDNSSEC("ProveInsecureCallback: ic %p Question %##s (%s), DNSSEC status %s", ic, question->qname.c, DNSTypeName(question->qtype), + DNSSECStatusName(question->ValidationStatus)); + + // Insecure is delivered for NSEC3 OptOut + if (question->ValidationStatus != DNSSEC_Secure && question->ValidationStatus != DNSSEC_Insecure) + { + LogDNSSEC("ProveInsecureCallback: Question %##s (%s) returned DNSSEC status %s", question->qname.c, + DNSTypeName(question->qtype), DNSSECStatusName(question->ValidationStatus)); + goto done; + } + ac = (AuthChain *)question->DNSSECAuthInfo; + if (!ac) + { + LogDNSSEC("ProveInsecureCallback: ac NULL for question %##s, %s", question->qname.c, DNSTypeName(question->qtype)); + goto done; + } + if (!ac->rrset) + { + LogDNSSEC("ProveInsecureCallback: ac->rrset NULL for question %##s, %s", question->qname.c, DNSTypeName(question->qtype)); + goto done; + } + if (ac->rrset->rrtype != kDNSType_DS && ac->rrset->rrtype != kDNSType_NSEC && ac->rrset->rrtype != kDNSType_NSEC3) + { + LogDNSSEC("ProveInsecureCallback: ac->rrset->rrtype %##s (%s) not handled", ac->rrset->name.c, + DNSTypeName(ac->rrset->rrtype)); + goto done; + } + AuthChainLink(pdv, ac); + question->DNSSECAuthInfo = mDNSNULL; + if (ac->rrset->rrtype == kDNSType_DS) + { + rdataDS *ds = (rdataDS *)ac->rrset->rdata; + + // If the delegation is secure, but the underlying zone is signed with an unsupported + // algorithm, then we can't verify it. Deliver insecure in that case. + if (!AlgorithmSupported(ds)) + { + LogDNSSEC("ProveInsecureCallback: Unsupported algorithm %d or digest %d", ds->alg, ds->digestType); + DeliverInsecureProofResult(m, ic, DNSSEC_Insecure); + return; + } + + // If the delegation is secure and the name that we queried for is same as the original + // name that started the insecure proof, then something is not right. We started the + // insecure proof e.g., the zone is not signed, but we are able to validate a DS for + // the same name which implies that the zone is signed (whose algorithm we support) and + // we should not have started the insecurity proof in the first place. + if (SameDomainName(&question->qname, &pdv->origName)) + { + LogDNSSEC("ProveInsecureCallback: Insecure proof reached original name %##s, error", question->qname.c); + DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); + return; + } + + LogDNSSEC("ProveInsecureCallback: Trying one more level down"); + ProveInsecure(m, pdv, ic, mDNSNULL); + } + else if (ac->rrset->rrtype == kDNSType_NSEC || ac->rrset->rrtype == kDNSType_NSEC3) + { + CacheRecord *cr; + + if (ac->rrset->rrtype == kDNSType_NSEC) + cr = NSECRecordIsDelegation(m, &question->qname, question->qtype); + else + cr = NSEC3RecordIsDelegation(m, &question->qname, question->qtype); + if (cr) + { + LogDNSSEC("ProveInsecureCallback: Non-existence proved and %s is a delegation for %##s (%s)", CRDisplayString(m, cr), + question->qname.c, DNSTypeName(question->qtype)); + DeliverInsecureProofResult(m, ic, DNSSEC_Insecure); + return; + } + // Could be a ENT. Go one more level down to see whether it is a secure delegation or not. + if (!SameDomainName(&question->qname, &pdv->origName)) + { + LogDNSSEC("ProveInsecureCallback: Not a delegation %##s (%s), go one more level down", question->qname.c, DNSTypeName(question->qtype)); + ProveInsecure(m, pdv, ic, mDNSNULL); + } + else + { + // Secure denial of existence and the name matches the original query. This means we should have + // received an NSEC (if the type does not exist) or signed records (if the name and type exists) + // and verified it successfully instead of starting the insecure proof. This could happen e.g., + // Wildcard expanded answer received without NSEC/NSEC3s etc. Also, is it possible that the + // zone went from unsigned to signed in a short time ? For now, we return bogus. + LogDNSSEC("ProveInsecureCallback: Not a delegation %##s (%s), but reached original name", question->qname.c, + DNSTypeName(question->qtype)); + DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); + } + } + return; +done: + DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); +} + +// We return Insecure if we don't have a trust anchor or we have a trust anchor and +// can prove that the delegation is not secure (and hence can't establish the trust +// chain) or the delegation is possibly secure but we don't have the algorithm support +// to prove that. +mDNSexport void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger) +{ + TrustAnchor *ta; + domainname *sname; + + if (ic == mDNSNULL) + { + ic = (InsecureContext *)mDNSPlatformMemAllocate(sizeof(InsecureContext)); + if (!ic) + { + LogMsg("mDNSPlatformMemAllocate: ERROR!! memory alloc failed for ic"); + return; + } + + // Save the AuthInfo while we are proving insecure. We don't want to mix up + // the auth chain for Bogus and Insecure. If we prove it to be insecure, we + // will add the chain corresponding to the insecure proof. Otherwise, we will + // restore this chain. + if (dv->ac) + { + if (!dv->saveac) + { + LogDNSSEC("ProveInsecure: saving authinfo"); + } + else + { + LogDNSSEC("ProveInsecure: ERROR!! authinfo already set"); + FreeDNSSECAuthChainInfo(dv->saveac); + } + dv->saveac = dv->ac; + ResetAuthChain(dv); + } + ic->dv = dv; + ic->q.ThisQInterval = -1; + + if (trigger) + { + LogDNSSEC("ProveInsecure: Setting Trigger %##s", trigger->c); + ic->triggerLabelCount = CountLabels(trigger); + } + else + { + LogDNSSEC("ProveInsecure: No Trigger"); + ic->triggerLabelCount = CountLabels(&dv->origName); + } + + ta = FindTrustAnchor(m, &dv->origName); + if (!ta) + { + LogDNSSEC("ProveInsecure: TrustAnchor NULL"); + DeliverInsecureProofResult(m, ic, DNSSEC_Insecure); + return; + } + // We want to skip the labels that is already matched by the trust anchor so + // that the first query starts just below the trust anchor + ic->skip = CountLabels(&dv->origName) - CountLabels(&ta->zone); + if (!ic->skip) + { + LogDNSSEC("ProveInsecure: origName %##s, skip is zero", dv->origName.c); + DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); + return; + } + } + // Look for the DS record starting just below the trust anchor. + // + // 1. If we find an NSEC record, then see if it is a delegation. If it is, then + // we are done. Otherwise, go down one more level. + // + // 2. If we find a DS record and no algorithm support, return "insecure". Otherwise, go + // down one more level. + // + sname = (domainname *)SkipLeadingLabels(&dv->origName, (ic->skip ? ic->skip - 1 : 0)); + if (!sname) + { + LogDNSSEC("ProveInsecure: sname NULL, origName %##s, skip %d", dv->origName.c, ic->skip); + DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); + return; + } + + // Insecurity proof is started during the normal bottom-up validation when we have a break in the trust + // chain e.g., we get NSEC/NSEC3s when looking up a DS record. Insecurity proof is top-down looking + // for a break in the trust chain. If we have already tried the validation (before the insecurity + // proof started) for this "sname", then don't bother with the proof. This happens sometimes, when + // we can't prove whether a zone is insecurely delegated or not. For example, if we are looking up + // host1.secure-nods.secure.example and when we encounter secure-nods, there is no DS record in the + // parent. We start the insecurity proof remembering that "secure-nods.secure.example" is the trigger + // point. As part of the proof we reach "secure-nods.secure.example". Even though secure.example + // prove that the name "secure-nods.secure.example/DS" does not exist, it can't prove that it is a + // delegation. So, we continue one more level down to host1.secure-nods.secure.example and we + // realize that we already tried the validation and hence abort here. + + if (CountLabels(sname) > ic->triggerLabelCount) + { + LogDNSSEC("ProveInsecure: Beyond the trigger current name %##s, origName %##s", sname->c, dv->origName.c); + DeliverInsecureProofResult(m, ic, DNSSEC_Bogus); + return; + } + + LogDNSSEC("ProveInsecure: OrigName %##s (%s), Current %##s", dv->origName.c, DNSTypeName(dv->origType), sname->c); + ic->skip--; + InitializeQuestion(m, &ic->q, dv->InterfaceID, sname, kDNSType_DS, ProveInsecureCallback, ic); + ic->q.ValidationRequired = DNSSEC_VALIDATION_INSECURE; + ic->q.ValidatingResponse = 0; + ic->q.DNSSECAuthInfo = mDNSNULL; + mDNS_StartQuery(m, &ic->q); +} + +mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) +{ + switch (type) + { + case kStatsTypeMemoryUsage: + if (action == kStatsActionIncrement) + { + m->DNSSECStats.TotalMemUsed += value; + } + else if (action == kStatsActionDecrement) + { + m->DNSSECStats.TotalMemUsed -= value; + } + break; + case kStatsTypeLatency: + if (action == kStatsActionSet) + { + if (value <= 4) + { + m->DNSSECStats.Latency0++; + } + else if (value <= 9) + { + m->DNSSECStats.Latency5++; + } + else if (value <= 19) + { + m->DNSSECStats.Latency10++; + } + else if (value <= 49) + { + m->DNSSECStats.Latency20++; + } + else if (value <= 99) + { + m->DNSSECStats.Latency50++; + } + else + { + m->DNSSECStats.Latency100++; + } + } + break; + case kStatsTypeExtraPackets: + if (action == kStatsActionSet) + { + if (value <= 2) + { + m->DNSSECStats.ExtraPackets0++; + } + else if (value <= 6) + { + m->DNSSECStats.ExtraPackets3++; + } + else if (value <= 9) + { + m->DNSSECStats.ExtraPackets7++; + } + else + { + m->DNSSECStats.ExtraPackets10++; + } + } + break; + case kStatsTypeStatus: + if (action == kStatsActionSet) + { + switch(value) + { + case DNSSEC_Secure: + m->DNSSECStats.SecureStatus++; + break; + case DNSSEC_Insecure: + m->DNSSECStats.InsecureStatus++; + break; + case DNSSEC_Indeterminate: + m->DNSSECStats.IndeterminateStatus++; + break; + case DNSSEC_Bogus: + m->DNSSECStats.BogusStatus++; + break; + case DNSSEC_NoResponse: + m->DNSSECStats.NoResponseStatus++; + break; + default: + LogMsg("BumpDNSSECStats: unknown status %d", value); + } + } + break; + case kStatsTypeMsgSize: + if (action == kStatsActionSet) + { + if (value <= 1024) + { + m->DNSSECStats.MsgSize0++; + } + else if (value <= 2048) + { + m->DNSSECStats.MsgSize1++; + } + else + { + m->DNSSECStats.MsgSize2++; + } + } + break; + case kStatsTypeProbe: + if (action == kStatsActionIncrement) + { + m->DNSSECStats.NumProbesSent += value; + } + break; + default: + LogMsg("BumpDNSSECStats: unknown type %d", type); + } + return; +} + +#else // !DNSSEC_DISABLED + +mDNSexport void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q) +{ + (void)m; + (void)dv; + (void)q; +} + +mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) +{ + (void)m; + (void)action; + (void)type; + (void)value; +} + +mDNSexport void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, mDNSu16 qtype, mDNSQuestionCallback *callback, void *context) +{ + (void) m; + (void) question; + (void) InterfaceID; + (void) qname; + (void) qtype; + (void) callback; + (void) context; +} + +mDNSexport char *DNSSECStatusName(DNSSECStatus status) +{ + (void) status; + + return mDNSNULL; +} + +#endif // !DNSSEC_DISABLED diff --git a/mDNSCore/dnssec.h b/mDNSCore/dnssec.h index 91aabeb..1a8d953 100644 --- a/mDNSCore/dnssec.h +++ b/mDNSCore/dnssec.h @@ -28,6 +28,7 @@ typedef enum typedef struct RRVerifier_struct RRVerifier; typedef struct DNSSECVerifier_struct DNSSECVerifier; typedef struct AuthChain_struct AuthChain; +typedef struct InsecureContext_struct InsecureContext; struct RRVerifier_struct { @@ -53,6 +54,11 @@ struct AuthChain_struct RRVerifier *key; // Public key for that RRSET }; +#define ResetAuthChain(dv) { \ + (dv)->ac = mDNSNULL; \ + (dv)->actail = &((dv)->ac); \ +} + typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); // // When we do a validation for a question, there might be additional validations that needs to be done e.g., @@ -80,46 +86,72 @@ typedef void DNSSECVerifierCallback (mDNS *const m, DNSSECVerifier *dv, DNSSECSt #define WILDCARD_PROVES_NONAME_EXISTS 0x00000002 #define NSEC_PROVES_NOTYPE_EXISTS 0x00000004 #define NSEC_PROVES_NONAME_EXISTS 0x00000008 +#define NSEC3_OPT_OUT 0x00000010 // OptOut was set in NSEC3 struct DNSSECVerifier_struct { - domainname origName; // Original question name that needs verification - mDNSu16 origType; // Original question type corresponding to origName - mDNSu16 currQtype; // Current question type that is being verified - mDNSInterfaceID InterfaceID; // InterfaceID of the question + domainname origName; // Original question name that needs verification + mDNSu16 origType; // Original question type corresponding to origName + mDNSu16 currQtype; // Current question type that is being verified + mDNSInterfaceID InterfaceID; // InterfaceID of the question DNSQuestion q; - mDNSu8 recursed; // Number of times recursed during validation + mDNSu8 recursed; // Number of times recursed during validation + mDNSu8 ValidationRequired; // Copy of the question's ValidationRequired status + mDNSu8 InsecureProofDone; + mDNSu8 NumPackets; // Number of packets that we send on the wire for DNSSEC verification. + mDNSs32 StartTime; // Time the DNSSEC verification starts mDNSu32 flags; RRVerifierSet next; - domainname *wildcardName; // set if the answer is wildcard expanded + domainname *wildcardName; // set if the answer is wildcard expanded RRVerifier *pendingNSEC; DNSSECVerifierCallback *DVCallback; DNSSECVerifier *parent; - RRVerifier *rrset; // rrset for which we have to verify - RRVerifier *rrsig; // RRSIG for rrset - RRVerifier *key; // DNSKEY for rrset - RRVerifier *rrsigKey; // RRSIG for DNSKEY - RRVerifier *ds; // DS for DNSKEY set in parent zone + RRVerifier *rrset; // rrset for which we have to verify + RRVerifier *rrsig; // RRSIG for rrset + RRVerifier *key; // DNSKEY for rrset + RRVerifier *rrsigKey; // RRSIG for DNSKEY + RRVerifier *ds; // DS for DNSKEY set in parent zone + AuthChain *saveac; AuthChain *ac; AuthChain **actail; AlgContext *ctx; }; + +struct InsecureContext_struct +{ + DNSSECVerifier *dv; // dv for which we are doing the insecure proof + mDNSu8 skip; // labels to skip for forming the name from origName + DNSSECStatus status; // status to deliver when done + mDNSu8 triggerLabelCount; // Label count of the name that triggered the insecure proof + DNSQuestion q; +}; + #define LogDNSSEC LogOperation #define DNS_SERIAL_GT(a, b) ((int)((a) - (b)) > 0) #define DNS_SERIAL_LT(a, b) ((int)((a) - (b)) < 0) -extern void StartDNSSECVerification(mDNS *const m, DNSSECVerifier *dv); +extern void StartDNSSECVerification(mDNS *const m, void *context); extern RRVerifier* AllocateRRVerifier(const ResourceRecord *const rr, mStatus *status); extern mStatus AddRRSetToVerifier(DNSSECVerifier *dv, const ResourceRecord *const rr, RRVerifier *rv, RRVerifierSet set); extern void VerifySignature(mDNS *const m, DNSSECVerifier *dv, DNSQuestion *q); extern void FreeDNSSECVerifier(mDNS *const m, DNSSECVerifier *dv); extern DNSSECVerifier *AllocateDNSSECVerifier(mDNS *const m, const domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID, - DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback); + mDNSu8 ValidationRequired, DNSSECVerifierCallback dvcallback, mDNSQuestionCallback qcallback); extern void InitializeQuestion(mDNS *const m, DNSQuestion *question, mDNSInterfaceID InterfaceID, const domainname *qname, mDNSu16 qtype, mDNSQuestionCallback *callback, void *context); extern void ValidateRRSIG(DNSSECVerifier *dv, RRVerifierSet type, const ResourceRecord *const rr); extern void AuthChainLink(DNSSECVerifier *dv, AuthChain *ae); +extern mStatus DNSNameToLowerCase(domainname *d, domainname *result); +extern int DNSMemCmp(const mDNSu8 *const m1, const mDNSu8 *const m2, int len); +extern int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain); +extern void ProveInsecure(mDNS *const m, DNSSECVerifier *dv, InsecureContext *ic, domainname *trigger); +extern void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value); +extern char *DNSSECStatusName(DNSSECStatus status); + +// DNSSECProbe belongs in DNSSECSupport.h but then we don't want to expose yet another plaform specific dnssec file +// to other platforms where dnssec is not supported. +extern void DNSSECProbe(mDNS *const m); #endif // __DNSSEC_H diff --git a/mDNSCore/mDNS.c b/mDNSCore/mDNS.c index 4b333c9..54fa735 100755 --- a/mDNSCore/mDNS.c +++ b/mDNSCore/mDNS.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ #include "uDNS.h" // Defines entry points into unicast-specific routines #include "nsec.h" #include "dnssec.h" +#include "anonymous.h" // Disable certain benign warnings with Microsoft compilers #if (defined(_MSC_VER)) @@ -43,6 +44,8 @@ #pragma warning(disable:4706) #endif +#include "dns_sd.h" // for kDNSServiceFlags* definitions + #if APPLE_OSX_mDNSResponder #include @@ -66,21 +69,28 @@ mDNSlocal void RetrySPSRegistrations(mDNS *const m); mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password); mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); -mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q); -mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q); +mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q); +mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q); mDNSlocal void mDNS_SendKeepalives(mDNS *const m); -mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSu32 *seq, - mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win); +mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, + mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win); + +mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m); +mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m); +mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords); +mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, + const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records); -#define mDNS_KeepaliveRecord(rr) ((rr)->rrtype == kDNSType_NULL && SameDomainLabel(SecondLabel((rr)->name)->c, (mDNSu8 *)"\x0A_keepalive")) // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Program Constants #endif -#define NO_HINFO 1 +// To Turn OFF mDNS_Tracer set MDNS_TRACER to 0 or undef it +#define MDNS_TRACER 1 +#define NO_HINFO 1 // Any records bigger than this are considered 'large' records #define SmallRecordLimit 1024 @@ -88,6 +98,21 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA #define kMaxUpdateCredits 10 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6) +// define special NR_AnswerTo values +#define NR_AnswerMulticast (mDNSu8*)~0 +#define NR_AnswerUnicast (mDNSu8*)~1 + +// Defined to set the kDNSQClass_UnicastResponse bit in the first four query packets. +// else, it's just set it the first query. +#define mDNS_REQUEST_UNICAST_RESPONSE 0 + +// The code (see SendQueries() and BuildQuestion()) needs to have the +// RequestUnicast value set to a value one greater than the number of times you want the query +// sent with the "request unicast response" (QU) bit set. +#define SET_QU_IN_FIRST_QUERY 2 +#define SET_QU_IN_FIRST_FOUR_QUERIES 5 + + mDNSexport const char *const mDNS_DomainTypeNames[] = { "b._dns-sd._udp.", // Browse @@ -107,21 +132,19 @@ mDNSexport const char *const mDNS_DomainTypeNames[] = #pragma mark - General Utility Functions #endif -// If there is a authoritative LocalOnly record that answers questions of type A, AAAA and CNAME -// this returns true. Main use is to handle /etc/hosts records. -#define LORecordAnswersAddressType(rr) ((rr)->ARType == AuthRecordLocalOnly && \ +// Returns true if this is a unique, authoritative LocalOnly record that answers questions of type +// A, AAAA , CNAME, or PTR. The caller should answer the question with this record and not send out +// the question on the wire if LocalOnlyRecordAnswersQuestion() also returns true. +// Main use is to handle /etc/hosts records and the LocalOnly PTR records created for localhost. +#define UniqueLocalOnlyRecord(rr) ((rr)->ARType == AuthRecordLocalOnly && \ (rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \ ((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \ - (rr)->resrec.rrtype == kDNSType_CNAME)) - -#define FollowCNAME(q, rr, AddRecord) (AddRecord && (q)->qtype != kDNSType_CNAME && \ - (rr)->RecordType != kDNSRecordTypePacketNegative && \ - (rr)->rrtype == kDNSType_CNAME) + (rr)->resrec.rrtype == kDNSType_CNAME || \ + (rr)->resrec.rrtype == kDNSType_PTR)) mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q) { - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); #if ForceAlerts if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; @@ -133,8 +156,7 @@ mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q) mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q) { - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); #if ForceAlerts if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0; @@ -330,7 +352,7 @@ mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slo return(CacheGroupForName(m, slot, rr->namehash, rr->name)); } -mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr) +mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself) { NetworkInterfaceInfo *intf; @@ -341,19 +363,44 @@ mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterface for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0) + { + if (myself) + { + if (mDNSSameIPv4Address(intf->ip.ip.v4, addr->ip.v4)) + *myself = mDNStrue; + else + *myself = mDNSfalse; + if (*myself) + debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning true", addr); + else + debugf("mDNS_AddressIsLocalSubnet: IPv4 %#a returning false", addr); + } return(mDNStrue); + } } if (addr->type == mDNSAddrType_IPv6) { - if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue); for (intf = m->HostInterfaces; intf; intf = intf->next) if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx) if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) && (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) && (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) && (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0)) - return(mDNStrue); + { + if (myself) + { + if (mDNSSameIPv6Address(intf->ip.ip.v6, addr->ip.v6)) + *myself = mDNStrue; + else + *myself = mDNSfalse; + if (*myself) + debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning true", addr); + else + debugf("mDNS_AddressIsLocalSubnet: IPv6 %#a returning false", addr); + } + return(mDNStrue); + } } return(mDNSfalse); @@ -366,6 +413,27 @@ mDNSlocal NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInt return(intf); } +mDNSlocal NetworkInterfaceInfo *FirstIPv4LLInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID) +{ + NetworkInterfaceInfo *intf; + + if (!InterfaceID) + return mDNSNULL; + + // Note: We don't check for InterfaceActive, as the active interface could be IPv6 and + // we still want to find the first IPv4 Link-Local interface + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->InterfaceID == InterfaceID && + intf->ip.type == mDNSAddrType_IPv4 && mDNSv4AddressIsLinkLocal(&intf->ip.ip.v4)) + { + debugf("FirstIPv4LLInterfaceForID: found LL interface with address %.4a", &intf->ip.ip.v4); + return intf; + } + } + return (mDNSNULL); +} + mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); @@ -373,7 +441,7 @@ mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID Interfa } // Caller should hold the lock -mDNSlocal void GenerateNegativeResponse(mDNS *const m) +mDNSlocal void GenerateNegativeResponse(mDNS *const m, QC_result qc) { DNSQuestion *q; if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; } @@ -388,7 +456,7 @@ mDNSlocal void GenerateNegativeResponse(mDNS *const m) // // The question may not have set Intermediates in which case we don't deliver negative responses. So, to force // through we use "QC_forceresponse". - AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_forceresponse); + AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, qc); if (m->CurrentQuestion == q) { q->ThisQInterval = 0; } // Deactivate this question // Don't touch the question after this m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it @@ -403,6 +471,22 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re else { const mDNSu32 c = q->CNAMEReferrals + 1; // Stash a copy of the new q->CNAMEReferrals value + UDPSocket *sock = q->LocalSocket; + mDNSOpaque16 id = q->TargetQID; + + // if there is a message waiting at the socket, we want to process that instead + // of throwing it away. If we have a CNAME response that answers + // both A and AAAA question and while answering it we don't want to throw + // away the response where the actual addresses are present. + if (mDNSPlatformPeekUDP(m, q->LocalSocket)) + { + LogInfo("AnswerQuestionByFollowingCNAME: Preserving UDP socket for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->LocalSocket = mDNSNULL; + } + else + { + sock = mDNSNULL; + } // The SameDomainName check above is to ignore bogus CNAME records that point right back at // themselves. Without that check we can get into a case where we have two duplicate questions, @@ -438,6 +522,14 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re // Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal, // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero q->CNAMEReferrals = c; + if (sock) + { + // We have a message waiting and that should answer this question. + if (q->LocalSocket) + mDNSPlatformUDPClose(q->LocalSocket); + q->LocalSocket = sock; + q->TargetQID = id; + } } } @@ -470,20 +562,13 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord if (q->QuestionCallback && !q->NoAnswer) { q->CurrentAnswers += AddRecord ? 1 : -1; - if (LORecordAnswersAddressType(rr)) + if (UniqueLocalOnlyRecord(rr)) { if (!followcname || q->ReturnIntermed) { // Don't send this packet on the wire as we answered from /etc/hosts q->ThisQInterval = 0; q->LOAddressAnswers += AddRecord ? 1 : -1; - // We can't possibly validate the entries in /etc/hosts. Hence, we - // report it as insecure. - if (q->ValidationRequired) - { - q->ValidationStatus = DNSSEC_Insecure; - q->ValidationState = DNSSECValDone; - } q->QuestionCallback(m, q, &rr->resrec, AddRecord); } mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again @@ -495,11 +580,6 @@ mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord } else { - if (q->ValidationRequired) - { - q->ValidationStatus = DNSSEC_Insecure; - q->ValidationState = DNSSECValDone; - } q->QuestionCallback(m, q, &rr->resrec, AddRecord); } } @@ -589,13 +669,17 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define DefaultProbeCountForTypeUnique ((mDNSu8)3) #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0) -#define InitialAnnounceCount ((mDNSu8)8) +// See RFC 6762: "8.3 Announcing" +// "The Multicast DNS responder MUST send at least two unsolicited responses, one second apart." +// Send 4, which is really 8 since we send on both IPv4 and IPv6. +#define InitialAnnounceCount ((mDNSu8)4) // For goodbye packets we set the count to 3, and for wakeups we set it to 18 // (which will be up to 15 wakeup attempts over the course of 30 seconds, // and then if the machine fails to wake, 3 goodbye packets). #define GoodbyeCount ((mDNSu8)3) #define WakeupCount ((mDNSu8)18) +#define MAX_PROBE_RESTARTS ((mDNSu8)20) // Number of wakeups we send if WakeOnResolve is set in the question #define InitialWakeOnResolveCount ((mDNSu8)3) @@ -617,6 +701,18 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond) #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR)) +// Adjustment factor to avoid race condition (used for unicast cache entries) : +// Suppose real record has TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100. +// If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another +// 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox. +// To avoid this, we extend the record's effective TTL to give it a little extra grace period. +// We adjust the 100 second TTL to 127. This means that when we do our 80% query at 102 seconds, +// the cached copy at our local caching server will already have expired, so the server will be forced +// to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds. + +#define RRAdjustTTL(ttl) ((ttl) + ((ttl)/4) + 2) +#define RRUnadjustedTTL(ttl) ((((ttl) - 2) * 4) / 5) + #define MaxUnansweredQueries 4 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent @@ -784,16 +880,8 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) // and we can begin broadcasting our announcements to take over ownership of that IP address. // If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk // (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address. - if (rr->AddressProxy.type) rr->LastAPTime = m->timenow; - - // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, - // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited - // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. - // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage - // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. - if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) - if (rr->WakeUp.HMAC.l[0] && rr->resrec.rrtype == kDNSType_AAAA) - rr->LastAPTime = m->timenow - rr->ThisAPInterval + mDNSPlatformOneSecond * 10; + if (rr->AddressProxy.type) + rr->LastAPTime = m->timenow; // Set LastMCTime to now, to inhibit multicast responses // (no need to send additional multicast responses when we're announcing anyway) @@ -868,6 +956,7 @@ mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr) rr->AnnounceCount = InitialAnnounceCount; rr->RequireGoodbye = mDNSfalse; + rr->ProbeRestartCount = 0; InitializeLastAPTime(m, rr); } } @@ -934,6 +1023,7 @@ mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) rr->state = regState_Pending; } rr->ProbeCount = 0; + rr->ProbeRestartCount = 0; rr->AnnounceCount = 0; rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; rr->LastAPTime = m->timenow - rr->ThisAPInterval; @@ -1038,6 +1128,52 @@ mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) return (mDNSNULL); } + +mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) +{ + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + { + m->AutoTargetServices--; + LogInfo("DecrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); + if (!m->AutoTargetServices) + DeadvertiseAllInterfaceRecords(m); + } +} + +mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) +{ + if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) + { + int count = m->AutoTargetServices; + + // Bump up before calling AdvertiseAllInterfaceRecords. AdvertiseInterface + // returns without doing anything if the count is zero. + m->AutoTargetServices++; + LogInfo("IncrementAutoTargetServices: AutoService Record %s, AutoTargetServices %d", ARDisplayString(m, rr), m->AutoTargetServices); + if (!count) + AdvertiseAllInterfaceRecords(m); + } +} + +mDNSlocal void getKeepaliveRaddr(mDNS *const m, AuthRecord *rr, mDNSAddr *raddr) +{ + mDNSAddr laddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; + + if (mDNS_KeepaliveRecord(&rr->resrec)) + { + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, raddr, ð, &seq, &ack, &lport, &rport, &win); + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(raddr) || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport)) + { + LogMsg("getKeepaliveRaddr: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, raddr, rport.NotAnInteger); + return; + } + } +} + // Exported so uDNS.c can call this mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) { @@ -1152,6 +1288,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // Field Group 3: Transient state for Authoritative Records rr->Acknowledged = mDNSfalse; rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); + rr->ProbeRestartCount = 0; rr->AnnounceCount = InitialAnnounceCount; rr->RequireGoodbye = mDNSfalse; rr->AnsweredLocalQ = mDNSfalse; @@ -1274,6 +1411,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) *p = rr; if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified; rr->ProbeCount = 0; + rr->ProbeRestartCount = 0; rr->AnnounceCount = 0; if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr); return(mStatus_NoError); // <--- Note: For unicast records, code currently bails out at this point @@ -1284,6 +1422,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) if (RRLocalOnly(rr)) { rr->ProbeCount = 0; + rr->ProbeRestartCount = 0; rr->AnnounceCount = 0; r = CheckAuthIdenticalRecord(&m->rrauth, rr); } @@ -1331,8 +1470,25 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } } + // If this is a keepalive record, fetch the MAC address of the remote host. + // This is used by the in-NIC proxy to send the keepalive packets. + if (mDNS_KeepaliveRecord(&rr->resrec)) + { + // Set the record type to known unique to prevent probing keep alive records. + // Also make sure we do not announce the keepalive records. + rr->resrec.RecordType = kDNSRecordTypeKnownUnique; + rr->AnnounceCount = 0; + mDNSAddr raddr; + getKeepaliveRaddr(m, rr, &raddr); + // This is an asynchronous call. Once the remote MAC address is available, helper will schedule an + // asynchronous task to update the resource record + mDNSPlatformGetRemoteMacAddr(m, &raddr); + } + if (!AuthRecord_uDNS(rr)) // This check is superfluous, given that for unicast records we (currently) bail out above { + // We have inserted the record in the list. See if we have to advertise the A/AAAA,HINFO,PTR records. + IncrementAutoTargetServices(m, rr); // For records that are not going to probe, acknowledge them right away if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering) AcknowledgeRecord(m, rr); @@ -1434,6 +1590,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, } dup->resrec.RecordType = rr->resrec.RecordType; dup->ProbeCount = rr->ProbeCount; + dup->ProbeRestartCount = rr->ProbeRestartCount; dup->AnnounceCount = rr->AnnounceCount; dup->RequireGoodbye = rr->RequireGoodbye; dup->AnsweredLocalQ = rr->AnsweredLocalQ; @@ -1467,8 +1624,9 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, // deregister (unilink from the DuplicateRecords list), we will be recursing infinitely. Hence, // clear the HMAC which will cause it to deregister. See for // details. - rr->WakeUp.HMAC = zeroEthAddr; + rr->WakeUp.HMAC = zeroEthAddr; rr->RequireGoodbye = mDNSfalse; + rr->resrec.RecordType = kDNSRecordTypeDeregistering; dupList = mDNStrue; } if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", @@ -1572,6 +1730,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, { *p = rr->next; // Cut this record from the list if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next; + DecrementAutoTargetServices(m, rr); } // If someone is about to look at this, bump the pointer forward if (m->CurrentRecord == rr) m->CurrentRecord = rr->next; @@ -1697,12 +1856,21 @@ mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseR } } +mDNSlocal int AnonInfoSpace(AnonymousInfo *info) +{ + ResourceRecord *rr = info->nsec3RR; + + // 2 bytes for compressed name + type (2) class (2) TTL (4) rdlength (2) rdata (n) + return (2 + 10 + rr->rdlength); +} + mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID) { AuthRecord *rr; AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + int AnoninfoSpace = 0; // Make a list of all our records that need to be unicast to this destination for (rr = m->ResourceRecords; rr; rr=rr->next) @@ -1731,7 +1899,11 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d } if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse) // rr->NR_AnswerTo - { rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; } + { + rr->NR_AnswerTo = NR_AnswerMulticast; + *nrp = rr; + nrp = &rr->NextResponse; + } } } } @@ -1748,11 +1920,23 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d while (ResponseRecords && ResponseRecords->NR_AnswerTo) { rr = ResponseRecords; + if (rr->resrec.AnonInfo) + { + AnoninfoSpace += AnonInfoSpace(rr->resrec.AnonInfo); + rr->resrec.AnonInfo->SendNow = mDNSInterfaceMark; + } if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it - newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec); + + // Retract the limit by AnoninfoSpace which we need to put the AnoInfo option. + newptr = PutResourceRecordTTLWithLimit(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl, + m->omsg.data + (AllowedRRSpace(&m->omsg) - AnoninfoSpace)); + rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state - if (!newptr && m->omsg.h.numAnswers) break; // If packet full, send it now + if (!newptr && m->omsg.h.numAnswers) + { + break; // If packet full, send it now + } if (newptr) responseptr = newptr; ResponseRecords = rr->NextResponse; rr->NextResponse = mDNSNULL; @@ -1761,6 +1945,29 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d rr->RequireGoodbye = mDNStrue; } + // We have reserved the space for AnonInfo option. PutResourceRecord uses the + // standard limit (AllowedRRSpace) and we should have space now. + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == mDNSInterfaceMark) + { + ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR; + + newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAuthorities, nsec3RR); + if (newptr) + { + responseptr = newptr; + debugf("SendDelayedUnicastResponse: Added NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID); + } + else + { + // We allocated space and we should not fail. Don't break, we need to clear the SendNow flag. + LogMsg("SendDelayedUnicastResponse: ERROR!! Cannot Add NSEC3 Record %s on %p", RRDisplayString(m, nsec3RR), intf->InterfaceID); + } + rr->resrec.AnonInfo->SendNow = mDNSNULL; + } + } + // Add additionals, if there's space while (ResponseRecords && !ResponseRecords->NR_AnswerTo) { @@ -2041,6 +2248,15 @@ mDNSlocal void SendNDP(mDNS *const m, const mDNSu8 op, const mDNSu8 flags, const mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID); } +mDNSlocal void SetupTracerOpt(const mDNS *const m, rdataOPT *const Trace) +{ + Trace->u.tracer.platf = m->mDNS_plat; + Trace->u.tracer.mDNSv = _DNS_SD_H/1000; + + Trace->opt = kDNSOpt_Trace; + Trace->optlen = DNSOpt_TraceData_Space - 4; +} + mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *const intf, rdataOPT *const owner) { owner->u.owner.vers = 0; @@ -2061,6 +2277,36 @@ mDNSlocal void GrantUpdateCredit(AuthRecord *rr) else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval); } +mDNSlocal mDNSBool ShouldSendGoodbyesBeforeSleep(mDNS *const m, const NetworkInterfaceInfo *intf, AuthRecord *rr) +{ + // If there are no sleep proxies, we set the state to SleepState_Sleeping explicitly + // and hence there is no need to check for Transfering state. But if we have sleep + // proxies and partially sending goodbyes for some records, we will be in Transfering + // state and hence need to make sure that we send goodbyes in that case too. Checking whether + // we are not awake handles both cases. + if ((rr->AuthFlags & AuthFlagsWakeOnly) && (m->SleepState != SleepState_Awake)) + { + debugf("ShouldSendGoodbyesBeforeSleep: marking for goodbye", ARDisplayString(m, rr)); + return mDNStrue; + } + + if (m->SleepState != SleepState_Sleeping) + return mDNSfalse; + + // If we are going to sleep and in SleepState_Sleeping, SendGoodbyes on the interface tell you + // whether you can send goodbyes or not. + if (!intf->SendGoodbyes) + { + debugf("ShouldSendGoodbyesBeforeSleep: not sending goodbye %s, int %p", ARDisplayString(m, rr), intf->InterfaceID); + return mDNSfalse; + } + else + { + debugf("ShouldSendGoodbyesBeforeSleep: sending goodbye %s, int %p", ARDisplayString(m, rr), intf->InterfaceID); + return mDNStrue; + } +} + // Note about acceleration of announcements to facilitate automatic coalescing of // multiple independent threads of announcements into a single synchronized thread: // The announcements in the packet may be at different stages of maturity; @@ -2125,7 +2371,8 @@ mDNSlocal void SendResponses(mDNS *const m) LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password); for (r2 = rr; r2; r2=r2->next) - if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC)) + if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) && + !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC)) { // For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original // owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict. @@ -2145,20 +2392,23 @@ mDNSlocal void SendResponses(mDNS *const m) { if (rr->AddressProxy.type) { - rr->AnnounceCount--; - rr->ThisAPInterval *= 2; - rr->LastAPTime = m->timenow; - if (rr->AddressProxy.type == mDNSAddrType_IPv4) - { - LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", - rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); - SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr); - } - else if (rr->AddressProxy.type == mDNSAddrType_IPv6) + if (!mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC)) { - LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", - rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); - SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + rr->AnnounceCount--; + rr->ThisAPInterval *= 2; + rr->LastAPTime = m->timenow; + if (rr->AddressProxy.type == mDNSAddrType_IPv4) + { + LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr); + } + else if (rr->AddressProxy.type == mDNSAddrType_IPv6) + { + LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s", + rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr)); + SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + } } } else @@ -2270,10 +2520,12 @@ mDNSlocal void SendResponses(mDNS *const m) while (intf) { - const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + int TraceRecordSpace = (mDNS_McastTracingEnabled && MDNS_TRACER) ? DNSOpt_Header_Space + DNSOpt_TraceData_Space : 0; int numDereg = 0; int numAnnounce = 0; int numAnswer = 0; + int AnoninfoSpace = 0; mDNSu8 *responseptr = m->omsg.data; mDNSu8 *newptr; InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags); @@ -2298,8 +2550,7 @@ mDNSlocal void SendResponses(mDNS *const m) RData *OldRData = rr->resrec.rdata; mDNSu16 oldrdlength = rr->resrec.rdlength; mDNSu8 active = (mDNSu8) - (rr->resrec.RecordType != kDNSRecordTypeDeregistering && - (m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type)); + (rr->resrec.RecordType != kDNSRecordTypeDeregistering && !ShouldSendGoodbyesBeforeSleep(m, intf, rr)); newptr = mDNSNULL; if (rr->NewRData && active) { @@ -2313,6 +2564,17 @@ mDNSlocal void SendResponses(mDNS *const m) SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); } + if (rr->resrec.AnonInfo) + { + int tmp = AnonInfoSpace(rr->resrec.AnonInfo); + + AnoninfoSpace += tmp; + // Adjust OwnerRecordSpace/TraceRecordSpace which is used by PutRR_OS_TTL below so that + // we have space to put in the NSEC3 record in the authority section. + OwnerRecordSpace += tmp; + TraceRecordSpace += tmp; + } + if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0); @@ -2335,6 +2597,13 @@ mDNSlocal void SendResponses(mDNS *const m) if (newptr) // If succeeded in sending, advance to next interface { + if (rr->resrec.AnonInfo) + { + debugf("SendResponses: Marking %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, + TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); + rr->resrec.AnonInfo->SendNow = intf->InterfaceID; + } + // If sending on all interfaces, go to next interface; else we're finished now if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any) rr->SendRNow = GetNextActiveInterfaceID(intf); @@ -2344,6 +2613,31 @@ mDNSlocal void SendResponses(mDNS *const m) } } + // Get the reserved space back + OwnerRecordSpace -= AnoninfoSpace; + TraceRecordSpace -= AnoninfoSpace; + newptr = responseptr; + for (rr = m->ResourceRecords; rr; rr=rr->next) + { + if (rr->resrec.AnonInfo && rr->resrec.AnonInfo->SendNow == intf->InterfaceID) + { + ResourceRecord *nsec3RR = rr->resrec.AnonInfo->nsec3RR; + + newptr = PutRR_OS_TTL(newptr, &m->omsg.h.numAuthorities, nsec3RR, nsec3RR->rroriginalttl); + if (newptr) + { + responseptr = newptr; + debugf("SendResponses: Added NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, + TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); + } + else + { + LogMsg("SendResponses: Cannot add NSEC3 %s, OwnerRecordSpace %d, TraceRecordSpace %d, limit %p", ARDisplayString(m, rr), OwnerRecordSpace, + TraceRecordSpace, m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace - TraceRecordSpace); + } + rr->resrec.AnonInfo->SendNow = mDNSNULL; + } + } // Second Pass. Add additional records, if there's space. newptr = responseptr; for (rr = m->ResourceRecords; rr; rr=rr->next) @@ -2446,26 +2740,47 @@ mDNSlocal void SendResponses(mDNS *const m) if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals) { - // If we have data to send, add OWNER option if necessary, then send packet - - if (OwnerRecordSpace) + // If we have data to send, add OWNER/TRACER/OWNER+TRACER option if necessary, then send packet + if (OwnerRecordSpace || TraceRecordSpace) { AuthRecord opt; mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdlength = sizeof(rdataOPT); opt.resrec.rdestimate = sizeof(rdataOPT); - SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + if (OwnerRecordSpace && TraceRecordSpace) + { + opt.resrec.rdlength += sizeof(rdataOPT); // Two options in this OPT record + opt.resrec.rdestimate += sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[1]); + } + else if (OwnerRecordSpace) + { + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + } + else if (TraceRecordSpace) + { + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]); + } newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); - if (newptr) { responseptr = newptr; LogSPS("SendResponses put %s", ARDisplayString(m, &opt)); } + if (newptr) + { + responseptr = newptr; + LogInfo("SendResponses put %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt)); + } else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1) - LogSPS("SendResponses: No space in packet for Owner OPT record (%d/%d/%d/%d) %s", - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + { + LogInfo("SendResponses: No space in packet for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", + m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } else - LogMsg("SendResponses: How did we fail to have space for Owner OPT record (%d/%d/%d/%d) %s", + { + LogMsg("SendResponses: How did we fail to have space for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } } - + debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p", numDereg, numDereg == 1 ? "" : "s", numAnnounce, numAnnounce == 1 ? "" : "s", @@ -2576,16 +2891,15 @@ mDNSexport void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s", (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr)); } - ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr)); } #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5) #define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5) #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 5) -#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30) +#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 5) -mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) +mDNSexport mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval) { if (interval < kMinimumReconfirmTime) interval = kMinimumReconfirmTime; @@ -2609,8 +2923,6 @@ mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, return(mStatus_NoError); } -#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) - // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr. // It also appends to the list of known answer records that need to be included, // and updates the forcast for the size of the known answer section. @@ -2620,7 +2932,8 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData; - mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); + mDNSu8 anoninfo_space = q->AnonInfo ? AnonInfoSpace(q->AnonInfo) : 0; + mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast - anoninfo_space, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit)); if (!newptr) { debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -2628,7 +2941,7 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer } else { - mDNSu32 forecast = *answerforecast; + mDNSu32 forecast = *answerforecast + anoninfo_space; const mDNSu32 slot = HashSlot(&q->qname); const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); CacheRecord *rr; @@ -2657,9 +2970,9 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **quer // then undo that last question and try again next time if (query->h.numQuestions > 1 && newptr + forecast >= limit) { - debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d", - q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data); query->h.numQuestions--; + debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d, total questions %d", + q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data, query->h.numQuestions); ka = *kalistptrptr; // Go back to where we started and retract these answer records while (*ka) { CacheRecord *c = *ka; *ka = mDNSNULL; ka = &c->NextInKAList; } return(mDNSfalse); // Return false, so we'll try again in the next packet @@ -2729,8 +3042,10 @@ mDNSlocal const CacheRecord *CacheHasAddressTypeForName(mDNS *const m, const dom return(cr); } + mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *const q, const CacheRecord *const c0, const CacheRecord *const c1) { +#ifndef SPC_DISABLED CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname); const CacheRecord *cr, *bestcr = mDNSNULL; mDNSu32 bestmetric = 1000000; @@ -2744,6 +3059,14 @@ mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *c if (bestmetric > metric) { bestmetric = metric; bestcr = cr; } } return(bestcr); +#else // SPC_DISABLED + (void) m; + (void) q; + (void) c0; + (void) c1; + (void) c1; + return mDNSNULL; +#endif // SPC_DISABLED } mDNSlocal void CheckAndSwapSPS(const CacheRecord *sps1, const CacheRecord *sps2) @@ -2876,6 +3199,7 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) EthAddr[i - 1] = 0; mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i); IPAddr[len - i] = 0; + m->mDNSStats.WakeOnResolves++; mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount); return; } @@ -2964,6 +3288,7 @@ mDNSlocal void SendQueries(mDNS *const m) { q->LastQTime = m->timenow - q->ThisQInterval; cr->UnansweredQueries++; + m->mDNSStats.CacheRefreshQueries++; } else if (q->SendQNow == mDNSNULL) { @@ -2973,6 +3298,13 @@ mDNSlocal void SendQueries(mDNS *const m) { q->SendQNow = mDNSInterfaceMark; } + + // Indicate that this question was marked for sending + // to update an existing cached answer record. + // The browse throttling logic below uses this to determine + // if the query should be sent. + if (mDNSOpaque16IsZero(q->TargetQID)) + q->CachedAnswerNeedsUpdate = mDNStrue; } } } @@ -2998,7 +3330,7 @@ mDNSlocal void SendQueries(mDNS *const m) { InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags); qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass); - mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL, q->UseBrackgroundTrafficClass); + mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); q->ThisQInterval *= QuestionIntervalStep; } if (q->ThisQInterval > MaxQuestionInterval) @@ -3037,21 +3369,44 @@ mDNSlocal void SendQueries(mDNS *const m) m->NextScheduledQuery = m->timenow + 0x78000000; for (q = m->Questions; q && q != m->NewQuestions; q=q->next) { - if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow || - (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) + if (mDNSOpaque16IsZero(q->TargetQID) + && (q->SendQNow || (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))) { // If at least halfway to next query time, advance to next interval // If less than halfway to next query time, then // treat this as logically a repeat of the last transmission, without advancing the interval if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0) { - //LogInfo("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q)); - q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces + // If we have reached the answer threshold for this question, + // don't send it again until MaxQuestionInterval unless: + // one of its cached answers needs to be refreshed, + // or it's the initial query for a kDNSServiceFlagsThresholdFinder mode browse. + if (q->BrowseThreshold + && (q->CurrentAnswers >= q->BrowseThreshold) + && (q->CachedAnswerNeedsUpdate == mDNSfalse) + && !((q->flags & kDNSServiceFlagsThresholdFinder) && (q->ThisQInterval == InitialQuestionInterval))) + { + q->SendQNow = mDNSNULL; + q->ThisQInterval = MaxQuestionInterval; + q->LastQTime = m->timenow; + q->RequestUnicast = 0; + LogInfo("SendQueries: (%s) %##s reached threshold of %d answers", + DNSTypeName(q->qtype), q->qname.c, q->BrowseThreshold); + } + else + { + // Mark this question for sending on all interfaces + q->SendQNow = mDNSInterfaceMark; + q->ThisQInterval *= QuestionIntervalStep; + } + debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); - q->ThisQInterval *= QuestionIntervalStep; - if (q->ThisQInterval > MaxQuestionInterval) + + if (q->ThisQInterval >= QuestionIntervalThreshold) + { q->ThisQInterval = MaxQuestionInterval; + } else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast && !(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash))) { @@ -3176,7 +3531,8 @@ mDNSlocal void SendQueries(mDNS *const m) // go through our interface list sending the appropriate queries on each interface while (intf) { - const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0; + int TraceRecordSpace = (mDNS_McastTracingEnabled && MDNS_TRACER) ? DNSOpt_Header_Space + DNSOpt_TraceData_Space : 0; mDNSu8 *queryptr = m->omsg.data; mDNSBool useBackgroundTrafficClass = mDNSfalse; // set if we should use background traffic class @@ -3186,13 +3542,14 @@ mDNSlocal void SendQueries(mDNS *const m) { // Start a new known-answer list CacheRecord **kalistptr = &KnownAnswerList; - mDNSu32 answerforecast = OwnerRecordSpace; // We start by assuming we'll need at least enough space to put the Owner Option + mDNSu32 answerforecast = OwnerRecordSpace + TraceRecordSpace; // Start by assuming we'll need at least enough space to put the Owner+Tracer Option // Put query questions in this packet for (q = m->Questions; q && q != m->NewQuestions; q=q->next) { if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow == intf->InterfaceID)) { + mDNSBool Suppress = mDNSfalse; debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d", SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting ", q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data); @@ -3204,9 +3561,24 @@ mDNSlocal void SendQueries(mDNS *const m) q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); } // If we're suppressing this question, or we successfully put it, update its SendQNow state - else if (SuppressOnThisInterface(q->DupSuppress, intf) || + else if ((Suppress = SuppressOnThisInterface(q->DupSuppress, intf)) || BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast)) { + // We successfully added the question to the packet. Make sure that + // we also send the NSEC3 record if required. BuildQuestion accounted for + // the space. + // + // Note: We don't suppress anonymous questions and hence Suppress should always + // be zero. + + if (Suppress) + m->mDNSStats.DupQuerySuppressions++; + + if (!Suppress && q->AnonInfo) + { + debugf("SendQueries: marking for question %##s, Suppress %d", q->qname.c, Suppress); + q->AnonInfo->SendNow = intf->InterfaceID; + } q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); if (q->WakeOnResolveCount) { @@ -3215,7 +3587,7 @@ mDNSlocal void SendQueries(mDNS *const m) } // use brackground traffic class if any included question requires it - if (q->UseBrackgroundTrafficClass) + if (q->UseBackgroundTrafficClass) { useBackgroundTrafficClass = mDNStrue; } @@ -3250,8 +3622,8 @@ mDNSlocal void SendQueries(mDNS *const m) { CacheRecord *ka = KnownAnswerList; mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond; - mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, - &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace); + mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers, &ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, + m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace - TraceRecordSpace); if (newptr) { verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", @@ -3272,6 +3644,7 @@ mDNSlocal void SendQueries(mDNS *const m) } for (ar = m->ResourceRecords; ar; ar=ar->next) + { if (ar->IncludeInProbe) { mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec); @@ -3279,28 +3652,66 @@ mDNSlocal void SendQueries(mDNS *const m) if (newptr) queryptr = newptr; else LogMsg("SendQueries: How did we fail to have space for the Update record %s", ARDisplayString(m,ar)); } + } + + for (q = m->Questions; q; q = q->next) + { + if (q->AnonInfo && q->AnonInfo->SendNow == intf->InterfaceID) + { + mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, q->AnonInfo->nsec3RR); + if (newptr) + { + debugf("SendQueries: Added NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID); + queryptr = newptr; + } + else + { + LogMsg("SendQueries: ERROR!! Cannot add NSEC3 record %s on InterfaceID %p", RRDisplayString(m, q->AnonInfo->nsec3RR), intf->InterfaceID); + } + q->AnonInfo->SendNow = mDNSNULL; + } + } if (queryptr > m->omsg.data) { - if (OwnerRecordSpace) + // If we have data to send, add OWNER/TRACER/OWNER+TRACER option if necessary, then send packet + if (OwnerRecordSpace || TraceRecordSpace) { AuthRecord opt; mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record + opt.resrec.rdlength = sizeof(rdataOPT); opt.resrec.rdestimate = sizeof(rdataOPT); - SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); - LogSPS("SendQueries putting %s", ARDisplayString(m, &opt)); + if (OwnerRecordSpace && TraceRecordSpace) + { + opt.resrec.rdlength += sizeof(rdataOPT); // Two options in this OPT record + opt.resrec.rdestimate += sizeof(rdataOPT); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[1]); + } + else if (OwnerRecordSpace) + { + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + } + else if (TraceRecordSpace) + { + SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]); + } + LogInfo("SendQueries putting %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt)); queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData); if (!queryptr) - LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s", + { + LogMsg("SendQueries: How did we fail to have space for %s %s OPT record (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } if (queryptr > m->omsg.data + NormalMaxDNSMessageData) + { if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1) - LogMsg("SendQueries: Why did we generate oversized packet with OPT record %p %p %p (%d/%d/%d/%d) %s", - m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, - m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + LogMsg("SendQueries: Why did we generate oversized packet with %s %s OPT record %p %p %p (%d/%d/%d/%d) %s", OwnerRecordSpace ? "OWNER" : "", + TraceRecordSpace ? "TRACER" : "", m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr, m->omsg.h.numQuestions, m->omsg.h.numAnswers, + m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt)); + } } if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1) @@ -3361,6 +3772,7 @@ mDNSlocal void SendQueries(mDNS *const m) // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions // we legitimately couldn't send because the interface is no longer available for (q = m->Questions; q; q=q->next) + { if (q->SendQNow) { DNSQuestion *x; @@ -3368,6 +3780,8 @@ mDNSlocal void SendQueries(mDNS *const m) LogMsg("SendQueries: No active interface %p to send %s question: %p %##s (%s)", q->SendQNow, x ? "new" : "old", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); q->SendQNow = mDNSNULL; } + q->CachedAnswerNeedsUpdate = mDNSfalse; + } } mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password) @@ -3422,7 +3836,7 @@ mDNSlocal void ResetQuestionState(mDNS *const m, DNSQuestion *q) q->LastQTxTime = m->timenow; q->RecentAnswerPkts = 0; q->ThisQInterval = MaxQuestionInterval; - q->RequestUnicast = mDNSfalse; + q->RequestUnicast = 0; // Reset unansweredQueries so that we don't penalize this server later when we // start sending queries when the cache expires. q->unansweredQueries = 0; @@ -3445,14 +3859,16 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco // a RMV for a single record in the rrset, we invalidate the response. If we deliver another add // in the future, we will do the revalidation again. // - // Also, if we deliver an ADD for a negative cache record and it has no NSECS, the ValidationStatus needs + // Also, if we deliver an ADD for a negative cache record and it has no NSEC/NSEC3, the ValidationStatus needs // to be reset. This happens normally when we deliver a "secure" negative response followed by an insecure - // negative response which can happen e.g., when disconnecting from network. As we don't deliver RMVs for - // negative responses that were delivered before, we need to do it on the next ADD of a negative cache - // record. This ADD could be the result of a timeout, no DNS servers etc. If we don't reset the state, we - // will deliver this as a secure response. + // negative response which can happen e.g., when disconnecting from network that leads to a negative response + // due to no DNS servers. As we don't deliver RMVs for negative responses that were delivered before, we need + // to do it on the next ADD of a negative cache record. This ADD could be the result of a timeout, no DNS servers + // etc. in which case we need to reset the state to make sure we don't deliver them as secure. If this is + // a real negative response, we would reset the state here and validate the results at the end of this function. + // or the real response again if we purge the cache. if (q->ValidationRequired && ((AddRecord == QC_rmv) || - (rr->resrec.RecordType == kDNSRecordTypePacketNegative && !rr->nsec))) + (rr->resrec.RecordType == kDNSRecordTypePacketNegative && (AddRecord == QC_add)))) { q->ValidationStatus = 0; q->ValidationState = DNSSECValRequired; @@ -3474,7 +3890,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco { // If the query is suppressed, then we don't want to answer from the cache. But if this query is // supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions - // that are timing out, which we know are answered with Negative cache record when timing out. + // that are timing out, which we know are answered with negative cache record when timing out. if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0)) return; } @@ -3510,9 +3926,9 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us // Only deliver negative answers if client has explicitly requested them except when we are forcing a negative response - // for the purpose of retrying search domains + // for the purpose of retrying search domains/timeout OR the question is suppressed if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype))) - if (!AddRecord || (AddRecord != QC_forceresponse && !q->ReturnIntermed)) return; + if (!AddRecord || (AddRecord != QC_suppressed && AddRecord != QC_forceresponse && !q->ReturnIntermed)) return; // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that if (q->QuestionCallback && !q->NoAnswer && (!followcname || q->ReturnIntermed)) @@ -3537,18 +3953,12 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco // 3) The name does not exist // 4) No DNS servers are available and we need a quick response for the application // - // (1) and (2) are handled by "QC_add" check as AddRecord would be "QC_forceresponse" in that case. - // For (3), it is possible that we don't get nsecs back but we still need to call VerifySignature so - // that we can deliver the appropriate DNSSEC result. There is no point in verifying signature for (4) - // and hence the explicit check for q->qDNSServer. + // (1) and (2) are handled by "QC_add" check as AddRecord would be "QC_forceresponse" or "QC_suppressed" + // in that case. For (3), it is possible that we don't get nsecs back but we still need to call + // VerifySignature so that we can deliver the appropriate DNSSEC result. There is no point in verifying + // signature for (4) and hence the explicit check for q->qDNSServer. // - // Note: It is important that we avoid (4) here because once the state is set to DNSSECValInProgress, - // we won't verify the signature when it is really needed. For example, start a query (which is answered - // securely), disconnect from the network. (3) would happen now and if we start the verification, we - // move DNSSECValInProgress but never have a chance to go back to DNSSECValRequired as we don't deliver - // RMVs for negative response that was added before. - // - if (m->CurrentQuestion == q && (AddRecord == QC_add) && !q->ValidatingResponse && + if (m->CurrentQuestion == q && (AddRecord == QC_add) && !q->ValidatingResponse && q->ValidationRequired && q->ValidationState == DNSSECValRequired && q->qDNSServer) { q->ValidationState = DNSSECValInProgress; @@ -3565,7 +3975,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG), // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated // first before following it - if (!DNSSECQuestion(q) && followcname && m->CurrentQuestion == q) + if (!ValidatingQuestion(q) && followcname && m->CurrentQuestion == q) AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); } @@ -3587,19 +3997,65 @@ mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr) m->CurrentQuestion = mDNSNULL; } -mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot) +mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot, mDNSBool *purge) { const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond; // See if there are any records expiring within one second const mDNSs32 start = m->timenow - 0x10000000; mDNSs32 delay = start; CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); const CacheRecord *rr; + + if (purge) + *purge = mDNSfalse; for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + // If there are records that will expire soon, there are cases that need delayed + // delivery of events: + // + // 1) A new cache entry is about to be added as a replacement. The caller needs to + // deliver a RMV (for the current old entry) followed by ADD (for the new entry). + // It needs to schedule the timer for the next cache expiry (ScheduleNextCacheCheckTime), + // so that the cache entry can be purged (purging causes the RMV followed by ADD) + // + // 2) A new question is about to be answered and the caller needs to know whether it's + // scheduling should be delayed so that the question is not answered with this record. + // Instead of delivering an ADD (old entry) followed by RMV (old entry) and another ADD + // (new entry), a single ADD can be delivered by delaying the scheduling of the question + // immediately. + // + // When the unicast cache record is created, it's TTL has been extended beyond its value + // given in the resource record (See RRAdjustTTL). If it is in the "extended" time, the + // cache is already expired and we set "purge" to indicate that. When "purge" is set, the + // return value of the function should be ignored by the callers. + // + // Note: For case (1), "purge" argument is NULL and hence the following checks are skipped. + // It is okay to skip in that case because the cache records have been set to expire almost + // immediately and the extended time does not apply. + // + // Also, if there is already an active question we don't try to optimize as purging the cache + // would end up delivering RMV for the active question and hence we avoid that. + + if (purge && !rr->resrec.InterfaceID && !rr->CRActiveQuestion && rr->resrec.rroriginalttl) + { + mDNSu32 uTTL = RRUnadjustedTTL(rr->resrec.rroriginalttl); + if (m->timenow - (rr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0) + { + LogInfo("CheckForSoonToExpireRecords: %s: rroriginalttl %u, unadjustedTTL %u, currentTTL %u", + CRDisplayString(m, rr), rr->resrec.rroriginalttl, uTTL, (m->timenow - rr->TimeRcvd)/mDNSPlatformOneSecond); + *purge = mDNStrue; + continue; + } + } if (threshhold - RRExpireTime(rr) >= 0) // If we have records about to expire within a second + { if (delay - RRExpireTime(rr) < 0) // then delay until after they've been deleted delay = RRExpireTime(rr); - if (delay - start > 0) return(NonZeroTime(delay)); - else return(0); + } + } + if (delay - start > 0) + return(NonZeroTime(delay)); + else + return(0); } // CacheRecordAdd is only called from CreateNewCacheEntry, *never* directly as a result of a client API call. @@ -3644,6 +4100,7 @@ mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr) &rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ? rr->resrec.rDNSServer->port : zeroIPPort), q); q->CurrentAnswers++; + q->unansweredQueries = 0; if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++; if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++; @@ -3748,6 +4205,17 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr) if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--; if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--; } + + // If we have dropped below the answer threshold for this mDNS question, + // restart the queries at InitialQuestionInterval. + if (mDNSOpaque16IsZero(q->TargetQID) && (q->BrowseThreshold > 0) && (q->CurrentAnswers < q->BrowseThreshold)) + { + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; + SetNextQueryTime(m,q); + LogInfo("CacheRecordRmv: (%s) %##s dropped below threshold of %d answers", + DNSTypeName(q->qtype), q->qname.c, q->BrowseThreshold); + } if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results { if (q->CurrentAnswers == 0) @@ -3790,30 +4258,8 @@ mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp) ReleaseCacheEntity(m, e); } -mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) +mDNSlocal void ReleaseAdditionalCacheRecords(mDNS *const m, CacheRecord **rp) { - CacheGroup *cg; - CacheRecord **rp; - const mDNSu32 slot = HashSlot(r->resrec.name); - - //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r)); - if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata); - r->resrec.rdata = mDNSNULL; - - cg = CacheGroupForRecord(m, slot, &r->resrec); - if (!cg) LogMsg("ReleaseCacheRecord: ERROR!! cg NULL for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); - - // When NSEC records are not added to the cache, it is usually cached at the "nsec" list - // of the CacheRecord. But sometimes they may be freed without adding to the "nsec" list - // (which is handled below) and in that case it should be freed here. - if (r->resrec.name && cg && r->resrec.name != cg->name) - { - LogInfo("ReleaseCacheRecord: freeing %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); - mDNSPlatformMemFree((void *)r->resrec.name); - } - r->resrec.name = mDNSNULL; - - rp = &(r->nsec); while (*rp) { CacheRecord *rr = *rp; @@ -3823,20 +4269,72 @@ mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) mDNSPlatformMemFree(rr->resrec.rdata); rr->resrec.rdata = mDNSNULL; } - // NSEC records that are added to the "nsec" list does not share the name + // NSEC or SOA records that are not added to the CacheGroup do not share the name // of the CacheGroup. if (rr->resrec.name) { - LogInfo("ReleaseCacheRecord: freeing cached nsec %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); + debugf("ReleaseAdditionalCacheRecords: freeing cached record %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); mDNSPlatformMemFree((void *)rr->resrec.name); rr->resrec.name = mDNSNULL; } + // Don't count the NSEC3 records used by anonymous browse/reg + if (!rr->resrec.InterfaceID) + { + m->rrcache_totalused_unicast -= rr->resrec.rdlength; + if (DNSSECRecordType(rr->resrec.rrtype)) + BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, rr->resrec.rdlength); + } ReleaseCacheEntity(m, (CacheEntity *)rr); } - ReleaseCacheEntity(m, (CacheEntity *)r); } -// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering +mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) +{ + CacheGroup *cg; + const mDNSu32 slot = HashSlot(r->resrec.name); + + //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r)); + if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata); + r->resrec.rdata = mDNSNULL; + + cg = CacheGroupForRecord(m, slot, &r->resrec); + + if (!cg) + { + // It is okay to have this printed for NSEC/NSEC3s + LogInfo("ReleaseCacheRecord: ERROR!! cg NULL for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + } + // When NSEC records are not added to the cache, it is usually cached at the "nsec" list + // of the CacheRecord. But sometimes they may be freed without adding to the "nsec" list + // (which is handled below) and in that case it should be freed here. + if (r->resrec.name && cg && r->resrec.name != cg->name) + { + debugf("ReleaseCacheRecord: freeing %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + mDNSPlatformMemFree((void *)r->resrec.name); + } + r->resrec.name = mDNSNULL; + + if (r->resrec.AnonInfo) + { + debugf("ReleaseCacheRecord: freeing AnonInfo for %##s (%s)", r->resrec.name->c, DNSTypeName(r->resrec.rrtype)); + FreeAnonInfo((void *)r->resrec.AnonInfo); + } + r->resrec.AnonInfo = mDNSNULL; + + if (!r->resrec.InterfaceID) + { + m->rrcache_totalused_unicast -= r->resrec.rdlength; + if (DNSSECRecordType(r->resrec.rrtype)) + BumpDNSSECStats(m, kStatsActionDecrement, kStatsTypeMemoryUsage, r->resrec.rdlength); + } + + ReleaseAdditionalCacheRecords(m, &r->nsec); + ReleaseAdditionalCacheRecords(m, &r->soa); + + ReleaseCacheEntity(m, (CacheEntity *)r); +} + +// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering // CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all // callbacks for old records are delivered before callbacks for newer records. mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGroup *const cg) @@ -3853,6 +4351,7 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou if (m->timenow - event >= 0) // If expired, delete it { *rp = rr->next; // Cut it from the list + verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s", m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr)); if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away @@ -3911,14 +4410,119 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou m->lock_rrcache = 0; } +// "LORecord" includes both LocalOnly and P2P record. This function assumes m->CurrentQuestion is pointing to "q". +// +// If "CheckOnly" is set to "true", the question won't be answered but just check to see if there is an answer and +// returns true if there is an answer. +// +// If "CheckOnly" is set to "false", the question will be answered if there is a LocalOnly/P2P record and +// returns true to indicate the same. +mDNSlocal mDNSBool AnswerQuestionWithLORecord(mDNS *const m, DNSQuestion *q, mDNSBool checkOnly) +{ + mDNSu32 slot; + AuthRecord *lr; + AuthGroup *ag; + + if (m->CurrentRecord) + LogMsg("AnswerQuestionWithLORecord ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + + slot = AuthHashSlot(&q->qname); + ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); + if (ag) + { + m->CurrentRecord = ag->members; + while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) + { + AuthRecord *rr = m->CurrentRecord; + m->CurrentRecord = rr->next; + // + // If the question is mDNSInterface_LocalOnly, all records local to the machine should be used + // to answer the query. This is handled in AnswerNewLocalOnlyQuestion. + // + // We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more + // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly + // we handle both mDNSInterface_Any and scoped questions. + + if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any)) + if (LocalOnlyRecordAnswersQuestion(rr, q)) + { + if (checkOnly) + { + LogInfo("AnswerQuestionWithLORecord: question %##s (%s) answered by %s", q->qname.c, DNSTypeName(q->qtype), + ARDisplayString(m, rr)); + m->CurrentRecord = mDNSNULL; + return mDNStrue; + } + AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); + if (m->CurrentQuestion != q) + break; // If callback deleted q, then we're finished here + } + } + } + m->CurrentRecord = mDNSNULL; + + if (m->CurrentQuestion != q) + { + LogInfo("AnswerQuestionWithLORecord: Question deleted while while answering LocalOnly record answers"); + return mDNStrue; + } + + if (q->LOAddressAnswers) + { + LogInfo("AnswerQuestionWithLORecord: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d", + q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); + return mDNStrue; + } + + // Before we go check the cache and ship this query on the wire, we have to be sure that there are + // no local records that could possibly answer this question. As we did not check the NewLocalRecords, we + // need to just peek at them to see whether it will answer this question. If it would answer, pretend + // that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally + // when we add new /etc/hosts entries and restart the question. It is a new question and also a new record. + if (ag) + { + lr = ag->NewLocalOnlyRecords; + while (lr) + { + if (UniqueLocalOnlyRecord(lr) && LocalOnlyRecordAnswersQuestion(lr, q)) + { + LogInfo("AnswerQuestionWithLORecord: Question %p %##s (%s) will be answered using new local auth records " + " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); + return mDNStrue; + } + lr = lr->next; + } + } + return mDNSfalse; +} + +// Today, we suppress questions (not send them on the wire) for several reasons e.g., +// AAAA query is suppressed because no IPv6 capability or PID is not allowed to make +// DNS requests. We need to temporarily suspend the suppress status so that we can +// deliver a negative response (AnswerCurrentQuestionWithResourceRecord does not answer +// suppressed questions) and reset it back. In the future, if there are other +// reasons for suppressing the query, this function should be updated. +mDNSlocal void AnswerSuppressedQuestion(mDNS *const m, DNSQuestion *q) +{ + mDNSBool SuppressQuery = q->SuppressQuery; + mDNSBool DisallowPID = q->DisallowPID; + + // make sure that QuerySuppressed() returns false + q->SuppressQuery = mDNSfalse; + q->DisallowPID = mDNSfalse; + + GenerateNegativeResponse(m, QC_suppressed); + + q->SuppressQuery = SuppressQuery; + q->DisallowPID = DisallowPID; +} + mDNSlocal void AnswerNewQuestion(mDNS *const m) { mDNSBool ShouldQueryImmediately = mDNStrue; DNSQuestion *const q = m->NewQuestions; // Grab the question we're going to answer mDNSu32 slot = HashSlot(&q->qname); CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - AuthRecord *lr; - AuthGroup *ag; mDNSBool AnsweredFromCache = mDNSfalse; verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -3964,67 +4568,17 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) if (m->CurrentQuestion == q) q->NoAnswer = NoAnswer_Fail; // Restore NoAnswer state m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } - if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; } - - // See if we want to tell it about LocalOnly records - if (m->CurrentRecord) - LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); - slot = AuthHashSlot(&q->qname); - ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname); - if (ag) - { - m->CurrentRecord = ag->members; - while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords) - { - AuthRecord *rr = m->CurrentRecord; - m->CurrentRecord = rr->next; - // - // If the question is mDNSInterface_LocalOnly, all records local to the machine should be used - // to answer the query. This is handled in AnswerNewLocalOnlyQuestion. - // - // We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more - // details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly - // we handle both mDNSInterface_Any and scoped questions. - - if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any)) - if (LocalOnlyRecordAnswersQuestion(rr, q)) - { - AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); - if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here - } - } - } - m->CurrentRecord = mDNSNULL; - - if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; } - if (q->LOAddressAnswers) + if (m->CurrentQuestion != q) { - LogInfo("AnswerNewQuestion: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d", - q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); + LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; } - // Before we go check the cache and ship this query on the wire, we have to be sure that there are - // no local records that could possibly answer this question. As we did not check the NewLocalRecords, we - // need to just peek at them to see whether it will answer this question. If it would answer, pretend - // that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally - // when we add new /etc/hosts entries and restart the question. It is a new question and also a new record. - if (ag) - { - lr = ag->NewLocalOnlyRecords; - while (lr) - { - if (LORecordAnswersAddressType(lr) && LocalOnlyRecordAnswersQuestion(lr, q)) - { - LogInfo("AnswerNewQuestion: Question %p %##s (%s) will be answered using new local auth records " - " LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers); - goto exit; - } - lr = lr->next; - } - } - + // See if we want to tell it about LocalOnly/P2P records. If we answered them using LocalOnly + // or P2P record, then we are done. + if (AnswerQuestionWithLORecord(m, q, mDNSfalse)) + goto exit; // If we are not supposed to answer this question, generate a negative response. // Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question @@ -4032,7 +4586,10 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) // If it is a question trying to validate some response, it already checked the cache for a response. If it still // reissues a question it means it could not find the RRSIGs. So, we need to bypass the cache check and send // the question out. - if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; GenerateNegativeResponse(m); q->SuppressQuery = mDNStrue; } + if (QuerySuppressed(q)) + { + AnswerSuppressedQuestion(m, q); + } else if (!q->ValidatingResponse) { CacheRecord *rr; @@ -4073,7 +4630,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains) { LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - GenerateNegativeResponse(m); + GenerateNegativeResponse(m, QC_forceresponse); } if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } @@ -4296,8 +4853,8 @@ mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const Res mDNSexport void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr) { - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("mDNS_PurgeCacheResourceRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); + // Make sure we mark this record as thoroughly expired -- we don't ever want to give // a positive answer using an expired record (e.g. from an interface that has gone away). // We don't want to clear CRActiveQuestion here, because that would leave the record subject to @@ -4400,10 +4957,13 @@ mDNSlocal void TimeoutQuestions(mDNS *const m) DNSQuestion *const q = m->CurrentQuestion; if (q->StopTime) { + if (!q->TimeoutQuestion) + LogMsg("TimeoutQuestions: ERROR!! TimeoutQuestion not set, but StopTime set for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + if (m->timenow - q->StopTime >= 0) { - LogInfo("TimeoutQuestions: question %##s timed out, time %d", q->qname.c, m->timenow - q->StopTime); - GenerateNegativeResponse(m); + LogInfo("TimeoutQuestions: question %p %##s timed out, time %d", q, q->qname.c, m->timenow - q->StopTime); + GenerateNegativeResponse(m, QC_forceresponse); if (m->CurrentQuestion == q) q->StopTime = 0; } else @@ -4425,7 +4985,6 @@ mDNSlocal void mDNSCoreFreeProxyRR(mDNS *const m) { AuthRecord *rrPtr = m->SPSRRSet, *rrNext = mDNSNULL; LogSPS("%s : Freeing stored sleep proxy A/AAAA records", __func__); - rrPtr = m->SPSRRSet; while (rrPtr) { rrNext = rrPtr->next; @@ -4439,6 +4998,10 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) { mDNS_Lock(m); // Must grab lock before trying to read m->timenow +#if APPLE_OSX_mDNSResponder + mDNSLogStatistics(m); +#endif // APPLE_OSX_mDNSResponder + if (m->timenow - m->NextScheduledEvent >= 0) { int i; @@ -4506,26 +5069,11 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) mDNS_SendKeepalives(m); } - // After two seconds after the owner option is set, call the ioctl to clear the - // ignore neighbor advertisement flag. -#if APPLE_OSX_mDNSResponder - if (m->clearIgnoreNA && m->timenow - m->clearIgnoreNA >= 0) - { - mDNSPlatformToggleInterfaceAdvt(m, mDNSfalse); - m->clearIgnoreNA = 0; - } -#endif // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) { m->AnnounceOwner = 0; } - if (m->ClearSPSRecords && m->timenow - m->ClearSPSRecords >= 0) - { - // Free the stored records that we had registered with the sleep proxy - mDNSCoreFreeProxyRR(m); - m->ClearSPSRecords = 0; - } if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) { @@ -4717,6 +5265,7 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) return(m->NextScheduledEvent); } +#ifndef UNICAST_DISABLED mDNSlocal void SuspendLLQs(mDNS *m) { DNSQuestion *q; @@ -4724,6 +5273,7 @@ mDNSlocal void SuspendLLQs(mDNS *m) if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->state == LLQ_Established) { q->ReqLease = 0; sendLLQRefresh(m, q); } } +#endif // UNICAST_DISABLED mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q) { @@ -4737,7 +5287,7 @@ mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q) { for (rr = ag->members; rr; rr=rr->next) // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME - if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) + if (UniqueLocalOnlyRecord(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) { LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr)); return mDNStrue; @@ -4803,7 +5353,7 @@ mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDoma DNSQuestion *q; DNSQuestion *restart = mDNSNULL; - if (!m->mDNS_busy) LogMsg("mDNSCoreRestartAddressQueries: ERROR!! Lock not held"); + mDNS_CheckLock(m); // 1. Flush the cache records if (flushCacheRecords) flushCacheRecords(m); @@ -4934,7 +5484,11 @@ mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q) if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) { q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it +#if mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE q->LastQTime = m->timenow - q->ThisQInterval; q->RecentAnswerPkts = 0; ExpireDupSuppressInfo(q->DupSuppress, m->timenow); @@ -4953,9 +5507,13 @@ mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int // announceCount < 0 indicates default announce count should be used if (announceCount < 0) announceCount = InitialAnnounceCount; - if (rr->AnnounceCount < announceCount) + if (rr->AnnounceCount < announceCount) rr->AnnounceCount = announceCount; - rr->AnnounceCount = InitialAnnounceCount; + + if (mDNS_KeepaliveRecord(&rr->resrec)) + rr->AnnounceCount = 0; // Do not announce keepalive records + else + rr->AnnounceCount = InitialAnnounceCount; rr->SendNSECNow = mDNSNULL; InitializeLastAPTime(m, rr); } @@ -4982,7 +5540,7 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) { allowSleep = mDNSfalse; mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", m->ProxyRecords); - LogInfo("Sleep disabled because we are proxying %d records", m->ProxyRecords); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because we are proxying %d records", m->ProxyRecords); } if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m)) @@ -4991,14 +5549,14 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) NetworkInterfaceInfo *intf; for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) { - if (intf->McastTxRx && !intf->Loopback) + if (intf->McastTxRx && !intf->Loopback && !mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) { // Disallow sleep if this interface doesn't support NetWake if (!intf->NetWake) { allowSleep = mDNSfalse; mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname); - LogInfo("Sleep disabled because %s does not support NetWake", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s does not support NetWake", intf->ifname); break; } @@ -5006,8 +5564,8 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL) { allowSleep = mDNSfalse; - mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname); - LogInfo("Sleep disabled because %s has no sleep proxy", intf->ifname); + mDNS_snprintf(reason, sizeof(reason), "No sleep proxy server on %s", intf->ifname); + LogInfo("mDNS_UpdateAllowSleep: Sleep disabled because %s has no sleep proxy server", intf->ifname); break; } } @@ -5017,7 +5575,10 @@ mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m) // Call the platform code to enable/disable sleep mDNSPlatformSetAllowSleep(m, allowSleep, reason); +#else + (void) m; #endif /* !defined(IDLESLEEPCONTROL_DISABLED) */ + } mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSu32 scopeid) @@ -5025,8 +5586,11 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte // If it is not a uDNS record, check to see if the updateid is zero. "updateid" is cleared when we have // sent the resource record on all the interfaces. If the update id is not zero, check to see if it is time // to send. - if (AuthRecord_uDNS(rr) || mDNSOpaque16IsZero(rr->updateid) || m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) + if (AuthRecord_uDNS(rr) || (rr->AuthFlags & AuthFlagsWakeOnly) || mDNSOpaque16IsZero(rr->updateid) || + m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) + { return mDNSfalse; + } // If we have a pending registration for "scopeid", it is ok to send the update on that interface. // If the scopeid is too big to check for validity, we don't check against updateIntID. When @@ -5043,10 +5607,44 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte return mDNSfalse; } -mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf) +mDNSexport void UpdateRMACCallback(mDNS *const m, void *context) +{ + IPAddressMACMapping *addrmap = (IPAddressMACMapping *)context ; + m->CurrentRecord = m->ResourceRecords; + + if (!addrmap) + { + LogMsg("UpdateRMACCallback: Address mapping is NULL"); + return; + } + + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + // If this is a keepalive record and the remote IP address matches, update the RData + if (mDNS_KeepaliveRecord(&rr->resrec)) + { + mDNSAddr raddr; + getKeepaliveRaddr(m, rr, &raddr); + if (mDNSSameAddress(&raddr, &addrmap->ipaddr)) + { + UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr)); + } + } + m->CurrentRecord = rr->next; + } + + if (addrmap) + { + mDNSPlatformMemFree(addrmap); + } +} + +mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr) { mDNSu16 newrdlength; mDNSAddr laddr, raddr; + mDNSEthAddr eth; mDNSIPPort lport, rport; mDNSu32 timeout, seq, ack; mDNSu16 win; @@ -5056,21 +5654,9 @@ mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInt mDNSTCPInfo mti; mStatus ret; - if (rr->NewRData) - { - RData *n = rr->NewRData; - - LogMsg("UpdateKeepaliveRData: Update was queued on %s", ARDisplayString(m, rr)); - - rr->NewRData = mDNSNULL; - if (rr->UpdateCallback) - rr->UpdateCallback(m, rr, n, rr->newrdlength); - } - // Note: If we fail to update the DNS NULL record with additional information in this function, it will be registered // with the SPS like any other record. SPS will not send keepalives if it does not have additional information. - - mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win); + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport)) { @@ -5078,27 +5664,37 @@ mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInt return mStatus_UnknownErr; } - // If this keepalive packet would be sent on a different interface than the current one that we are processing - // now, then we don't update the DNS NULL record. But we do not prevent it from registering with the SPS. When SPS sees - // this DNS NULL record, it does not send any keepalives as it does not have all the information - - ret = mDNSPlatformRetrieveTCPInfo(m, &laddr, &lport, &raddr, &rport, &mti); - if (ret != mStatus_NoError) + if (updateMac) { - LogMsg("mDNSPlatformRetrieveTCPInfo: mDNSPlatformRetrieveTCPInfo failed %d", ret); - return ret; - } + if (laddr.type == mDNSAddrType_IPv4) + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d h=%#a d=%#a l=%u r=%u m=%s", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ethAddr); + else + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d H=%#a D=%#a l=%u r=%u m=%s", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ethAddr); - if (mti.IntfId != intf->InterfaceID) - { - LogInfo("mDNSPlatformRetrieveTCPInfo: InterfaceID mismatch mti %p, Interface %p", mti.IntfId, intf->InterfaceID); - return mStatus_BadParamErr; } - - if (laddr.type == mDNSAddrType_IPv4) - newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d h=%#a d=%#a l=%u r=%u s=%u a=%u w=%u", timeout, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), mti.seq, mti.ack, mti.window); else - newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d H=%#a D=%#a l=%u%u r=%u%u s=%u a=%u w=%u", timeout, &laddr, &raddr, lport.b[0], lport.b[1], rport.b[0], rport.b[1], rport.NotAnInteger, mti.seq, mti.ack, mti.window); + { + // If this keepalive packet would be sent on a different interface than the current one that we are processing + // now, then we don't update the DNS NULL record. But we do not prevent it from registering with the SPS. When SPS sees + // this DNS NULL record, it does not send any keepalives as it does not have all the information + mDNSPlatformMemZero(&mti, sizeof (mDNSTCPInfo)); + ret = mDNSPlatformRetrieveTCPInfo(m, &laddr, &lport, &raddr, &rport, &mti); + if (ret != mStatus_NoError) + { + LogMsg("mDNSPlatformRetrieveTCPInfo: mDNSPlatformRetrieveTCPInfo failed %d", ret); + return ret; + } + if ((intf != mDNSNULL) && (mti.IntfId != intf->InterfaceID)) + { + LogInfo("mDNSPlatformRetrieveTCPInfo: InterfaceID mismatch mti.IntfId = %p InterfaceID = %p", mti.IntfId, intf->InterfaceID); + return mStatus_BadParamErr; + } + + if (laddr.type == mDNSAddrType_IPv4) + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d h=%#a d=%#a l=%u r=%u m=%.6a s=%u a=%u w=%u", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ð, mti.seq, mti.ack, mti.window); + else + newrdlength = mDNS_snprintf((char *)&txt.c[1], sizeof(txt.c) - 1, "t=%d i=%d c=%d H=%#a D=%#a l=%u r=%u m=%.6a s=%u a=%u w=%u", timeout, kKeepaliveRetryInterval, kKeepaliveRetryCount, &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport), ð, mti.seq, mti.ack, mti.window); + } // Did we insert a null byte at the end ? if (newrdlength == (sizeof(txt.c) - 1)) @@ -5119,16 +5715,16 @@ mDNSlocal mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInt newrd->MaxRDLength = (mDNSu16) rdsize; mDNSPlatformMemCopy(&newrd->u, txt.c, newrdlength); - rr->NewRData = newrd; - rr->newrdlength = newrdlength; - if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrd)) + // If we are updating the record for the first time, rdata points to rdatastorage as the rdata memory + // was allocated as part of the AuthRecord itself. We allocate memory when we update the AuthRecord. + // If the resource record has data that we allocated in a previous pass (to update MAC address), + // free that memory here before copying in the new data. + if ( rr->resrec.rdata != &rr->rdatastorage) { - LogMsg("UpdateKeepaliveRData: ValidateRData failed %s", ARDisplayString(m, rr)); - return mStatus_BadParamErr; + mDNSPlatformMemFree(rr->resrec.rdata); + LogSPS("UpdateKeepaliveRData: Freed allocated memory for keep alive packet: %s ", ARDisplayString(m, rr)); } - - // We don't send goodbyes for non-shared records and hence updating here should be fine - CompleteRDataUpdate(m, rr); + SetNewRData(&rr->resrec, newrd, newrdlength); // Update our rdata LogSPS("UpdateKeepaliveRData: successfully updated the record %s", ARDisplayString(m, rr)); return mStatus_NoError; @@ -5165,8 +5761,11 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo * // with different owner option gets different ID. msgid = mDNS_NewMessageID(m); for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { + if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name)))) + { if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner))) { rr->SendRNow = mDNSInterfaceMark; // mark it now @@ -5178,6 +5777,9 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo * else msgid = rr->updateid; } + } + } + } } else msgid = id; @@ -5200,12 +5802,13 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo * const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace; // If we can't update the keepalive record, don't send it - if (mDNS_KeepaliveRecord(&rr->resrec) && (UpdateKeepaliveRData(m, rr, intf) != mStatus_NoError)) + if (mDNS_KeepaliveRecord(&rr->resrec) && (UpdateKeepaliveRData(m, rr, intf, mDNSfalse, mDNSNULL) != mStatus_NoError)) { if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY)) { bit_clr_opaque64(rr->updateIntID, scopeid); } + rr->SendRNow = mDNSNULL; continue; } @@ -5290,6 +5893,7 @@ mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecor mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID InterfaceID, AuthRecord *const rr) { AuthRecord *newRR = mDNSPlatformMemAllocate(sizeof(AuthRecord)); + if (newRR == mDNSNULL) { LogSPS("%s : could not allocate memory for new resource record", __func__); @@ -5319,7 +5923,6 @@ mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID Interfa // Insert the new node at the head of the list. newRR->next = m->SPSRRSet; m->SPSRRSet = newRR; - m->ClearSPSRecords = 0; LogSPS("%s : Storing proxy record : %s ", __func__, ARDisplayString(m, rr)); } @@ -5328,10 +5931,12 @@ mDNSlocal void mDNSCoreStoreProxyRR(mDNS *const m, const mDNSInterfaceID Interfa // updateIntID bit field tells us on which interfaces we need to register this record. When we get an // ack from the sleep proxy server, we clear the interface bit. This way, we know when a record completes // registration on all the interfaces -mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntID) +mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntID, mDNSBool *WakeOnlyService) { AuthRecord *ar; LogSPS("SPSInitRecordsBeforeUpdate: UpdateIntID 0x%x 0x%x", updateIntID.l[1], updateIntID.l[0]); + + *WakeOnlyService = mDNSfalse; // Before we store the A and AAAA records that we are going to register with the sleep proxy, // make sure that the old sleep proxy records are removed. @@ -5341,11 +5946,21 @@ mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntI // never be registered on any other interface. For others, it should be sent on all interfaces. for (ar = m->ResourceRecords; ar; ar=ar->next) { + ar->updateIntID = zeroOpaque64; + ar->updateid = zeroID; if (AuthRecord_uDNS(ar)) { continue; } - ar->updateid = zeroID; + if (ar->AuthFlags & AuthFlagsWakeOnly) + { + if (ar->resrec.RecordType == kDNSRecordTypeShared && ar->RequireGoodbye) + { + ar->ImmedAnswer = mDNSInterfaceMark; + *WakeOnlyService = mDNStrue; + continue; + } + } if (!ar->resrec.InterfaceID) { LogSPS("Setting scopeid (ALL) 0x%x 0x%x for %s", updateIntID.l[1], updateIntID.l[0], ARDisplayString(m, ar)); @@ -5358,9 +5973,9 @@ mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntI mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, ar->resrec.InterfaceID, mDNStrue); if ((scopeid < (sizeof(updateIntID) * mDNSNBBY)) && bit_get_opaque64(updateIntID, scopeid)) { - ar->updateIntID = zeroOpaque64; bit_set_opaque64(ar->updateIntID, scopeid); - LogSPS("Setting scopeid(%d) 0x%x 0x%x for %s", scopeid, ar->updateIntID.l[1], ar->updateIntID.l[0], ARDisplayString(m, ar)); + LogSPS("SPSInitRecordsBeforeUpdate: Setting scopeid(%d) 0x%x 0x%x for %s", scopeid, ar->updateIntID.l[1], + ar->updateIntID.l[0], ARDisplayString(m, ar)); } else { @@ -5441,7 +6056,7 @@ mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const Resour if (answer->rrtype == kDNSType_SRV) { - // 1. Got the SRV record; now look up the target host's IP address + // 1. Got the SRV record; now look up the target host's IP address mDNS_StopQuery(m, question); intf->SPSPort[sps] = answer->rdata->u.srv.port; AssignDomainName(&question->qname, &answer->rdata->u.srv.target); @@ -5461,7 +6076,7 @@ mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const Resour } else if (answer->rrtype == kDNSType_A && answer->rdlength == 0) { - // 3. Got negative response -- target host apparently has IPv4 disabled -- so try looking up the target host's IPv6 address(es) instead + // 3. Got negative response -- target host apparently has IPv6 disabled -- so try looking up the target host's IPv4 address(es) instead mDNS_StopQuery(m, question); LogSPS("NetWakeResolve: SPS %d %##s has no IPv4 address, will try IPv6 instead", sps, question->qname.c); question->qtype = kDNSType_AAAA; @@ -5489,32 +6104,68 @@ mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m) return mDNSfalse; } -mDNSlocal void SendSleepGoodbyes(mDNS *const m) +#ifdef APPLE_OSX_mDNSResponder +// This function is used only in the case of local NIC proxy. For external +// sleep proxy server, we do this in SPSInitRecordsBeforeUpdate when we +// walk the resource records. +mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyService) { AuthRecord *rr; - m->SleepState = SleepState_Sleeping; -#ifndef UNICAST_DISABLED - SleepRecordRegistrations(m); // If we have no SPS, need to deregister our uDNS records -#endif /* UNICAST_DISABLED */ + *WakeOnlyService = mDNSfalse; // Mark all the records we need to deregister and send them for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + { + if ((rr->AuthFlags & AuthFlagsWakeOnly) && + rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + { rr->ImmedAnswer = mDNSInterfaceMark; - SendResponses(m); + *WakeOnlyService = mDNStrue; + } + } } +#endif // APPLE_OSx_mDNSResponder -/* - * This function attempts to detect if multiple interfaces are on the same subnet. - * It makes this determination based only on the IPv4 Addresses and subnet masks. - * IPv6 link local addresses that are configured by default on all interfaces make - * it hard to make this determination - * - * The 'real' fix for this would be to send out multicast packets over one interface - * and conclude that multiple interfaces are on the same subnet only if these packets - * are seen on other interfaces on the same system - */ +mDNSlocal void SendSleepGoodbyes(mDNS *const m, mDNSBool AllInterfaces, mDNSBool unicast) +{ + AuthRecord *rr; + m->SleepState = SleepState_Sleeping; + + // If AllInterfaces is not set, the caller has already marked it appropriately + // on which interfaces this should be sent. + if (AllInterfaces) + { + NetworkInterfaceInfo *intf; + for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) + { + intf->SendGoodbyes = 1; + } + } + if (unicast) + { +#ifndef UNICAST_DISABLED + SleepRecordRegistrations(m); // If we have no SPS, need to deregister our uDNS records +#endif /* UNICAST_DISABLED */ + } + + // Mark all the records we need to deregister and send them + for (rr = m->ResourceRecords; rr; rr=rr->next) + if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye) + rr->ImmedAnswer = mDNSInterfaceMark; + SendResponses(m); +} + +/* + * This function attempts to detect if multiple interfaces are on the same subnet. + * It makes this determination based only on the IPv4 Addresses and subnet masks. + * IPv6 link local addresses that are configured by default on all interfaces make + * it hard to make this determination + * + * The 'real' fix for this would be to send out multicast packets over one interface + * and conclude that multiple interfaces are on the same subnet only if these packets + * are seen on other interfaces on the same system + */ mDNSlocal mDNSBool skipSameSubnetRegistration(mDNS *const m, mDNSInterfaceID *regID, mDNSu32 count, mDNSInterfaceID intfid) { NetworkInterfaceInfo *intf; @@ -5548,14 +6199,35 @@ mDNSlocal mDNSBool skipSameSubnetRegistration(mDNS *const m, mDNSInterfaceID *re return (mDNSfalse); } +mDNSlocal void DoKeepaliveCallbacks(mDNS *m) +{ + // Loop through the keepalive records and callback with an error + m->CurrentRecord = m->ResourceRecords; + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if ((mDNS_KeepaliveRecord(&rr->resrec)) && (rr->resrec.RecordType != kDNSRecordTypeDeregistering)) + { + LogSPS("DoKeepaliveCallbacks: Invoking the callback for %s", ARDisplayString(m, rr)); + if (rr->RecordCallback) + rr->RecordCallback(m, rr, mStatus_BadStateErr); + } + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} + // BeginSleepProcessing is called, with the lock held, from either mDNS_Execute or mDNSCoreMachineSleep mDNSlocal void BeginSleepProcessing(mDNS *const m) { mDNSBool SendGoodbyes = mDNStrue; + mDNSBool WakeOnlyService = mDNSfalse; + mDNSBool invokeKACallback = mDNStrue; const CacheRecord *sps[3] = { mDNSNULL }; mDNSOpaque64 updateIntID = zeroOpaque64; mDNSInterfaceID registeredIntfIDS[128]; mDNSu32 registeredCount = 0; + int skippedRegistrations = 0; m->NextScheduledSPRetry = m->timenow; @@ -5566,7 +6238,31 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) NetworkInterfaceInfo *intf; for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) { - if (!intf->NetWake) LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname); + // Intialize it to false. These values make sense only when SleepState is set to Sleeping. + intf->SendGoodbyes = 0; + + // If it is not multicast capable, we could not have possibly discovered sleep proxy + // servers. + if (!intf->McastTxRx || mDNSPlatformInterfaceIsD2D(intf->InterfaceID)) + { + LogSPS("BeginSleepProcessing: %-6s Ignoring for registrations", intf->ifname); + continue; + } + + // If we are not capable of WOMP, then don't register with sleep proxy. + // + // Note: If we are not NetWake capable, we don't browse for the sleep proxy server. + // We might find sleep proxy servers in the cache and start a resolve on them. + // But then if the interface goes away, we won't stop these questions because + // mDNS_DeactivateNetWake_internal assumes that a browse has been started for it + // to stop both the browse and resolve questions. + if (!intf->NetWake) + { + LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname); + intf->SendGoodbyes = 1; + skippedRegistrations++; + continue; + } // Check if we have already registered with a sleep proxy for this subnet if (skipSameSubnetRegistration(m, registeredIntfIDS, registeredCount, intf->InterfaceID)) @@ -5576,20 +6272,33 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) } #if APPLE_OSX_mDNSResponder - else if (ActivateLocalProxy(m, intf->ifname) == mStatus_NoError) + else if (SupportsInNICProxy(intf)) { - SendGoodbyes = mDNSfalse; - LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); - // This will leave m->SleepState set to SleepState_Transferring, - // which is okay because with no outstanding resolves, or updates in flight, - // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed - - registeredIntfIDS[registeredCount] = intf->InterfaceID; - registeredCount++; + if (ActivateLocalProxy(m, intf) == mStatus_NoError) + { + SendGoodbyesForWakeOnlyService(m, &WakeOnlyService); + SendGoodbyes = mDNSfalse; + invokeKACallback = mDNSfalse; + LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname); + // This will leave m->SleepState set to SleepState_Transferring, + // which is okay because with no outstanding resolves, or updates in flight, + // mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed + + registeredIntfIDS[registeredCount] = intf->InterfaceID; + registeredCount++; + } } #endif // APPLE_OSX_mDNSResponder else { +#if APPLE_OSX_mDNSResponder + // If on battery, do not attempt to offload to external sleep proxies + if (m->SystemWakeOnLANEnabled == mDNS_WakeOnBattery) + { + LogSPS("BegingSleepProcessing: Not connected to AC power - Not registering with an external sleep proxy."); + return; + } +#endif // APPLE_OSX_mDNSResponder FindSPSInCache(m, &intf->NetWakeBrowse, sps); if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)", intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval); @@ -5601,10 +6310,6 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) intf->NextSPSAttempt = 0; intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond; -#if APPLE_OSX_mDNSResponder - // Before we start the sleep processing, stop IPv6 advertisements - mDNSPlatformToggleInterfaceAdvt(m, mDNStrue); -#endif scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, intf->InterfaceID, mDNStrue); // Now we know for sure that we have to wait for registration to complete on this interface. if (scopeid < (sizeof(updateIntID) * mDNSNBBY)) @@ -5641,12 +6346,47 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) // If we have at least one interface on which we are registering with an external sleep proxy, // initialize all the records appropriately. - if (!mDNSOpaque64IsZero(&updateIntID)) SPSInitRecordsBeforeUpdate(m, updateIntID); + if (!mDNSOpaque64IsZero(&updateIntID)) + SPSInitRecordsBeforeUpdate(m, updateIntID, &WakeOnlyService); - if (SendGoodbyes) // If we didn't find even one Sleep Proxy + // Call the applicaitons that registered a keepalive record to inform them that we failed to offload + // the records to a sleep proxy. + if (invokeKACallback) + { + LogSPS("BeginSleepProcessing: Did not register with an in-NIC proxy - invoking the callbacks for KA records"); + DoKeepaliveCallbacks(m); + } + + // SendSleepGoodbyes last two arguments control whether we send goodbyes on all + // interfaces and also deregister unicast registrations. + // + // - If there are no sleep proxy servers, then send goodbyes on all interfaces + // for both multicast and unicast. + // + // - If we skipped registrations on some interfaces, then we have already marked + // them appropriately above. We don't need to send goodbyes for unicast as + // we have registered with at least one sleep proxy. + // + // - If we are not planning to send any goodbyes, then check for WakeOnlyServices. + // + // Note: If we are planning to send goodbyes, we mark the record with mDNSInterfaceAny + // and call SendResponses which inturn calls ShouldSendGoodbyesBeforeSleep which looks + // at WakeOnlyServices first. + if (SendGoodbyes) { LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server"); - SendSleepGoodbyes(m); + SendSleepGoodbyes(m, mDNStrue, mDNStrue); + } + else if (skippedRegistrations) + { + LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server on all interfaces"); + SendSleepGoodbyes(m, mDNSfalse, mDNSfalse); + } + else if (WakeOnlyService) + { + // If we saw WakeOnly service above, send the goodbyes now. + LogSPS("BeginSleepProcessing: Sending goodbyes for WakeOnlyServices"); + SendResponses(m); } } @@ -5668,7 +6408,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) mDNSu8 oldstate = m->SPSState; mDNS_DropLockBeforeCallback(); // mDNS_DeregisterService expects to be called without the lock held, so we emulate that here m->SPSState = 2; +#ifndef SPC_DISABLED if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords); +#else + (void)oldstate; +#endif mDNS_ReclaimLockAfterCallback(); } @@ -5683,6 +6427,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { m->DelaySleep = 0; m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10); + m->mDNSStats.Sleeps++; BeginSleepProcessing(m); } @@ -5703,6 +6448,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) CacheGroup *cg; CacheRecord *cr; NetworkInterfaceInfo *intf; + mDNSs32 currtime, diff; mDNS_Lock(m); // Reset SleepLimit back to 0 now that we're awake again. @@ -5725,6 +6471,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) m->SPSState = 0; mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags); } + m->mDNSStats.Wakes++; // ... and the same for NextSPSAttempt for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1; @@ -5737,13 +6484,71 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow); // 2. Re-validate our cache records + currtime = mDNSPlatformUTC(); + +#if APPLE_OSX_mDNSResponder + // start time of this statistics gathering interval + m->StatStartTime = currtime; +#endif // APPLE_OSX_mDNSResponder + + diff = currtime - m->TimeSlept; FORALL_CACHERECORDS(slot, cg, cr) { - mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake); + // Temporary fix: For unicast cache records, look at how much time we slept. + // Adjust the RecvTime by the amount of time we slept so that we age the + // cache record appropriately. If it is expired already, purge. If there + // is a network change that happens after the wakeup, we might purge the + // cache anyways and this helps only in the case where there are no network + // changes across sleep/wakeup transition. + // + // Note: If there is a network/DNS server change that already happened and + // these cache entries are already refreshed and we are getting a delayed + // wake up notification, we might adjust the TimeRcvd based on the time slept + // now which can cause the cache to purge pre-maturely. As this is not a very + // common case, this should happen rarely. + if (!cr->resrec.InterfaceID) + { + if (diff > 0) + { + mDNSu32 uTTL = RRUnadjustedTTL(cr->resrec.rroriginalttl); + const mDNSs32 remain = uTTL - (m->timenow - cr->TimeRcvd) / mDNSPlatformOneSecond; + + // -if we have slept longer than the remaining TTL, purge and start fresh. + // -if we have been sleeping for a long time, we could reduce TimeRcvd below by + // a sufficiently big value which could cause the value to go into the future + // because of the signed comparison of time. For this to happen, we should have been + // sleeping really long (~24 days). For now, we want to be conservative and flush even + // if we have slept for more than two days. + + if (diff >= remain || diff > (2 * 24 * 3600)) + { + LogInfo("mDNSCoreMachineSleep: %s: Purging cache entry SleptTime %d, Remaining TTL %d", + CRDisplayString(m, cr), diff, remain); + mDNS_PurgeCacheResourceRecord(m, cr); + continue; + } + cr->TimeRcvd -= (diff * mDNSPlatformOneSecond); + if (m->timenow - (cr->TimeRcvd + ((mDNSs32)uTTL * mDNSPlatformOneSecond)) >= 0) + { + LogInfo("mDNSCoreMachineSleep: %s: Purging after adjusting the remaining TTL %d by %d seconds", + CRDisplayString(m, cr), remain, diff); + mDNS_PurgeCacheResourceRecord(m, cr); + } + else + { + LogInfo("mDNSCoreMachineSleep: %s: Adjusted the remain ttl %u by %d seconds", CRDisplayString(m, cr), remain, diff); + } + } + } + else + { + mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake); + } } // 3. Retrigger probing and announcing for all our authoritative records for (rr = m->ResourceRecords; rr; rr=rr->next) + { if (AuthRecord_uDNS(rr)) { ActivateUnicastRegistration(m, rr); @@ -5752,17 +6557,17 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { mDNSCoreRestartRegistration(m, rr, -1); } + } // 4. Refresh NAT mappings // We don't want to have to assume that all hardware can necessarily keep accurate - // track of passage of time while asleep, so on wake we refresh our NAT mappings + // track of passage of time while asleep, so on wake we refresh our NAT mappings. // We typically wake up with no interfaces active, so there's no need to rush to try to find our external address. - // When we get a network configuration change, mDNSMacOSXNetworkChanged calls uDNS_SetupDNSConfig, which calls - // mDNS_SetPrimaryInterfaceInfo, which then sets m->retryGetAddr to immediately request our external address from the NAT gateway. - m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - m->retryGetAddr = m->timenow + mDNSPlatformOneSecond * 5; - LogInfo("mDNSCoreMachineSleep: retryGetAddr in %d %d", m->retryGetAddr - m->timenow, m->timenow); - RecreateNATMappings(m); + // But if we do get a network configuration change, mDNSMacOSXNetworkChanged will call uDNS_SetupDNSConfig, which + // will call mDNS_SetPrimaryInterfaceInfo, which will call RecreateNATMappings to refresh them, potentially sooner + // than five seconds from now. + LogInfo("mDNSCoreMachineSleep: recreating NAT mappings in 5 seconds"); + RecreateNATMappings(m, mDNSPlatformOneSecond * 5); mDNS_Unlock(m); } } @@ -5871,7 +6676,7 @@ spsnotready: // If we allow just one more second to send our goodbyes, that puts us at 27 seconds. m->SleepLimit = now + mDNSPlatformOneSecond * 1; - SendSleepGoodbyes(m); + SendSleepGoodbyes(m, mDNStrue, mDNStrue); } notready: @@ -6181,6 +6986,40 @@ mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const Res } return(rr); } +mDNSlocal void DeregisterProxyRecord(mDNS *const m, AuthRecord *const rr) +{ + rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host + rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it + mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); + SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); +} + +mDNSlocal void ClearKeepaliveProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist, const mDNSInterfaceID InterfaceID) +{ + if (m->CurrentRecord) + LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord)); + m->CurrentRecord = thelist; + + // Normally, the RDATA of the keepalive record will be different each time and hence we always + // clean up the keepalive record. + while (m->CurrentRecord) + { + AuthRecord *const rr = m->CurrentRecord; + if (InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) + { + if (mDNS_KeepaliveRecord(&m->rec.r.resrec)) + { + LogSPS("ClearKeepaliveProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", + m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); + DeregisterProxyRecord(m, rr); + } + } + // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because + // new records could have been added to the end of the list as a result of that call. + if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now + m->CurrentRecord = rr->next; + } +} // Called from mDNSCoreReceiveUpdate when we get a sleep proxy registration request, // to check our lists and discard any stale duplicates of this record we already have @@ -6193,16 +7032,11 @@ mDNSlocal void ClearIdenticalProxyRecords(mDNS *const m, const OwnerOptData *con { AuthRecord *const rr = m->CurrentRecord; if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC)) - // Normally, the RDATA of the keepalive record will be different each time and hence we always - // clean up the keepalive record. - if (mDNS_KeepaliveRecord(&rr->resrec) || IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec)) + if (IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec)) { LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s", m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr)); - rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host - rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it - mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); - SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID); + DeregisterProxyRecord(m, rr); } // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because // new records could have been added to the end of the list as a result of that call. @@ -6255,7 +7089,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast, mDNSBool QueryWasLocalUnicast, DNSMessage *const response) { - mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + mDNSBool FromLocalSubnet = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); AuthRecord *ResponseRecords = mDNSNULL; AuthRecord **nrp = &ResponseRecords; @@ -6272,6 +7106,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con mDNSu8 *responseptr = mDNSNULL; AuthRecord *rr; int i; + CacheRecord *McastNSEC3Records = mDNSNULL; // *** // *** 1. Look in Additional Section for an OPT record @@ -6296,6 +7131,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it } + // + // Look in Authority Section for NSEC3 record + // + + mDNSParseNSEC3Records(m, query, end, InterfaceID, &McastNSEC3Records); + // *** // *** 2. Parse Question Section and mark potential answers // *** @@ -6309,6 +7150,9 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con ptr = getQuestion(query, ptr, end, InterfaceID, &pktq); // get the question... if (!ptr) goto exit; + pktq.AnonInfo = mDNSNULL; + if (McastNSEC3Records) + InitializeAnonInfoForQuestion(m, &McastNSEC3Records, &pktq); // The only queries that *need* a multicast response are: // * Queries sent via multicast // * from port 5353 @@ -6319,6 +7163,12 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // For other queries, we may still choose to send the occasional multicast response anyway, // to keep our neighbours caches warm, and for ongoing conflict detection. QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse); + + if (pktq.qclass & kDNSQClass_UnicastResponse) + m->mDNSStats.UnicastBitInQueries++; + else + m->mDNSStats.NormalQueries++; + // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later pktq.qclass &= ~kDNSQClass_UnicastResponse; @@ -6336,6 +7186,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con m->CurrentRecord = rr->next; if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery)) { + m->mDNSStats.MatchingAnswersForQueries++; if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype)) { if (rr->resrec.RecordType == kDNSRecordTypeUnique) @@ -6343,13 +7194,20 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con else if (ResourceRecordIsValidAnswer(rr)) { NumAnswersForThisQuestion++; + // As we have verified this question to be part of the same subset, + // set the anonymous data which is needed below when walk the cache + // records to see what answers we should be expecting. The cache records + // may cache only the nsec3RR and not the anonymous data itself. + if (pktq.AnonInfo && rr->resrec.AnonInfo) + SetAnonData(&pktq, &rr->resrec, mDNStrue); + // Note: We should check here if this is a probe-type query, and if so, generate an immediate // unicast answer back to the source, because timeliness in answering probes is important. // Notes: // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast) - // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead) - // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later) + // NR_AnswerTo == NR_AnswerUnicast means "answer via delayed unicast" (to modern querier; may promote to multicast instead) + // NR_AnswerTo == NR_AnswerMulticast means "definitely answer via multicast" (can't downgrade to unicast later) // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set, // but the multicast querier is not on a matching subnet (e.g. because of overlaid subnets on one link) // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source) @@ -6360,9 +7218,9 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // This is to guard against the case where someone blasts us with queries as fast as they can. if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 || (rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID)) - rr->NR_AnswerTo = (mDNSu8*)~0; + rr->NR_AnswerTo = NR_AnswerMulticast; } - else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1; + else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : NR_AnswerUnicast; } } else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr)) @@ -6385,6 +7243,8 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con // so use random delay on response to reduce collisions if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms + if (query->h.flags.b[0] & kDNSFlag0_TC) + m->mDNSStats.KnownAnswerMultiplePkts++; #if ENABLE_MULTI_PACKET_QUERY_SNOOPING if (QuestionNeedsMulticastResponse) #else @@ -6433,14 +7293,24 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con #if ENABLE_MULTI_PACKET_QUERY_SNOOPING if (!(query->h.flags.b[0] & kDNSFlag0_TC)) #endif - for (q = m->Questions; q; q=q->next) - if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) - if (!q->InterfaceID || q->InterfaceID == InterfaceID) - if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) - if (q->qtype == pktq.qtype && - q->qclass == pktq.qclass && - q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) - { *dqp = q; dqp = &q->NextInDQList; } + // For anonymous question, the duplicate suppressesion should happen if the + // question belongs in the same group. As the group is expected to be + // small, we don't do the optimization for now. + if (!pktq.AnonInfo) + { + for (q = m->Questions; q; q=q->next) + if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4) + if (!q->InterfaceID || q->InterfaceID == InterfaceID) + if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList) + if (q->qtype == pktq.qtype && + q->qclass == pktq.qclass && + q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname)) + { *dqp = q; dqp = &q->NextInDQList; } + } + } + if (pktq.AnonInfo) + { + FreeAnonInfo(pktq.AnonInfo); } } @@ -6469,8 +7339,14 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { // See if this Known-Answer suppresses any of our currently planned answers for (rr=ResponseRecords; rr; rr=rr->NextResponse) + { if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr)) - { rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; } + { + m->mDNSStats.KnownAnswerSuppressions++; + rr->NR_AnswerTo = mDNSNULL; + rr->NR_AdditionalTo = mDNSNULL; + } + } // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression) for (rr=m->ResourceRecords; rr; rr=rr->next) @@ -6488,6 +7364,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con } if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester)) { + m->mDNSStats.KnownAnswerSuppressions++; rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES @@ -6555,20 +7432,42 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response) - // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc. +#if !TARGET_OS_EMBEDDED + // always honor kDNSQClass_UnicastResponse in embedded environment to increase reliability + // in high multicast packet loss environments. + + // If it's been one TTL/4 since we multicast this, then send a multicast response + // for conflict detection, etc. if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0) { SendMulticastResponse = mDNStrue; // If this record was marked for modern (delayed) unicast response, then mark it as promoted to // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below). // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value. - if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0; + if (rr->NR_AnswerTo == NR_AnswerUnicast) + { + m->mDNSStats.UnicastDemotedToMulticast++; + rr->NR_AnswerTo = NR_AnswerMulticast; + } } +#endif // !TARGET_OS_EMBEDDED // If the client insists on a multicast response, then we'd better send one - if (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue; - else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse = mDNStrue; - else if (rr->NR_AnswerTo) SendLegacyResponse = mDNStrue; + if (rr->NR_AnswerTo == NR_AnswerMulticast) + { + m->mDNSStats.MulticastResponses++; + SendMulticastResponse = mDNStrue; + } + else if (rr->NR_AnswerTo == NR_AnswerUnicast) + { + m->mDNSStats.UnicastResponses++; + SendUnicastResponse = mDNStrue; + } + else if (rr->NR_AnswerTo) + { + SendLegacyResponse = mDNStrue; + } + if (SendMulticastResponse || SendUnicastResponse) { @@ -6602,7 +7501,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con if (query->h.flags.b[0] & kDNSFlag0_TC) delayresponse = mDNSPlatformOneSecond * 20; // Divided by 50 = 400ms else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond; // Divided by 50 = 20ms } - else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0) + else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == NR_AnswerMulticast) { // Since additional records are an optimization anyway, we only ever send them on one interface at a time // If two clients on different interfaces do queries that invoke the same optional additional answer, @@ -6697,6 +7596,7 @@ exit: debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + m->mDNSStats.PoofCacheDeletions++; mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); } #if ENABLE_MULTI_PACKET_QUERY_SNOOPING @@ -6744,6 +7644,12 @@ exit: srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i); } + if (McastNSEC3Records) + { + debugf("ProcessQuery: McastNSEC3Records not used"); + FreeNSECRecords(m, McastNSEC3Records); + } + return(responseptr); } @@ -6753,7 +7659,7 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, { mDNSu8 *responseend = mDNSNULL; mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && - !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL); if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) { @@ -6871,6 +7777,111 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, return(mDNSNULL); } +// Return a pointer to the primary service name, skipping subtype name if present. +mDNSlocal const domainname *getPrimaryServiceName(const domainname *domainName) +{ + const domainname *primaryName = domainName; + const domainname *subName = SkipLeadingLabels(domainName, 1); + + if (SameDomainLabel(subName->c, (const mDNSu8 *)mDNSSubTypeLabel)) + { + // skip "._sub" portion of name + primaryName = SkipLeadingLabels(domainName, 2); + debugf("getPrimaryServiceName: returning %##s for _sub type", primaryName); + } + + return primaryName; +} + +// This function is not called if the packet is from us, which implies that we accept all multicast packets coming from us. +mDNSlocal mDNSBool ExpectingMulticastResponseForRecord(mDNS *const m, CacheRecord *rr, const mDNSAddr *srcaddr, mDNSBool recordAccepted, + CacheRecord **McastNSEC3Records) +{ + DNSQuestion *q; + + // Accept A and AAAA if we accepted something before in the same packet as most likely related to the + // service records that we may have accepted. + if (recordAccepted && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA)) + { + LogInfo("ExpectingMulticastResponseForRecord:A:AAAA: accepting %s, from %#a due to same packet %d", CRDisplayString(m, rr), srcaddr, m->PktNum); + return mDNStrue; + } + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && mDNSOpaque16IsZero(q->TargetQID)) + { + mDNSBool ret; + // 1. If a resource record answers question, cache it. This also will cache NSECs if it asserts + // non-existence of q->qtype. If we have any matching NSEC3 Records for the question, send + // it along with the resource record. Do it only for questions that are expecting to + // discover only its peers (q->AnonInfo not NULL) + if (q->AnonInfo && McastNSEC3Records && !rr->resrec.AnonInfo) + { + InitializeAnonInfoForCR(m, McastNSEC3Records, rr); + } + ret = ResourceRecordAnswersQuestion(&rr->resrec, q); + if (ret) + { + // The record and the question belong to the same subset. Set the + // anonymous data in the cache record. + if (q->AnonInfo && rr->resrec.AnonInfo) + { + SetAnonData(q, &rr->resrec, mDNSfalse); + } + LogInfo("ExpectingMulticastResponseForRecord: Name and Type match, accepting %s, from %#a", CRDisplayString(m, rr), srcaddr); + if (rr->resrec.rrtype == kDNSType_NSEC) + LogInfo("ExpectingMulticastResponseForRecord: record %s, question %##s (%s)", CRDisplayString(m, rr), q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + if (rr->resrec.rrtype == kDNSType_SRV || rr->resrec.rrtype == kDNSType_TXT) + { + // Point to the service type in the record name + const domainname *name = SkipLeadingLabels(rr->resrec.name, 1); + + // If question is for a sub type, just compare against the primary service type + const domainname *primaryName = getPrimaryServiceName(&q->qname); + + // 2. If the SRV or TXT record matches the service name, then cache it. If the TXT or SRV record is + // before the PTR record in the packet, PTR record may not be in the cache yet and hence the logic + // in (3) below will fail to cache it. + if (q->qtype == kDNSType_PTR && name && SameDomainName(primaryName, name)) + { + LogInfo("ExpectingMulticastResponseForRecord: Accepting %s due to PTR match, question %##s from %#a, pktnum %d", + CRDisplayString(m, rr), q->qname.c, srcaddr, m->PktNum); + return mDNStrue; + } + + if (name) + { + const mDNSu32 slot = HashSlot(name); + const mDNSu32 namehash = DomainNameHashValue(name); + CacheGroup *cg = CacheGroupForName(m, slot, namehash, name); + CacheRecord *cr; + + // 3. Same as in (2), but look in the cache in case we don't have the PTR question. + + for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next) + { + if (cr->resrec.rrtype == kDNSType_PTR) + { + primaryName = getPrimaryServiceName(cr->resrec.name); + + if (SameDomainName(primaryName, name)) + { + LogInfo("ExpectingMulticastResponseForRecord: accepting %s, from %#a, pktnum %d", + CRDisplayString(m, rr), srcaddr, m->PktNum); + return mDNStrue; + } + } + } + } + } + } + } + debugf("ExpectingMulticastResponseForRecord: discarding %s, from %#a, pktnum %d", CRDisplayString(m, rr), srcaddr, m->PktNum); + return(mDNSfalse); +} + // Certain data types need more space for in-memory storage than their in-packet rdlength would imply // Currently this applies only to rdata types containing more than one domainname, // or types where the domainname is not the last item in the structure. @@ -6902,8 +7913,18 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C { 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.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 + + // 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 + // CurrentAnswers etc. Otherwise, when a cache entry gets removed, CacheRecordRmv + // will complain. + if (m->rec.r.resrec.AnonInfo) + { + rr->resrec.AnonInfo = m->rec.r.resrec.AnonInfo; + m->rec.r.resrec.AnonInfo = mDNSNULL; + } rr->DelayDelivery = delay; // If this is an oversized record with external storage allocated, copy rdata to external storage @@ -6916,10 +7937,18 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C rr->next = mDNSNULL; // Clear 'next' pointer rr->nsec = mDNSNULL; + rr->soa = mDNSNULL; if (sourceAddress) rr->sourceAddress = *sourceAddress; + if (!rr->resrec.InterfaceID) + { + m->rrcache_totalused_unicast += rr->resrec.rdlength; + if (DNSSECRecordType(rr->resrec.rrtype)) + BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeMemoryUsage, rr->resrec.rdlength); + } + if (Add) { *(cg->rrcache_tail) = rr; // Append this record to tail of cache slot list @@ -6988,15 +8017,7 @@ mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // also do this check here to make sure we can't get overflow below when we add a quarter to the TTL if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond; - // Adjustment factor to avoid race condition: - // Suppose real record as TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100. - // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another - // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox. - // To avoid this, we extend the record's effective TTL to give it a little extra grace period. - // We adjust the 100 second TTL to 126. This means that when we do our 80% query at 101 seconds, - // the cached copy at our local caching server will already have expired, so the server will be forced - // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds. - ttl += ttl/4 + 2; + ttl = RRAdjustTTL(ttl); // For mDNS, TTL zero means "delete this record" // For uDNS, TTL zero means: this data is true at this moment, but don't cache it. @@ -7073,9 +8094,15 @@ mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist DNSTypeName(q->qtype)); return mDNStrue; } - else if (typeCovered == kDNSType_NSEC) + else if (typeCovered == kDNSType_NSEC || typeCovered == kDNSType_NSEC3) + { + LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches %s type (nseclist = 1)", CRDisplayString(m, newcr), DNSTypeName(typeCovered)); + *nseclist = mDNStrue; + return mDNStrue; + } + else if (typeCovered == kDNSType_SOA) { - LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches NSEC type (nseclist = 1)", CRDisplayString(m, newcr)); + LogInfo("IsResponseAcceptable: Accepting RRSIG %s matches SOA type (nseclist = 1)", CRDisplayString(m, newcr)); *nseclist = mDNStrue; return mDNStrue; } @@ -7092,6 +8119,18 @@ mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist *nseclist = mDNStrue; return mDNStrue; } + if (rr->rrtype == kDNSType_SOA) + { + LogInfo("IsResponseAcceptable: Accepting SOA %s (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } + else if (rr->rrtype == kDNSType_NSEC3) + { + LogInfo("IsResponseAcceptable: Accepting NSEC3 %s (nseclist = 1)", CRDisplayString(m, newcr)); + *nseclist = mDNStrue; + return mDNStrue; + } } return mDNSfalse; } @@ -7107,11 +8146,58 @@ mDNSlocal void FreeNSECRecords(mDNS *const m, CacheRecord *NSECRecords) } } +// If we received zero DNSSEC records even when the DO/EDNS0 bit was set, we need to provide this +// information to ValidatingResponse question to indicate the DNSSEC status to the application +mDNSlocal void mDNSCoreReceiveNoDNSSECAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr, + mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) +{ + int i; + const mDNSu8 *ptr = response->data; + + for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) + { + DNSQuestion pktq; + DNSQuestion *qptr = mDNSNULL; + ptr = getQuestion(response, ptr, end, InterfaceID, &pktq); + if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &pktq, !dstaddr)) && + qptr->ValidatingResponse) + { + DNSQuestion *next, *q; + + if (qptr->DuplicateOf) + LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question matching response", qptr->qname.c, DNSTypeName(qptr->qtype)); + + // Be careful to call the callback for duplicate questions first and then the original + // question. If we called the callback on the original question, it could stop and + // a duplicate question would become the original question. + mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls + for (q = qptr->next ; q && q != m->NewQuestions; q = next) + { + next = q->next; + if (q->DuplicateOf == qptr) + { + if (q->ValidatingResponse) + LogInfo("mDNSCoreReceiveNoDNSSECAnswers: qptr %##s (%s) Duplicate question found", q->qname.c, DNSTypeName(q->qtype)); + else + LogMsg("mDNSCoreReceiveNoDNSSECAnswers: ERROR!! qptr %##s (%s) Duplicate question not ValidatingResponse", q->qname.c, DNSTypeName(q->qtype)); + if (q->QuestionCallback) + q->QuestionCallback(m, q, mDNSNULL, QC_nodnssec); + } + } + if (qptr->QuestionCallback) + qptr->QuestionCallback(m, qptr, mDNSNULL, QC_nodnssec); + mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again + } + } +} + mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, uDNS_LLQType LLQType, mDNSu8 rcode, CacheRecord *NSECRecords) { int i; const mDNSu8 *ptr = response->data; + CacheRecord *SOARecord = mDNSNULL; + for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++) { DNSQuestion q; @@ -7152,10 +8238,18 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * { LogInfo("mDNSCoreReceiveNoUnicastAnswers: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); m->CurrentQuestion = qptr; - GenerateNegativeResponse(m); + // We are not creating a cache record in this case, we need to pass back + // the error we got so that the proxy code can return the right one to + // the application + if (qptr->ProxyQuestion) + qptr->responseFlags = response->h.flags; + GenerateNegativeResponse(m, QC_forceresponse); m->CurrentQuestion = mDNSNULL; } - else LogInfo("mDNSCoreReceiveNoUnicastAnswers: Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + else + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: Skipping check and not creating a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + } } else { @@ -7182,6 +8276,8 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA) { + const mDNSu32 s = HashSlot(m->rec.r.resrec.name); + CacheGroup *cgSOA = CacheGroupForRecord(m, s, &m->rec.r.resrec); const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data; mDNSu32 ttl_s = soa->min; // We use the lesser of the SOA.MIN field and the SOA record's TTL, *except* @@ -7191,11 +8287,18 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * ttl_s = m->rec.r.resrec.rroriginalttl; if (negttl < ttl_s) negttl = ttl_s; + // Create the SOA record as we may have to return this to the questions + // that we are acting as a proxy for currently or in the future. + SOARecord = CreateNewCacheEntry(m, s, cgSOA, 1, mDNSfalse, mDNSNULL); + // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer, // with an Authority Section SOA record for d.com, then this is a hint that the authority // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either. // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us - if (q.qtype == kDNSType_SOA) + // + // For ProxyQuestions, we don't do this as we need to create additional SOA records to cache them + // along with the negative cache record. For simplicity, we don't create the additional records. + if (!qptr->ProxyQuestion && q.qtype == kDNSType_SOA) { int qcount = CountLabels(&q.qname); int scount = CountLabels(m->rec.r.resrec.name); @@ -7234,44 +8337,90 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * // restarted (setting it to InitialQuestionInterval) for other reasons e.g., wakeup, // we should reset its question interval here to MaxQuestionInterval. ResetQuestionState(m, qptr); + if (DNSSECQuestion(qptr)) + neg->CRDNSSECQuestion = 1; // Update the NSEC records again. // TBD: Need to purge and revalidate if the cached NSECS and the new set are not same. if (NSECRecords) { if (!AddNSECSForCacheRecord(m, NSECRecords, neg, rcode)) { - LogMsg("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s during refresh", CRDisplayString(m, neg)); + // We might just have an SOA record for zones that are not signed and hence don't log + // this as an error + LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s during refresh", CRDisplayString(m, neg)); FreeNSECRecords(m, NSECRecords); + neg->CRDNSSECQuestion = 0; } NSECRecords = mDNSNULL; } + if (SOARecord) + { + if (neg->soa) + ReleaseCacheRecord(m, neg->soa); + neg->soa = SOARecord; + SOARecord = mDNSNULL; + } } else while (1) { + CacheRecord *negcr; debugf("mDNSCoreReceiveNoUnicastAnswers making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype)); MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer); + m->rec.r.responseFlags = response->h.flags; + // We create SOA records above which might create new cache groups. Earlier + // in the function we looked up the cache group for the name and it could have + // been NULL. If we pass NULL cg to new cache entries that we create below, + // it will create additional cache groups for the same name. To avoid that, + // look up the cache group again to re-initialize cg again. + cg = CacheGroupForName(m, slot, hash, name); if (NSECRecords && DNSSECQuestion(qptr)) { - CacheRecord *negcr; // Create the cache entry with delay and then add the NSEC records // to it and add it immediately. negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL); - if (!AddNSECSForCacheRecord(m, NSECRecords, negcr, rcode)) + if (negcr) { - LogMsg("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s", CRDisplayString(m, negcr)); - FreeNSECRecords(m, NSECRecords); + negcr->CRDNSSECQuestion = 0; + if (!AddNSECSForCacheRecord(m, NSECRecords, negcr, rcode)) + { + LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord failed to add NSEC for negcr %s", + CRDisplayString(m, negcr)); + FreeNSECRecords(m, NSECRecords); + } + else + { + negcr->CRDNSSECQuestion = 1; + LogInfo("mDNSCoreReceiveNoUnicastAnswers: AddNSECSForCacheRecord added neg NSEC for %s", CRDisplayString(m, negcr)); + } + NSECRecords = mDNSNULL; + negcr->DelayDelivery = 0; + CacheRecordDeferredAdd(m, negcr); } - else LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord added neg NSEC for %s", CRDisplayString(m, negcr)); - NSECRecords = mDNSNULL; - negcr->DelayDelivery = 0; - CacheRecordDeferredAdd(m, negcr); m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it break; } else { - CreateNewCacheEntry(m, slot, cg, 0, mDNStrue, mDNSNULL); // We never need any delivery delay for these generated negative cache records + // Need to add with a delay so that we can tag the SOA record + negcr = CreateNewCacheEntry(m, slot, cg, 1, mDNStrue, mDNSNULL); + if (negcr) + { + negcr->CRDNSSECQuestion = 0; + if (DNSSECQuestion(qptr)) + negcr->CRDNSSECQuestion = 1; + negcr->DelayDelivery = 0; + + if (SOARecord) + { + if (negcr->soa) + ReleaseCacheRecord(m, negcr->soa); + negcr->soa = SOARecord; + SOARecord = mDNSNULL; + } + CacheRecordDeferredAdd(m, negcr); + } } + m->rec.r.responseFlags = zeroID; m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it if (!repeat) break; repeat--; @@ -7285,6 +8434,7 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * } } if (NSECRecords) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: NSECRecords not used"); FreeNSECRecords(m, NSECRecords); } + if (SOARecord) { LogInfo("mDNSCoreReceiveNoUnicastAnswers: SOARecord not used"); ReleaseCacheRecord(m, SOARecord); } } mDNSlocal void mDNSCorePrintStoredProxyRecords(mDNS *const m) @@ -7313,20 +8463,254 @@ mDNSlocal mDNSBool mDNSCoreRegisteredProxyRecord(mDNS *const m, AuthRecord *rr) return mDNSfalse; } -// Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change -// the record list and/or question list. -// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. -// InterfaceID non-NULL tells us the interface this multicast response was received on -// InterfaceID NULL tells us this was a unicast response -// dstaddr NULL tells us we received this over an outgoing TCP connection we made -mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, - const DNSMessage *const response, const mDNSu8 *end, - const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, - const mDNSInterfaceID InterfaceID) +mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage *const response, uDNS_LLQType LLQType, + const mDNSu32 slot, CacheGroup *cg, DNSQuestion *unicastQuestion, CacheRecord ***cfp, CacheRecord **NSECCachePtr, + mDNSInterfaceID InterfaceID) { - int i; + CacheRecord *rr; + CacheRecord **cflocal = *cfp; + + for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + { + mDNSBool match; + // Resource record received via unicast, the resGroupID should match ? + if (!InterfaceID) + { + mDNSu16 id1 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); + mDNSu16 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0); + match = (id1 == id2); + } + else + match = (rr->resrec.InterfaceID == InterfaceID); + // If we found this exact resource record, refresh its TTL + if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) + { + if (m->rec.r.resrec.rdlength > InlineCacheRDSize) + verbosedebugf("mDNSCoreReceiveCacheCheck: Found record size %5d interface %p already in cache: %s", + m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); + + if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list + if (rr->NextInCFList == mDNSNULL && *cfp != &rr->NextInCFList && LLQType != uDNS_LLQ_Events) + { + *cflocal = rr; + cflocal = &rr->NextInCFList; + *cflocal = (CacheRecord*)1; + *cfp = &rr->NextInCFList; + } + + // If this packet record is marked unique, and our previous cached copy was not, then fix it + if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) + { + DNSQuestion *q; + for (q = m->Questions; q; q=q->next) + { + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) + q->UniqueAnswers++; + } + rr->resrec.RecordType = m->rec.r.resrec.RecordType; + } + } + + if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS)) + { + // If the rdata of the packet record differs in name capitalization from the record in our cache + // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get + // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. + // mDNS -F returns the same domain multiple times with different casing + rr->resrec.rroriginalttl = 0; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change old: %s", CRDisplayString(m, rr)); + LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change new: %s", CRDisplayString(m, &m->rec.r)); + LogInfo("mDNSCoreReceiveCacheCheck: Discarding due to domainname case change in %d slot %3d in %d %d", + NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); + // DO NOT break out here -- we want to continue as if we never found it + } + else if (!IdenticalAnonInfo(m->rec.r.resrec.AnonInfo, rr->resrec.AnonInfo)) + { + // If the NSEC3 record changed, a few possibilities + // + // 1) the peer reinitialized e.g., after network change and still part of the + // same set. + // 2) the peer went to a different set but we did not see the goodbyes. If we just + // update the nsec3 record, it would be incorrect. Flush the cache so that we + // can deliver a RMV followed by ADD. + // 3) if the peer is ourselves and we see the goodbye when moving to a different set + // and so we flush the cache and create a new cache record with the new set information. + // Now we move back to the original set. In this case, we can't just update the + // NSEC3 record alone. We need to flush so that we can deliver an RMV followed by ADD + // when we create the new cache entry. + // + // Note: For case (1), we could avoid flushing the cache but we can't tell the difference + // from the other cases. + rr->resrec.rroriginalttl = 0; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + LogInfo("mDNSCoreReceiveCacheCheck: AnonInfo changed for %s", CRDisplayString(m, rr)); + // DO NOT break out here -- we want to continue as if we never found it. When we return + // from this function, we will create a new cache entry with the new NSEC3 record + } + else if (m->rec.r.resrec.rroriginalttl > 0) + { + DNSQuestion *q; + + m->mDNSStats.CacheRefreshed++; + + if (rr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, rr)); + RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); + rr->responseFlags = response->h.flags; + + // If we may have NSEC records returned with the answer (which we don't know yet as it + // has not been processed), we need to cache them along with the first cache + // record in the list that answers the question so that it can be used for validation + // later. The "type" check below is to make sure that we cache on the cache record + // that would answer the question. It is possible that we might cache additional things + // e.g., MX question might cache A records also, and we want to cache the NSEC on + // the record that answers the question. + if (response->h.numAnswers && unicastQuestion && unicastQuestion->qtype == rr->resrec.rrtype + && !(*NSECCachePtr)) + { + LogInfo("mDNSCoreReceiveCacheCheck: rescuing RR %s", CRDisplayString(m, rr)); + *NSECCachePtr = rr; + } + // We have to reset the question interval to MaxQuestionInterval so that we don't keep + // polling the network once we get a valid response back. For the first time when a new + // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. + // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server + // configuration changed, without flushing the cache, we reset the question interval here. + // Currently, we do this for for both multicast and unicast questions as long as the record + // type is unique. For unicast, resource record is always unique and for multicast it is + // true for records like A etc. but not for PTR. + if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) + { + for (q = m->Questions; q; q=q->next) + { + if (!q->DuplicateOf && !q->LongLived && + ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + ResetQuestionState(m, q); + debugf("mDNSCoreReceiveCacheCheck: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 + } + } + } + break; + } + else + { + + // If the packet TTL is zero, that means we're deleting this record. + // To give other hosts on the network a chance to protest, we push the deletion + // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. + // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent + // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. + // If record's current expiry time is more than a second from now, we set it to expire in one second. + // If the record is already going to expire in less than one second anyway, we leave it alone -- + // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. + debugf("DE for %s", CRDisplayString(m, rr)); + if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) + { + rr->resrec.rroriginalttl = 1; + rr->TimeRcvd = m->timenow; + rr->UnansweredQueries = MaxUnansweredQueries; + SetNextCacheCheckTimeForRecord(m, rr); + } + break; + } + } + } + return rr; +} + +mDNSlocal void mDNSParseNSEC3Records(mDNS *const m, const DNSMessage *const response, const mDNSu8 *end, + const mDNSInterfaceID InterfaceID, CacheRecord **NSEC3Records) +{ + const mDNSu8 *ptr = response->data; + CacheRecord *rr; + int i; + + if (!response->h.numAuthorities) + return; + ptr = LocateAuthorities(response, end); + if (!ptr) + { + LogInfo("mDNSParseNSEC3Records: ERROR can't locate authorities"); + return; + } + for (i = 0; i < response->h.numAuthorities && ptr && ptr < end; i++) + { + mDNSu32 slot; + CacheGroup *cg; + + ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); + if (!ptr || m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative || m->rec.r.resrec.rrtype != kDNSType_NSEC3) + { + debugf("mDNSParseNSEC3Records: ptr %p, Record %s, ignoring", ptr, CRDisplayString(m, &m->rec.r)); + m->rec.r.resrec.RecordType = 0; + continue; + } + slot = HashSlot(m->rec.r.resrec.name); + cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); + // Create the cache entry but don't add it to the cache it. We need + // to cache this along with the main cache record. + rr = CreateNewCacheEntry(m, slot, cg, 0, mDNSfalse, mDNSNULL); + if (rr) + { + debugf("mDNSParseNSEC3Records: %s", CRDisplayString(m, rr)); + *NSEC3Records = rr; + NSEC3Records = &rr->next; + } + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + } +} + +mDNSlocal void mDNSCoreResetRecord(mDNS *const m) +{ + m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + if (m->rec.r.resrec.AnonInfo) + { + FreeAnonInfo(m->rec.r.resrec.AnonInfo); + m->rec.r.resrec.AnonInfo = mDNSNULL; + } +} + +#define DEVICE_INFO_RECORD_LABELS 4 + +// Determine if the record is an instance of _device-info._tcp.local. +mDNSlocal mDNSBool IsDeviceInfoRecord(const domainname *d) +{ + const domainname *afterInstance; + + if (CountLabels(d) != DEVICE_INFO_RECORD_LABELS) + return mDNSfalse; + + // skip the instance name + afterInstance = SkipLeadingLabels(d, 1); + if (SameDomainName(afterInstance, &LocalDeviceInfoName)) + return mDNStrue; + + return mDNSfalse; +} + +// Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change +// the record list and/or question list. +// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this. +// InterfaceID non-NULL tells us the interface this multicast response was received on +// InterfaceID NULL tells us this was a unicast response +// dstaddr NULL tells us we received this over an outgoing TCP connection we made +mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, + const DNSMessage *const response, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, + const mDNSInterfaceID InterfaceID) +{ + int i; + mDNSBool myself; mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr); - mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); + mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, &myself); DNSQuestion *llqMatch = mDNSNULL; DNSQuestion *unicastQuestion = mDNSNULL; uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch); @@ -7339,8 +8723,13 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, CacheRecord *NSECRecords = mDNSNULL; CacheRecord *NSECCachePtr = mDNSNULL; CacheRecord **nsecp = &NSECRecords; + CacheRecord *McastNSEC3Records = mDNSNULL; mDNSBool nseclist; mDNSu8 rcode = '\0'; + mDNSBool rrsigsCreated = mDNSfalse; + mDNSBool DNSSECQuestion = mDNSfalse; + mDNSBool recordAccepted = mDNSfalse; + NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID); // All records in a DNS response packet are treated as equally valid statements of truth. If we want // to guard against spoof responses, then the only credible protection against that is cryptographic @@ -7411,7 +8800,19 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // decisions later on in this function const mDNSu32 slot = HashSlot(&q.qname); CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname); - if (!mDNSOpaque16IsZero(response->h.id)) unicastQuestion = qptr; + if (!mDNSOpaque16IsZero(response->h.id)) + { + unicastQuestion = qptr; + if (qptr->qDNSServer && DNSSECQuestion(qptr)) + { + LogInfo("mDNSCoreReceiveResponse: Setting aware for %##s (%s) on %#a", qptr->qname.c, + DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr); + qptr->qDNSServer->DNSSECAware = mDNStrue; + qptr->qDNSServer->req_DO = mDNStrue; + } + if (qptr->ValidatingResponse) + DNSSECQuestion = mDNStrue; + } for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) if (SameNameRecordAnswersQuestion(&rr->resrec, qptr)) { @@ -7420,14 +8821,36 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm rr->TimeRcvd = m->timenow - TicksTTL(rr) - 1; rr->UnansweredQueries = MaxUnansweredQueries; + rr->CRDNSSECQuestion = 0; + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + { + LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for record %s, question %##s (%s)", CRDisplayString(m, rr), + unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype)); + rr->CRDNSSECQuestion = 1; + } } } else { if (qptr) { - LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); - PenalizeDNSServer(m, qptr); + // If we recv any error from the DNSServer for a DNSSEC Query and if we know that the server + // is not DNSSEC aware, stop doing DNSSEC for that DNSServer. Note that by setting the + // req_DO to false here, the next retransmission for this question will turn off validation + // and hence retransmit without the EDNS0/DOK option. + if (DNSSECOptionalQuestion(qptr) && qptr->qDNSServer && !qptr->qDNSServer->DNSSECAware) + { + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to DNSSEC Query %##s (%s), clear DO flag", + qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); + qptr->qDNSServer->req_DO = mDNSfalse; + } + // For Unicast DNS Queries, penalize the DNSServer + else + { + LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", + qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype)); + PenalizeDNSServer(m, qptr, response->h.flags); + } } returnEarly = mDNStrue; } @@ -7443,8 +8866,18 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // generate negative cache entries (we want to query the next server) return; } + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + { + BumpDNSSECStats(m, kStatsActionSet, kStatsTypeMsgSize, (end - response->data)); + } } + // Parse the NSEC3 records from the Authority section before we process + // the Answer section so that we can cache them along with the proper + // cache records we create. + if (mDNSOpaque16IsZero(response->h.id)) + mDNSParseNSEC3Records(m, response, end, InterfaceID, &McastNSEC3Records); + for (i = 0; i < totalrecords && ptr && ptr < end; i++) { // All responses sent via LL multicast are acceptable for caching @@ -7458,10 +8891,26 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd; ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec); if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting - if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) { m->rec.r.resrec.RecordType = 0; continue; } + if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) + { + mDNSCoreResetRecord(m); + continue; + } + + // We have already parsed the NSEC3 records and cached them approrpriately for + // multicast responses. + if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype == kDNSType_NSEC3) + { + mDNSCoreResetRecord(m); + continue; + } // Don't want to cache OPT or TSIG pseudo-RRs - if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { m->rec.r.resrec.RecordType = 0; continue; } + if (m->rec.r.resrec.rrtype == kDNSType_TSIG) + { + mDNSCoreResetRecord(m); + continue; + } if (m->rec.r.resrec.rrtype == kDNSType_OPT) { const rdataOPT *opt; @@ -7474,15 +8923,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords); ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords); } - m->rec.r.resrec.RecordType = 0; + mDNSCoreResetRecord(m); continue; } - // if a CNAME record points to itself, then don't add it to the cache if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name)) { LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c); - m->rec.r.resrec.RecordType = 0; + mDNSCoreResetRecord(m); continue; } @@ -7505,6 +8953,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // has already matched the question using the 64 bit Id in the packet and we use that here. if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer; + + // If this is a DNSSEC question that is also LongLived, don't accept records from the + // Additional/Authority section blindly. We need to go through IsAcceptableResponse below + // so that NSEC/NSEC3 record are cached in the nseclist if we accept them. This can happen + // for both negative responses and wildcard expanded positive responses as both of come + // back with NSEC/NSEC3s. + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + AcceptableResponse = mDNSfalse; } else if (!AcceptableResponse || !dstaddr) { @@ -7513,36 +8969,69 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that // we create. - DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + if (!mDNSOpaque16IsZero(response->h.id)) + { + DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); - // Intialize the DNS server on the resource record which will now filter what questions we answer with - // this record. - // - // We could potentially lookup the DNS server based on the source address, but that may not work always - // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came - // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based - // on the "id" and "source port", then this response answers the question and assume the response - // came from the same DNS server that we sent the query to. + // Initialize the DNS server on the resource record which will now filter what questions we answer with + // this record. + // + // We could potentially lookup the DNS server based on the source address, but that may not work always + // and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came + // from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based + // on the "id" and "source port", then this response answers the question and assume the response + // came from the same DNS server that we sent the query to. - if (q != mDNSNULL) - { - AcceptableResponse = mDNStrue; - if (!InterfaceID) + if (q != mDNSNULL) { - debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); - m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; + AcceptableResponse = mDNStrue; + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; + } + else + LogInfo("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); } - } - else - { - // If we can't find a matching question, we need to see whether we have seen records earlier that matched - // the question. The code below does that. So, make this record unacceptable for now - if (!InterfaceID) + else { - debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); - AcceptableResponse = mDNSfalse; + // If we can't find a matching question, we need to see whether we have seen records earlier that matched + // the question. The code below does that. So, make this record unacceptable for now + if (!InterfaceID) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } } } + else if (ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) + { + recordAccepted = mDNStrue; + AcceptableResponse = mDNStrue; + LogInfo("mDNSCoreReceiveResponse: Accepting record in response to QU question %s, InterfaceID %p", CRDisplayString(m, &m->rec.r), + InterfaceID); + } + else if (IsDeviceInfoRecord(m->rec.r.resrec.name)) + { + recordAccepted = mDNStrue; + AcceptableResponse = mDNStrue; + LogInfo("mDNSCoreReceiveResponse: Accepting _device-info record %s, InterfaceID %p", + CRDisplayString(m, &m->rec.r), InterfaceID); + } + } + } + else if (llintf && llintf->IgnoreIPv4LL && m->rec.r.resrec.rrtype == kDNSType_A) + { + CacheRecord *const rr = &m->rec.r; + RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data; + + // If we are supposed to ignore link-local addresses on this interface, drop + // all "A" records that have link-local address in them. + if (mDNSv4AddressIsLinkLocal(&rdb->ipv4)) + { + LogInfo("mDNSResponder: Dropping LinkLocal packet %s", CRDisplayString(m, &m->rec.r)); + mDNSCoreResetRecord(m); + continue; } } @@ -7621,6 +9110,8 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, if (!mDNSCoreRegisteredProxyRecord(m, rr)) { LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); + + m->mDNSStats.NameConflicts++; mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); } } @@ -7632,6 +9123,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique) { LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); + m->mDNSStats.KnownUniqueNameConflicts++; mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); } else @@ -7664,127 +9156,54 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { const mDNSu32 slot = HashSlot(m->rec.r.resrec.name); CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec); - CacheRecord *rr; + CacheRecord *rr = mDNSNULL; - // 2a. Check if this packet resource record is already in our cache - for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next) + if (McastNSEC3Records) + InitializeAnonInfoForCR(m, &McastNSEC3Records, &m->rec.r); + + // 2a. Check if this packet resource record is already in our cache. + // + // If this record should go in the nseclist, don't look in the cache for updating it. + // They are supposed to be cached under the "nsec" field of the cache record for + // validation. Just create the cache record. + if (!nseclist) + { + rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, unicastQuestion, &cfp, &NSECCachePtr, InterfaceID); + } + + // If mDNSOppCaching is set (which affects only multicast), enable opportunistic caching in which case we cache + // everything that was received over multicast. Otherwise, we are selective about the caching. + // + // Cache everything that is from ourselves (that's how we answer any questions looking for them). Otherwise call + // ExpectingMulticastResponseForRecord which decides whether to cache this record or not. + // + if (!m->mDNSOppCaching && !rr && !myself && mDNSOpaque16IsZero(response->h.id)) { - mDNSBool match; - // Resource record received via unicast, the resGroupID should match ? - if (!InterfaceID) + if (!ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records)) { - mDNSu16 id1 = (rr->resrec.rDNSServer ? rr->resrec.rDNSServer->resGroupID : 0); - mDNSu16 id2 = (m->rec.r.resrec.rDNSServer ? m->rec.r.resrec.rDNSServer->resGroupID : 0); - match = (id1 == id2); + //LogMsg("mDNSCoreReceiveResponse: discarding %s", CRDisplayString(m, &m->rec.r)); + mDNSCoreResetRecord(m); + continue; } else - match = (rr->resrec.InterfaceID == InterfaceID); - // If we found this exact resource record, refresh its TTL - if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec)) { - if (m->rec.r.resrec.rdlength > InlineCacheRDSize) - verbosedebugf("Found record size %5d interface %p already in cache: %s", - m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r)); - - if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) - { - // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list - if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList && LLQType != uDNS_LLQ_Events) - { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } - - // If this packet record is marked unique, and our previous cached copy was not, then fix it - if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)) - { - DNSQuestion *q; - for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++; - rr->resrec.RecordType = m->rec.r.resrec.RecordType; - } - } - - if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS)) - { - // If the rdata of the packet record differs in name capitalization from the record in our cache - // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get - // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one. - // mDNS -F returns the same domain multiple times with different casing - rr->resrec.rroriginalttl = 0; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); - LogInfo("Discarding due to domainname case change old: %s", CRDisplayString(m,rr)); - LogInfo("Discarding due to domainname case change new: %s", CRDisplayString(m,&m->rec.r)); - LogInfo("Discarding due to domainname case change in %d slot %3d in %d %d", - NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow); - // DO NOT break out here -- we want to continue as if we never found it - } - else if (m->rec.r.resrec.rroriginalttl > 0) - { - DNSQuestion *q; - //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr)); - RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl); - - // If we may have NSEC records returned with the answer (which we don't know yet as it - // has not been processed), we need to cache them along with the first cache - // record in the list that answers the question so that it can be used for validation - // later. - if (response->h.numAnswers && unicastQuestion && !NSECCachePtr) - { - LogInfo("mDNSCoreReceiveResponse: rescuing RR %s", CRDisplayString(m, rr)); - NSECCachePtr = rr; - } - // We have to reset the question interval to MaxQuestionInterval so that we don't keep - // polling the network once we get a valid response back. For the first time when a new - // cache entry is created, AnswerCurrentQuestionWithResourceRecord does that. - // Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server - // configuration changed, without flushing the cache, we reset the question interval here. - // Currently, we do this for for both multicast and unicast questions as long as the record - // type is unique. For unicast, resource record is always unique and for multicast it is - // true for records like A etc. but not for PTR. - if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) - { - for (q = m->Questions; q; q=q->next) - { - if (!q->DuplicateOf && !q->LongLived && - ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) - { - ResetQuestionState(m, q); - debugf("mDNSCoreReceiveResponse: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - break; // Why break here? Aren't there other questions we might want to look at?-- SC July 2010 - } - } - } - break; - } - else - { - // If the packet TTL is zero, that means we're deleting this record. - // To give other hosts on the network a chance to protest, we push the deletion - // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries. - // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent - // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth. - // If record's current expiry time is more than a second from now, we set it to expire in one second. - // If the record is already going to expire in less than one second anyway, we leave it alone -- - // we don't want to let the goodbye packet *extend* the record's lifetime in our cache. - debugf("DE for %s", CRDisplayString(m, rr)); - if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond) - { - rr->resrec.rroriginalttl = 1; - rr->TimeRcvd = m->timenow; - rr->UnansweredQueries = MaxUnansweredQueries; - SetNextCacheCheckTimeForRecord(m, rr); - } - break; - } + recordAccepted = mDNStrue; } } + // If packet resource record not in our cache, add it now // (unless it is just a deletion of a record we never had, in which case we don't care) if (!rr && m->rec.r.resrec.rroriginalttl > 0) { const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events); - const mDNSs32 delay = AddToCFList ? NonZeroTime(m->timenow + mDNSPlatformOneSecond) : - CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot); + mDNSs32 delay; + + if (AddToCFList) + delay = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + else + delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot, mDNSNULL); + // If unique, assume we may have to delay delivery of this 'add' event. // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd() // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime() @@ -7792,20 +9211,57 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, rr = CreateNewCacheEntry(m, slot, cg, delay, !nseclist, srcaddr); if (rr) { - // NSEC Records and its signatures are cached with the negative cache entry + rr->responseFlags = response->h.flags; + // If we are not creating signatures, then we need to inform DNSSEC so that + // it does not wait forever. Don't do this if we got NSEC records + // as it indicates that this name does not exist. + if (rr->resrec.rrtype == kDNSType_RRSIG && !nseclist) + { + rrsigsCreated = mDNStrue; + } + // Remember whether we created a cache record in response to a DNSSEC question. + // This helps DNSSEC code not to reissue the question to fetch the DNSSEC records. + rr->CRDNSSECQuestion = 0; + if (unicastQuestion && DNSSECQuestion(unicastQuestion)) + { + LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for new record %s, question %##s (%s)", CRDisplayString(m, rr), + unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype)); + rr->CRDNSSECQuestion = 1; + } + // NSEC/NSEC3 records and its signatures are cached with the negative cache entry // which we should be creating below. It is also needed in the wildcard // expanded answer case and in that case it is cached along with the answer. - if (nseclist) { *nsecp = rr; nsecp = &rr->next; } - else if (AddToCFList) { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; } - else if (rr->DelayDelivery) ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery); + if (nseclist) + { + rr->TimeRcvd = m->timenow; + *nsecp = rr; + nsecp = &rr->next; + } + else if (AddToCFList) + { + *cfp = rr; + cfp = &rr->NextInCFList; + *cfp = (CacheRecord*)1; + } + else if (rr->DelayDelivery) + { + ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery); + } + } + } + else + { + if (rr && rr->resrec.AnonInfo && m->rec.r.resrec.AnonInfo) + { + CopyAnonInfoForCR(m, rr, &m->rec.r); } } } - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + mDNSCoreResetRecord(m); } exit: - m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it + mDNSCoreResetRecord(m); // If we've just received one or more records with their cache flush bits set, // then scan that cache slot to see if there are any old stale records we need to flush @@ -7950,13 +9406,13 @@ exit: // might start the verification process which needs these NSEC records if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) { - LogMsg("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); + LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); FreeNSECRecords(m, NSECRecords); } NSECRecords = mDNSNULL; NSECCachePtr = mDNSNULL; } - r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot); + r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot, mDNSNULL); // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1); else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery); @@ -7970,15 +9426,34 @@ exit: LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr)); if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) { - LogMsg("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); + LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); FreeNSECRecords(m, NSECRecords); } NSECRecords = mDNSNULL; NSECCachePtr = mDNSNULL; } + // If there is at least one answer and we did not create RRSIGs and there was a + // ValidatingResponse question waiting for this response, give a hint that no RRSIGs + // were created. We don't need to give a hint: + // + // - if we have no answers, the mDNSCoreReceiveNoUnicastAnswers below should + // generate a negative response + // + // - if we have NSECRecords, it means we might have a potential proof for + // non-existence of name that we are looking for + // + if (response->h.numAnswers && !rrsigsCreated && DNSSECQuestion && !NSECRecords) + mDNSCoreReceiveNoDNSSECAnswers(m, response, end, dstaddr, dstport, InterfaceID); + // See if we need to generate negative cache entries for unanswered unicast questions mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, LLQType, rcode, NSECRecords); + + if (McastNSEC3Records) + { + debugf("mDNSCoreReceiveResponse: McastNSEC3Records not used"); + FreeNSECRecords(m, McastNSEC3Records); + } } // ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing @@ -8044,6 +9519,37 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re } } +mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth) +{ + int i; + mDNSs8 hval = 0; + int colons = 0; + mDNSu8 val = 0; + + for (i = 0; ptr < limit && *ptr != ' ' && i < 17; i++, ptr++) + { + hval = HexVal(*ptr); + if (hval != -1) + { + val <<= 4; + val |= hval; + } + else if (*ptr == ':') + { + eth->b[colons] = val; + colons++; + val = 0; + } + } + if (colons != 5) + { + LogMsg("GetValueForMACAddr: Address malformed colons %d", colons); + return mDNSNULL; + } + eth->b[colons] = val; + return ptr; +} + mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6) { int hval; @@ -8151,12 +9657,11 @@ mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6 mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4) { - int i; mDNSu32 val; int dots = 0; - val = 0; - for (i = 0; ptr < limit && *ptr != ' '; ptr++) + + for ( ; ptr < limit && *ptr != ' '; ptr++) { if (*ptr >= '0' && *ptr <= '9') val = val * 10 + *ptr - '0'; @@ -8183,11 +9688,10 @@ mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4 mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *value) { - int i; mDNSu32 val; val = 0; - for (i = 0; ptr < limit && *ptr != ' '; ptr++) + for ( ; ptr < limit && *ptr != ' '; ptr++) { if (*ptr < '0' || *ptr > '9') { @@ -8205,7 +9709,7 @@ mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *valu return ptr; } -mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSu32 *seq, +mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, mDNSu32 *seq, mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win) { if (ar->resrec.rrtype != kDNSType_NULL) @@ -8216,13 +9720,11 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA int len = ar->resrec.rdlength; mDNSu8 *ptr = &ar->resrec.rdata->u.txt.c[1]; mDNSu8 *limit = ptr + len - 1; // Exclude the first byte that is the length - mDNSu32 value; + mDNSu32 value = 0; while (ptr < limit) { mDNSu8 param = *ptr; - mDNSu8 *p; - ptr += 2; // Skip the letter and the "=" if (param == 'h') { @@ -8244,13 +9746,16 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA raddr->type = mDNSAddrType_IPv6; ptr = GetValueForIPv6Addr(ptr, limit, &raddr->ip.v6); } + else if (param == 'm') + { + ptr = GetValueForMACAddr(ptr, limit, eth); + } else { ptr = GetValueForKeepalive(ptr, limit, &value); } if (!ptr) { LogMsg("mDNS_ExtractKeepaliveInfo: Cannot parse\n"); return; } - p = (mDNSu8 *)&value; // Extract everything in network order so that it is easy for sending a keepalive and also // for matching incoming TCP packets switch (param) @@ -8263,26 +9768,27 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA case 'H': case 'd': case 'D': + case 'm': + case 'i': + case 'c': break; case 'l': - lport->NotAnInteger = p[0] << 8 | p[1]; + lport->NotAnInteger = swap16((mDNSu16)value); break; case 'r': - rport->NotAnInteger = p[0] << 8 | p[1]; + rport->NotAnInteger = swap16((mDNSu16)value); break; case 's': - value = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; - *seq = value; + *seq = swap32(value); break; case 'a': - value = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; - *ack = value; + *ack = swap32(value); break; case 'w': - *win = p[0] << 8 | p[1]; + *win = swap16((mDNSu16)value); break; default: - LogMsg("mDNS_ExtractKeepaliveInfo: unknown value\n"); + LogMsg("mDNS_ExtractKeepaliveInfo: unknown value %c\n", param); ptr = limit; break; } @@ -8293,11 +9799,12 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA // Matches the proxied auth records to the incoming TCP packet and returns the match and its sequence and ack in "rseq" and "rack" so that // the clients need not retrieve this information from the auth record again. -mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr const* pladdr, const mDNSAddr const* praddr, const mDNSIPPort plport, +mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr* pladdr, const mDNSAddr* praddr, const mDNSIPPort plport, const mDNSIPPort prport, mDNSu32 *rseq, mDNSu32 *rack) { AuthRecord *ar; mDNSAddr laddr, raddr; + mDNSEthAddr eth; mDNSIPPort lport, rport; mDNSu32 timeout, seq, ack; mDNSu16 win; @@ -8311,7 +9818,7 @@ mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr cons if (!ar->WakeUp.HMAC.l[0]) continue; - mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win); + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); // Did we parse correctly ? if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) @@ -8344,6 +9851,7 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m) mDNSu32 timeout, seq, ack; mDNSu16 win; mDNSAddr laddr, raddr; + mDNSEthAddr eth; mDNSIPPort lport, rport; timeout = seq = ack = 0; @@ -8354,7 +9862,7 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m) if (!ar->WakeUp.HMAC.l[0]) continue; - mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, &seq, &ack, &lport, &rport, &win); + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) { @@ -8375,9 +9883,39 @@ mDNSlocal void mDNS_SendKeepalives(mDNS *const m) } } -mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, - const DNSMessage *const msg, const mDNSu8 *end, - const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, +mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar) +{ + if (ar != mDNSNULL) + { + LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL"); + return; + } + mDNSu32 timeout, seq, ack; + mDNSu16 win; + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + + timeout = seq = ack = 0; + win = 0; + + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + + mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win) + { + LogInfo("mDNS_SendKeepaliveACK: not a valid record %s for keepalive", ARDisplayString(m, ar)); + return; + } + LogMsg("mDNS_SendKeepaliveACK: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport)); + mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win); +} + +mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, + const DNSMessage *const msg, const mDNSu8 *end, + const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID) { int i; @@ -8452,6 +9990,11 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, updatelease = 0x40000000UL / mDNSPlatformOneSecond; ptr = LocateAuthorities(msg, end); + + // Clear any stale TCP keepalive records that may exist + ClearKeepaliveProxyRecords(m, &owner, m->DuplicateRecords, InterfaceID); + ClearKeepaliveProxyRecords(m, &owner, m->ResourceRecords, InterfaceID); + for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++) { ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec); @@ -8459,13 +10002,21 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, { mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec); AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem); - if (!ar) { m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; break; } + if (!ar) + { + m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; + break; + } else { mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared; m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet; - ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record - ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords); + // All stale keepalive records have been flushed prior to this loop. + if (!mDNS_KeepaliveRecord(&m->rec.r.resrec)) + { + ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record + ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords); + } mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar); AssignDomainName(&ar->namestorage, m->rec.r.resrec.name); ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse); @@ -8487,13 +10038,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, m->NextScheduledSPS = ar->TimeExpire; ar->KATimeExpire = 0; mDNS_Register_internal(m, ar); - // Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating, - // but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited - // Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower. - // Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage - // new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records. - if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6) - if (ar->resrec.rrtype == kDNSType_AAAA) ar->resrec.rroriginalttl = 0; + m->ProxyRecords++; mDNS_UpdateAllowSleep(m); LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar)); @@ -8524,7 +10069,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, mDNS_SendKeepalives(m); } -mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSInterfaceID InterfaceID) +mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID) { if (InterfaceID) { @@ -8577,6 +10122,12 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now m->CurrentRecord = rr->next; } + + // Update the dynamic store with the IP Address and MAC address of the sleep proxy + char *ifname = InterfaceNameForID(m, InterfaceID); + mDNSAddr spsaddr; + mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr)); + mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname); } // If we were waiting to go to sleep, then this SPS registration or wide-area record deletion // may have been the thing we were waiting for, so schedule another check to see if we can sleep now. @@ -8625,6 +10176,11 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr, #endif cr->NextInCFList = mDNSNULL; cr->nsec = mDNSNULL; + cr->soa = mDNSNULL; + cr->CRDNSSECQuestion = 0; + // Initialize to the basic one and the caller can set it to more + // specific based on the response if any + cr->responseFlags = ResponseFlags; } mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end, @@ -8657,7 +10213,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co if (mDNSSameIPPort(srcport, NATPMPPort)) { mDNS_Lock(m); - uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); + uDNS_ReceiveNATPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt)); mDNS_Unlock(m); return; } @@ -8688,6 +10244,21 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co mDNS_Lock(m); m->PktNum++; + if (mDNSOpaque16IsZero(msg->h.id)) + { + m->MPktNum++; +#if APPLE_OSX_mDNSResponder + // Track the number of multicast packets received from a source outside our subnet. + // Check the destination address to avoid accounting for spurious packets that + // comes in with message id zero. + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL) && + mDNSAddressIsAllDNSLinkGroup(dstaddr)) + { + m->RemoteSubnet++; + } +#endif // #if APPLE_OSX_mDNSResponder + } + #ifndef UNICAST_DISABLED if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR))) if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses @@ -8702,7 +10273,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid); else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID); - else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, InterfaceID); + else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, srcaddr, InterfaceID); else { LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)", @@ -8752,6 +10323,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co // The main reason for this design is that cache entries point to a *single* question and that question is responsible // for keeping the cache fresh as long as it is active. Having multiple active question for a single cache entry // breaks this design principle. +// // If IsLLQ(Q) is true, it means the question is both: // (a) long-lived and @@ -8773,9 +10345,12 @@ mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuest q->qclass == question->qclass && // class, IsLLQ(q) == IsLLQ(question) && // and long-lived status matches (!q->AuthInfo || question->AuthInfo) && // to avoid deadlock, don't make public query dup of a private one + (q->AnonInfo == question->AnonInfo) && // Anonymous query not a dup of normal query (q->SuppressQuery == question->SuppressQuery) && // Questions that are suppressed/not suppressed (q->ValidationRequired == question->ValidationRequired) && // Questions that require DNSSEC validation (q->ValidatingResponse == question->ValidatingResponse) && // Questions that are validating responses using DNSSEC + (q->DisallowPID == question->DisallowPID) && // Disallowing a PID should not affect a PID that is allowed + (q->BrowseThreshold == question->BrowseThreshold) && // browse thresholds must match q->qnamehash == question->qnamehash && SameDomainName(&q->qname, &question->qname)) // and name return(q); @@ -8825,6 +10400,11 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi q->triedAllServersOnce = question->triedAllServersOnce; q->TargetQID = question->TargetQID; + if (q->LocalSocket) + { + mDNSPlatformUDPClose(q->LocalSocket); + } + q->LocalSocket = question->LocalSocket; q->state = question->state; @@ -8833,8 +10413,6 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi q->expire = question->expire; q->ntries = question->ntries; q->id = question->id; - q->ValidationState = question->ValidationState; - q->ValidationStatus = question->ValidationStatus; question->LocalSocket = mDNSNULL; question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question @@ -8872,8 +10450,7 @@ mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout); - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); while (*p) // Check if we already have this {interface, domain} tuple registered { @@ -9034,10 +10611,49 @@ mDNSexport mDNSBool DomainEnumQuery(const domainname *qname) return mDNStrue; } +// Note: InterfaceID is the InterfaceID of the question +mDNSlocal mDNSBool DNSServerMatch(DNSServer *d, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID) +{ + // 1) Unscoped questions (NULL InterfaceID) should consider *only* unscoped DNSServers ( DNSServer + // with "scoped" set to kScopeNone) + // + // 2) Scoped questions (non-NULL InterfaceID) should consider *only* scoped DNSServers (DNSServer + // with "scoped" set to kScopeInterfaceId) and their InterfaceIDs should match. + // + // 3) Scoped questions (non-zero ServiceID) should consider *only* scoped DNSServers (DNSServer + // with "scoped" set to kScopeServiceID) and their ServiceIDs should match. + // + // The first condition in the "if" statement checks to see if both the question and the DNSServer are + // unscoped. The question is unscoped only if InterfaceID is zero and ServiceID is -1. + // + // If the first condition fails, following are the possible cases (the notes below are using + // InterfaceID for discussion and the same holds good for ServiceID): + // + // - DNSServer is not scoped, InterfaceID is not NULL - we should skip the current DNSServer entry + // as scoped questions should not pick non-scoped DNSServer entry (Refer to (2) above). + // + // - DNSServer is scoped, InterfaceID is NULL - we should skip the current DNSServer entry as + // unscoped question should not match scoped DNSServer (Refer to (1) above). The InterfaceID check + // would fail in this case. + // + // - DNSServer is scoped and InterfaceID is not NULL - the InterfaceID of the question and the DNSServer + // should match (Refer to (2) above). + // + // Note: mDNSInterface_Unicast is used only by .local unicast questions and are treated as unscoped. + // If a question is scoped both to InterfaceID and ServiceID, the question will be scoped to InterfaceID. + + if (((d->scoped == kScopeNone) && ((!InterfaceID && ServiceID == -1) || InterfaceID == mDNSInterface_Unicast)) || + ((d->scoped == kScopeInterfaceID) && d->interface == InterfaceID) || + ((d->scoped == kScopeServiceID) && d->serviceID == ServiceID)) + { + return mDNStrue; + } + return mDNSfalse; +} + // Sets all the Valid DNS servers for a question mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) { - DNSServer *curmatch = mDNSNULL; int bestmatchlen = -1, namecount = CountLabels(&question->qname); DNSServer *curr; int bettermatch, currcount; @@ -9052,7 +10668,10 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped); // skip servers that will soon be deleted if (curr->flags & DNSServer_FlagDelete) - { debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } + { + debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); + continue; + } // This happens normally when you unplug the interface where we reset the interfaceID to mDNSInterface_Any for all // the DNS servers whose scope match the interfaceID. Few seconds later, we also receive the updated DNS configuration. @@ -9063,12 +10682,13 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout if (curr->scoped && curr->interface == mDNSInterface_Any) - { debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; } + { + debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); + continue; + } currcount = CountLabels(&curr->domain); - if ((!DEQuery || !curr->cellIntf) && - ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || - (curr->interface == question->InterfaceID))) + if ((!DEQuery || !curr->cellIntf) && DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) { bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); @@ -9078,14 +10698,19 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) // bit if ((bettermatch == 1) || (bettermatch == 0)) { - curmatch = curr; bestmatchlen = currcount; - if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; } + if (bettermatch) + { + debugf("SetValidDNSServers: Resetting all the bits"); + question->validDNSServers = zeroOpaque64; + timeout = 0; + } debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d," " Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout, curr->interface); timeout += curr->timeout; - if (DEQuery) debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf); + if (DEQuery) + debugf("DomainEnumQuery: Question %##s, DNSServer %#a, cell %d", question->qname.c, &curr->addr, curr->cellIntf); bit_set_opaque64(question->validDNSServers, index); } } @@ -9095,13 +10720,15 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) debugf("SetValidDNSServers: ValidDNSServer bits 0x%x%x for question %p %##s (%s)", question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype)); - // If there are no matching resolvers, then use the default value to timeout - return (question->ValidatingResponse ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT); + // If there are no matching resolvers, then use the default timeout value. + // For ProxyQuestion, shorten the timeout so that dig does not timeout on us in case of no response. + return ((question->ProxyQuestion || question->ValidatingResponse) ? DEFAULT_UDNSSEC_TIMEOUT : timeout ? timeout : DEFAULT_UDNS_TIMEOUT); } // Get the Best server that matches a name. If you find penalized servers, look for the one // that will come out of the penalty box soon -mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSOpaque64 validBits, int *selected, mDNSBool nameMatch) +mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID, mDNSOpaque64 validBits, + int *selected, mDNSBool nameMatch) { DNSServer *curmatch = mDNSNULL; int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0; @@ -9117,10 +10744,18 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac { // skip servers that will soon be deleted if (curr->flags & DNSServer_FlagDelete) - { debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; } + { + debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); + continue; + } // Check if this is a valid DNSServer - if (!bit_get_opaque64(validBits, index)) { debugf("GetBestServer: continuing for index %d", index); index++; continue; } + if (!bit_get_opaque64(validBits, index)) + { + debugf("GetBestServer: continuing for index %d", index); + index++; + continue; + } currcount = CountLabels(&curr->domain); currPenaltyTime = PenaltyTimeForServer(m, curr); @@ -9133,24 +10768,9 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac // the least penalized one. BetterMatchForName walks through all best matches and // "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server // in the list when there are no penalized servers and least one among them - // when there are some penalized servers - // - // Notes on InterfaceID matching: - // - // 1) A DNSServer entry may have an InterfaceID but the scoped flag may not be set. This - // is the old way of specifying an InterfaceID option for DNSServer. We recoginize these - // entries by "scoped" being false. These are like any other unscoped entries except that - // if it is picked e.g., domain match, when the packet is sent out later, the packet will - // be sent out on that interface. Theese entries can be matched by either specifying a - // zero InterfaceID or non-zero InterfaceID on the question. Specifying an InterfaceID on - // the question will cause an extra check on matching the InterfaceID on the question - // against the DNSServer. - // - // 2) A DNSServer may also have both scoped set and InterfaceID non-NULL. This - // is the new way of specifying an InterfaceID option for DNSServer. These will be considered - // only when the question has non-zero interfaceID. + // when there are some penalized servers. - if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID)) + if (DNSServerMatch(curr, InterfaceID, ServiceID)) { // If we know that all the names are already equally good matches, then skip calling BetterMatchForName. @@ -9158,15 +10778,22 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac // Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive // part and still do some redundant steps e.g., InterfaceID match - if (nameMatch) bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen); - else bettermatch = 0; + if (nameMatch) + bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen); + else + bettermatch = 0; // If we found a better match (bettermatch == 1) then we don't need to // compare penalty times. But if we found an equal match, then we compare // the penalty times to pick a better match if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime)) - { currindex = index; curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime; } + { + currindex = index; + curmatch = curr; + bestmatchlen = currcount; + bestPenaltyTime = currPenaltyTime; + } } index++; } @@ -9175,7 +10802,7 @@ mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfac } // Look up a DNS Server, matching by name and InterfaceID -mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID) +mDNSlocal DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSs32 ServiceID) { DNSServer *curmatch = mDNSNULL; char *ifname = mDNSNULL; // for logging purposes only @@ -9189,7 +10816,7 @@ mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInte // By passing in all ones, we make sure that every DNS server is considered allValid.l[0] = allValid.l[1] = 0xFFFFFFFF; - curmatch = GetBestServer(m, name, InterfaceID, allValid, mDNSNULL, mDNStrue); + 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, @@ -9213,20 +10840,28 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly)) InterfaceID = mDNSNULL; - if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID); + if (InterfaceID) + ifname = InterfaceNameForID(m, InterfaceID); if (!mDNSOpaque64IsZero(&question->validDNSServers)) { - curmatch = GetBestServer(m, name, InterfaceID, question->validDNSServers, &currindex, mDNSfalse); - if (currindex != -1) bit_clr_opaque64(question->validDNSServers, currindex); + curmatch = GetBestServer(m, name, InterfaceID, question->ServiceID, question->validDNSServers, &currindex, mDNSfalse); + if (currindex != -1) + bit_clr_opaque64(question->validDNSServers, currindex); } if (curmatch != mDNSNULL) - LogInfo("GetServerForQuestion: %p DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s (%s)", question, &curmatch->addr, - mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None", - InterfaceID, name, DNSTypeName(question->qtype)); + { + LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) found for name %##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) found for name %##s (%s)", question, ifname ? ifname : "None", InterfaceID, name, DNSTypeName(question->qtype)); + { + LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) found for name %##s (%s)", + question, ifname ? ifname : "None", InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype)); + } return(curmatch); } @@ -9239,9 +10874,9 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question) mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) { DNSQuestion *q; - (void)n; // Unused mDNS_Lock(m); LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result); + n->clientContext = mDNSNULL; // we received at least one callback since starting this NAT-T for (q = m->Questions; q; q=q->next) if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) startLLQHandshake(m, q); // If ExternalPort is zero, will do StartLLQPolling instead @@ -9251,80 +10886,147 @@ mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n) mDNS_Unlock(m); } -mDNSlocal mDNSBool IsAutoTunnelAddress(mDNS *const m, const mDNSv6Addr a) +mDNSlocal mDNSBool IsPrivateDomain(mDNS *const m, DNSQuestion *q) { - DomainAuthInfo *ai = mDNSNULL; - - if (mDNSSameIPv6Address(a, m->AutoTunnelRelayAddr)) + DomainAuthInfo *AuthInfo; + // Skip Private domains as we have special addresses to get the hosts in the Private domain + AuthInfo = GetAuthInfoForName_internal(m, &q->qname); + if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel) + { + debugf("IsPrivateDomain: %##s true", q->qname.c); return mDNStrue; - - for (ai = m->AuthInfoList; ai; ai = ai->next) + } + else { - if (!ai->deltime && ai->AutoTunnel && mDNSSameIPv6Address(a, ai->AutoTunnelInnerAddress)) - { - return mDNStrue; - } + debugf("IsPrivateDomain: %##s false", q->qname.c); + return mDNSfalse; } - - return mDNSfalse; } -mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16 qtype, mDNSInterfaceID InterfaceID) +// This function takes the DNSServer as a separate argument because sometimes the +// caller has not yet assigned the DNSServer, but wants to evaluate the SuppressQuery +// status before switching to it. +mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNSServer *d) { - NetworkInterfaceInfo *i; - mDNSs32 iptype; - DomainAuthInfo *AuthInfo; + // 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)); + return mDNSfalse; + } - if (qtype == kDNSType_A) iptype = mDNSAddrType_IPv4; - else if (qtype == kDNSType_AAAA) iptype = mDNSAddrType_IPv6; - else { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", qname, DNSTypeName(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)); + return mDNSfalse; + } - // We still want the ability to be able to listen to the local services and hence - // don't fail .local requests. We always have a loopback interface which we don't - // check here. - if (InterfaceID != mDNSInterface_Unicast && IsLocalDomain(qname)) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", qname, DNSTypeName(qtype)); return mDNSfalse; } + if (!d) + { + LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, as the DNS server is NULL", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + + // Check if the DNS Configuration allows A/AAAA queries to be sent + if ((q->qtype == kDNSType_A) && (d->req_A)) + { + LogInfo("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, + DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port)); + return mDNSfalse; + } - // Skip Private domains as we have special addresses to get the hosts in the Private domain - AuthInfo = GetAuthInfoForName_internal(m, qname); - if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel) - { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Private Domain", qname, DNSTypeName(qtype)); return mDNSfalse; } + LogInfo("ShouldSuppressUnicastQuery: Query suppressed for %##s, qtype %s, since DNS Configuration does not allow (req_A is %s and req_AAAA is %s)", + q->qname.c, DNSTypeName(q->qtype), d->req_A ? "true" : "false", d->req_AAAA ? "true" : "false"); - // Match on Type, Address and InterfaceID + return mDNStrue; +} + +mDNSlocal mDNSBool ShouldSuppressDotLocalQuery(mDNS *const m, DNSQuestion *q) +{ + NetworkInterfaceInfo *intf; + AuthRecord *rr; + mDNSBool ret; + + // Check to see if there is at least one interface other than loopback and don't suppress + // .local questions if you find one. If we have at least one interface, it means that + // we can send unicast queries for the .local name and we don't want to suppress + // multicast in that case as upper layers don't know how to handle if we return a + // negative response for multicast followed by a positive response for unicast. // - // Check whether we are looking for a name that ends in .local, then presence of a link-local - // address on the interface is sufficient. - for (i = m->HostInterfaces; i; i = i->next) + // Note: we used to check for multicast capable interfaces instead of just any interface + // present. That did not work in the case where we have a valid interface for unicast + // but not multicast capable e.g., cellular, as we ended up delivering a negative response + // first and the upper layer did not wait for the positive response that came later. + for (intf = m->HostInterfaces; intf; intf = intf->next) { - if (i->ip.type != iptype) continue; + if (intf->InterfaceActive && !intf->Loopback) + { + LogInfo("ShouldSuppressDotLocalQuery: Found interface %s, not suppressing", intf->ifname); + return mDNSfalse; + } + } - if (!InterfaceID || (InterfaceID == mDNSInterface_LocalOnly) || (InterfaceID == mDNSInterface_P2P) || - (InterfaceID == mDNSInterface_Unicast) || (i->InterfaceID == InterfaceID)) + // 1. If we find a LocalOnly or P2P record answering this question, then don't suppress it. + // Set m->CurrentQuestion as it is required by AnswerQuestionWithLORecord. + m->CurrentQuestion = q; + ret = AnswerQuestionWithLORecord(m, q, mDNStrue); + m->CurrentQuestion = mDNSNULL; + + if (ret) + { + LogInfo("ShouldSuppressDotLocalQuery: Found LocalOnly record for %##s (%s), not suppressing", q->qname.c, + DNSTypeName(q->qtype)); + return mDNSfalse; + } + + // 2. If we find a local AuthRecord answering this question, then don't suppress it. + for (rr = m->ResourceRecords; rr; rr = rr->next) + { + if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { - if (iptype == mDNSAddrType_IPv4 && !mDNSv4AddressIsLoopback(&i->ip.ip.v4) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v4)) - { - LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.4a found", qname, DNSTypeName(qtype), - &i->ip.ip.v4); - if (m->SleepState == SleepState_Sleeping) - LogInfo("ShouldSuppressQuery: Would have returned true earlier"); - return mDNSfalse; - } - else if (iptype == mDNSAddrType_IPv6 && - !mDNSv6AddressIsLoopback(&i->ip.ip.v6) && - !mDNSv6AddressIsLinkLocal(&i->ip.ip.v6) && - !IsAutoTunnelAddress(m, i->ip.ip.v6)) - { - LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.16a found", qname, DNSTypeName(qtype), - &i->ip.ip.v6); - if (m->SleepState == SleepState_Sleeping) - LogInfo("ShouldSuppressQuery: Would have returned true earlier"); - return mDNSfalse; - } + LogInfo("ShouldSuppressDotLocalQuery: Found resource record %s for %##s (%s) not suppressing", ARDisplayString(m, rr), + q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; } } - LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, because no matching interface found", qname, DNSTypeName(qtype)); return mDNStrue; } +mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, DNSQuestion *q) +{ + if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + + // We still want the ability to be able to listen to the local services and hence + // don't fail .local query if we have local records that can potentially answer + // the question. + if (q->InterfaceID != mDNSInterface_Unicast && IsLocalDomain(&q->qname)) + { + if (!ShouldSuppressDotLocalQuery(m, q)) + { + LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + else + { + LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, Local question", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; + } + } + + return (ShouldSuppressUnicastQuery(m, q, q->qDNSServer)); +} + mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q) { CacheRecord *rr; @@ -9408,7 +11110,7 @@ mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q { for (rr = ag->members; rr; rr=rr->next) // Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME - if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) + if (UniqueLocalOnlyRecord(rr) && LocalOnlyRecordAnswersQuestion(rr, q)) { LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s", ARDisplayString(m, rr)); @@ -9448,6 +11150,65 @@ mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q return mDNStrue; } +mDNSlocal void SuppressStatusChanged(mDNS *const m, DNSQuestion *q, DNSQuestion **restart) +{ + // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero + // LOddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before + // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers) + if (q->SuppressQuery) + { + q->SuppressQuery = mDNSfalse; + if (!CacheRecordRmvEventsForQuestion(m, q)) + { + LogInfo("SuppressStatusChanged: Question deleted while delivering RMV events from cache"); + return; + } + q->SuppressQuery = mDNStrue; + } + + // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts) + // and SuppressQuery status does not mean anything for these questions. As we are going to stop the + // question below, we need to deliver the RMV events so that the ADDs that will be delivered during + // the restart will not be a duplicate ADD + if (!LocalRecordRmvEventsForQuestion(m, q)) + { + LogInfo("SuppressStatusChanged: Question deleted while delivering RMV events from Local AuthRecords"); + return; + } + + // There are two cases here. + // + // 1. Previously it was suppressed and now it is not suppressed, restart the question so + // that it will start as a new question. Note that we can't just call ActivateUnicastQuery + // because when we get the response, if we had entries in the cache already, it will not answer + // this question if the cache entry did not change. Hence, we need to restart + // the query so that it can be answered from the cache. + // + // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions + // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question + // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false). + // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed + // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an + // immediate response and not want to be blocked behind a question that is querying DNS servers. When + // the question is not suppressed, we don't want two active questions sending packets on the wire. + // This affects both efficiency and also the current design where there is only one active question + // pointed to from a cache entry. + // + // We restart queries in a two step process by first calling stop and build a temporary list which we + // will restart at the end. The main reason for the two step process is to handle duplicate questions. + // If there are duplicate questions, calling stop inherits the values from another question on the list (which + // will soon become the real question) including q->ThisQInterval which might be zero if it was + // suppressed before. At the end when we have restarted all questions, none of them is active as each + // inherits from one another and we need to reactivate one of the questions here which is a little hacky. + // + // It is much cleaner and less error prone to build a list of questions and restart at the end. + + LogInfo("SuppressStatusChanged: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StopQuery_internal(m, q); + q->next = *restart; + *restart = q; +} + // The caller should hold the lock mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) { @@ -9473,304 +11234,480 @@ mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m) { q = m->RestartQuestion; m->RestartQuestion = q->next; - if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable) + if (q->SuppressUnusable) { mDNSBool old = q->SuppressQuery; - q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID); + q->SuppressQuery = ShouldSuppressQuery(m, q); if (q->SuppressQuery != old) { - // NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero - // LOddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before - // LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers) + // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before + // followed by a negative cache response. Temporarily turn off suppression so that + // AnswerCurrentQuestionWithResourceRecord can answer the question + SuppressStatusChanged(m, q, &restart); + } + } + } + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } +} - if (q->SuppressQuery) - { - // Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before - // followed by a negative cache response. Temporarily turn off suppression so that - // AnswerCurrentQuestionWithResourceRecord can answer the question - q->SuppressQuery = mDNSfalse; - if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; } - q->SuppressQuery = mDNStrue; - } +mDNSlocal void RestartUnicastQuestions(mDNS *const m) +{ + DNSQuestion *q; + DNSQuestion *restart = mDNSNULL; + + if (m->RestartQuestion) + LogMsg("RestartUnicastQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)", + m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype)); + m->RestartQuestion = m->Questions; + while (m->RestartQuestion) + { + q = m->RestartQuestion; + m->RestartQuestion = q->next; + if (q->Restart) + { + if (mDNSOpaque16IsZero(q->TargetQID)) + LogMsg("RestartUnicastQuestions: ERROR!! Restart set for multicast question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + + q->Restart = 0; + SuppressStatusChanged(m, q, &restart); + } + } + while (restart) + { + q = restart; + restart = restart->next; + q->next = mDNSNULL; + LogInfo("RestartUnicastQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + mDNS_StartQuery_internal(m, q); + } +} - // SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts) - // and SuppressQuery status does not mean anything for these questions. As we are going to stop the - // question below, we need to deliver the RMV events so that the ADDs that will be delivered during - // the restart will not be a duplicate ADD - if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; } - // There are two cases here. - // - // 1. Previously it was suppressed and now it is not suppressed, restart the question so - // that it will start as a new question. Note that we can't just call ActivateUnicastQuery - // because when we get the response, if we had entries in the cache already, it will not answer - // this question if the cache entry did not change. Hence, we need to restart - // the query so that it can be answered from the cache. - // - // 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions - // so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question - // is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false). - // A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed - // (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an - // immediate response and not want to be blocked behind a question that is querying DNS servers. When - // the question is not suppressed, we don't want two active questions sending packets on the wire. - // This affects both efficiency and also the current design where there is only one active question - // pointed to from a cache entry. - // - // We restart queries in a two step process by first calling stop and build a temporary list which we - // will restart at the end. The main reason for the two step process is to handle duplicate questions. - // If there are duplicate questions, calling stop inherits the values from another question on the list (which - // will soon become the real question) including q->ThisQInterval which might be zero if it was - // suppressed before. At the end when we have restarted all questions, none of them is active as each - // inherits from one another and we need to reactivate one of the questions here which is a little hacky. - // - // It is much cleaner and less error prone to build a list of questions and restart at the end. +// ValidateParameters() is called by mDNS_StartQuery_internal() to check the client parameters of +// DNS Question that are already set by the client before calling mDNS_StartQuery() +mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) +{ - LogInfo("CheckSuppressUnusableQuestions: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - mDNS_StopQuery_internal(m, q); - q->next = restart; - restart = q; - } + if (question->Target.type && !ValidQuestionTarget(question)) + { + LogMsg("ValidateParameters: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)", + question->Target.type, mDNSVal16(question->TargetPort), question->qname.c); + question->Target.type = mDNSAddrType_None; + } + + // If no question->Target specified, clear TargetPort + if (!question->Target.type) + question->TargetPort = zeroIPPort; + + if (!ValidateDomainName(&question->qname)) + { + LogMsg("ValidateParameters: Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return(mStatus_Invalid); + } + + // If this question is referencing a specific interface, verify it exists + if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P) + { + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); + if (!intf) + LogMsg("ValidateParameters: Note: InterfaceID %p for question %##s (%s) not currently found in active interface list", + question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); + } + + return(mStatus_NoError); +} + +// InitDNSConfig() is called by InitCommonState() to initialize the DNS configuration of the Question. +// These are a subset of the internal uDNS fields. Must be done before ShouldSuppressQuery() & mDNS_PurgeForQuestion() +mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) +{ + // First reset all DNS Configuration + question->qDNSServer = mDNSNULL; + question->validDNSServers = zeroOpaque64; + question->triedAllServersOnce = 0; + question->noServerResponse = 0; + question->StopTime = 0; + + // Need not initialize the DNS Configuration for Local Only OR P2P Questions + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + return; + // Proceed to initialize DNS Configuration (some are set in SetValidDNSServers()) + if (!mDNSOpaque16IsZero(question->TargetQID)) + { + mDNSu32 timeout = SetValidDNSServers(m, question); + // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have + // a networking change/search domain change that calls this function again we keep + // reinitializing the timeout value which means it may never timeout. If this becomes + // a common case in the future, we can easily fix this by adding extra state that + // indicates that we have already set the StopTime. + // + // Note that we set the timeout for all questions. If this turns out to be a duplicate, + // it gets a full timeout value even if the original question times out earlier. + if (question->TimeoutQuestion) + { + question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); + LogInfo("InitDNSConfig: Setting StopTime on question %p %##s (%s)", question, question->qname.c, DNSTypeName(question->qtype)); } + + question->qDNSServer = GetServerForQuestion(m, question); + LogInfo("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)); + } + else + { + if (question->TimeoutQuestion) + question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); + } + // Set StopTime here since it is a part of DNS Configuration + if (question->StopTime) + SetNextQueryStopTime(m, question); + // SetNextQueryTime() need not be initialized for LocalOnly OR P2P Questions since those questions + // will never be transmitted on the wire. Hence we call SetNextQueryTime() here. + SetNextQueryTime(m,question); +} + +// InitCommonState() is called by mDNS_StartQuery_internal() to initialize the common(uDNS/mDNS) internal +// state fields of the DNS Question. These are independent of the Client layer. +mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) +{ + mDNSBool purge; + int i; + + // Note: In the case where we already have the answer to this question in our cache, that may be all the client + // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would + // be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval). + // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate + // that to go out immediately. + question->next = mDNSNULL; + // ThisQInterval should be initialized before any memory allocations occur. If malloc + // debugging is turned on within mDNSResponder (see mDNSDebug.h for details) it validates + // the question list to check if ThisQInterval is negative which means the question has been + // stopped and can't be on the list. The question is already on the list and ThisQInterval + // can be negative if the caller just stopped it and starting it again. Hence, it always has to + // be initialized. CheckForSoonToExpireRecords below prints the cache records when logging is + // turned ON which can allocate memory e.g., base64 encoding, in the case of DNSSEC. + question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question + question->qnamehash = DomainNameHashValue(&question->qname); + question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname), &purge); + question->LastQTime = m->timenow; + question->ExpectUnicastResp = 0; + question->LastAnswerPktNum = m->PktNum; + question->RecentAnswerPkts = 0; + question->CurrentAnswers = 0; + +#if APPLE_OSX_mDNSResponder + +// Initial browse threshold used by Finder. +#define mDNSFinderBrowseThreshold 20 + + // Set the threshold at which we move to a passive browse state, + // not actively sending queries. + if (question->flags & kDNSServiceFlagsThresholdOne) + question->BrowseThreshold = 1; + else if (question->flags & kDNSServiceFlagsThresholdFinder) + question->BrowseThreshold = mDNSFinderBrowseThreshold; + else + question->BrowseThreshold = 0; + +#else // APPLE_OSX_mDNSResponder + question->BrowseThreshold = 0; +#endif // APPLE_OSX_mDNSResponder + question->CachedAnswerNeedsUpdate = mDNSfalse; + + question->LargeAnswers = 0; + question->UniqueAnswers = 0; + question->LOAddressAnswers = 0; + question->FlappingInterface1 = mDNSNULL; + question->FlappingInterface2 = mDNSNULL; + + question->ServiceID = mDNSPlatformGetServiceID(m, question); + + InitDNSConfig(m, question); + + question->AuthInfo = GetAuthInfoForQuestion(m, question); + question->SuppressQuery = 0; + if (question->SuppressUnusable) + question->SuppressQuery = ShouldSuppressQuery(m, question); + + // If ServiceID is 0 or the policy disallows making DNS requests, + // set DisallowPID + question->DisallowPID = (question->ServiceID == 0 || (mDNSPlatformAllowPID(m, question) == 0)); + if (question->DisallowPID) + { + LogInfo("InitCommonState: Query suppressed for %##s (%s), PID %d/ServiceID %d not allowed", question->qname.c, + DNSTypeName(question->qtype), question->pid, question->ServiceID); + } + + question->NextInDQList = mDNSNULL; + question->SendQNow = mDNSNULL; + question->SendOnAll = mDNSfalse; + +#if mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE + +#if APPLE_OSX_mDNSResponder + // Request unicast response for first 4 queries to increase + // reliability in an environment with high multicast packet loss. + // Must set to one more than the number of unicast queries you want, since SendQueries() + // decrements it before calling BuildQuestion() which acts on it. + if (question->flags & kDNSServiceFlagsUnicastResponse) + { + question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; + LogInfo("InitCommonState: setting RequestUnicast = %d for %##s (%s)", question->RequestUnicast, question->qname.c, + DNSTypeName(question->qtype)); + } + else if (question->flags & kDNSServiceFlagsThresholdFinder) + { + // always send one request with QU bit set when kDNSServiceFlagsThresholdFinder is set +#if mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + question->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE + + LogInfo("InitCommonState: kDNSServiceFlagsThresholdFinder set, setting RequestUnicast = %d for %##s (%s)", + question->RequestUnicast, question->qname.c, DNSTypeName(question->qtype)); + } +#endif // APPLE_OSX_mDNSResponder + + question->LastQTxTime = m->timenow; + question->CNAMEReferrals = 0; + + question->WakeOnResolveCount = 0; + if (question->WakeOnResolve) + { + question->WakeOnResolveCount = InitialWakeOnResolveCount; + purge = mDNStrue; + } + + for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; + + question->Restart = 0; + + debugf("InitCommonState: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)", + question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow, + NextQSendTime(question) - m->timenow, + question->DelayAnswering ? question->DelayAnswering - m->timenow : 0, + question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf); + + if (question->DelayAnswering) + LogInfo("InitCommonState: Delaying answering for %d ticks while cache stabilizes for %##s (%s)", + question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype)); + + return(purge); +} + +// Excludes the DNS Config fields which are already handled by InitDNSConfig() +mDNSlocal void InitWABState(DNSQuestion *const question) +{ + // We'll create our question->LocalSocket on demand, if needed. + // We won't need one for duplicate questions, or from questions answered immediately out of the cache. + // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single + // NAT mapping for receiving inbound add/remove events. + question->LocalSocket = mDNSNULL; + question->unansweredQueries = 0; + question->nta = mDNSNULL; + question->servAddr = zeroAddr; + question->servPort = zeroIPPort; + question->tcp = mDNSNULL; + question->NoAnswer = NoAnswer_Normal; +} + +mDNSlocal void InitLLQNATState(mDNS *const m) +{ + // If we don't have our NAT mapping active, start it now + if (!m->LLQNAT.clientCallback) + { + m->LLQNAT.Protocol = NATOp_MapUDP; + m->LLQNAT.IntPort = m->UnicastPort4; + m->LLQNAT.RequestedPort = m->UnicastPort4; + m->LLQNAT.clientCallback = LLQNATCallback; + m->LLQNAT.clientContext = (void*)1; // Means LLQ NAT Traversal just started + mDNS_StartNATOperation_internal(m, &m->LLQNAT); + } +} + +mDNSlocal void InitLLQState(DNSQuestion *const question) +{ + question->state = LLQ_InitialRequest; + question->ReqLease = 0; + question->expire = 0; + question->ntries = 0; + question->id = zeroOpaque64; +} + +// InitDNSSECProxyState() is called by mDNS_StartQuery_internal() to initialize +// DNSSEC & DNS Proxy fields of the DNS Question. +mDNSlocal void InitDNSSECProxyState(mDNS *const m, DNSQuestion *const question) +{ + (void) m; + + // DNS server selection affects DNSSEC. Turn off validation if req_DO is not set + // or the request is going over cellular interface. + // + // Note: This needs to be done here before we call FindDuplicateQuestion as it looks + // at ValidationRequired setting also. + if (question->qDNSServer) + { + if (question->qDNSServer->cellIntf) + { + LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); going over cell", question->qname.c, DNSTypeName(question->qtype)); + question->ValidationRequired = mDNSfalse; + } + if (DNSSECOptionalQuestion(question) && !(question->qDNSServer->req_DO)) + { + LogInfo("InitDNSSECProxyState: Turning off validation for %##s (%s); req_DO false", + question->qname.c, DNSTypeName(question->qtype)); + question->ValidationRequired = DNSSEC_VALIDATION_NONE; + } + } + question->ValidationState = (question->ValidationRequired ? DNSSECValRequired : DNSSECValNotRequired); + question->ValidationStatus = 0; + question->responseFlags = zeroID; +} + +// Once the question is completely initialized including the duplicate logic, this function +// is called to finalize the unicast question which requires flushing the cache if needed, +// activating the query etc. +mDNSlocal void FinalizeUnicastQuestion(mDNS *const m, DNSQuestion *question, mDNSBool purge) +{ + // Ensure DNS related info of duplicate question is same as the orig question + if (question->DuplicateOf) + { + question->validDNSServers = question->DuplicateOf->validDNSServers; + question->qDNSServer = question->DuplicateOf->qDNSServer; + LogInfo("FinalizeUnicastQuestion: Duplicate question %p (%p) %##s (%s), DNS Server %#a:%d", + question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), + question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, + mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); + } + + ActivateUnicastQuery(m, question, mDNSfalse); + + // If purge was set above, flush the cache. Need to do this after we set the + // DNS server on the question + if (purge) + { + question->DelayAnswering = 0; + mDNS_PurgeForQuestion(m, question); } - while (restart) + else if (!question->DuplicateOf && DNSSECQuestion(question)) { - q = restart; - restart = restart->next; - q->next = mDNSNULL; - LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - mDNS_StartQuery_internal(m, q); + // For DNSSEC questions, we need to have the RRSIGs also for verification. + CheckForDNSSECRecords(m, question); + } + if (question->LongLived) + { + // Unlike other initializations, InitLLQNATState should be done after + // we determine that it is a unicast question. LongLived is set for + // both multicast and unicast browse questions but we should initialize + // the LLQ NAT state only for unicast. Otherwise we will unnecessarily + // start the NAT traversal that is not needed. + InitLLQNATState(m); +#if APPLE_OSX_mDNSResponder + UpdateAutoTunnelDomainStatuses(m); +#endif } } mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question) { - if (question->Target.type && !ValidQuestionTarget(question)) - { - LogMsg("mDNS_StartQuery_internal: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)", - question->Target.type, mDNSVal16(question->TargetPort), question->qname.c); - question->Target.type = mDNSAddrType_None; - } + DNSQuestion **q; + mStatus vStatus; + mDNSBool purge; - if (!question->Target.type) question->TargetPort = zeroIPPort; // If no question->Target specified clear TargetPort + // First check for cache space (can't do queries if there is no cache space allocated) + if (m->rrcache_size == 0) + return(mStatus_NoCache); + vStatus = ValidateParameters(m, question); + if (vStatus) + return(vStatus); + question->TargetQID = #ifndef UNICAST_DISABLED - (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : + (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : #endif // UNICAST_DISABLED - zeroID; - - debugf("mDNS_StartQuery: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + zeroID; + debugf("mDNS_StartQuery_internal: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + + // Note: It important that new questions are appended at the *end* of the list, not prepended at the start + q = &m->Questions; + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + q = &m->LocalOnlyQuestions; + while (*q && *q != question) + q=&(*q)->next; - if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated - return(mStatus_NoCache); - else + if (*q) { - int i; - DNSQuestion **q; - - if (!ValidateDomainName(&question->qname)) - { - LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - return(mStatus_Invalid); - } - - // Note: It important that new questions are appended at the *end* of the list, not prepended at the start - q = &m->Questions; - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) q = &m->LocalOnlyQuestions; - while (*q && *q != question) q=&(*q)->next; - - if (*q) - { - LogMsg("Error! Tried to add a question %##s (%s) %p that's already in the active list", - question->qname.c, DNSTypeName(question->qtype), question); - return(mStatus_AlreadyRegistered); - } - - *q = question; - - // If this question is referencing a specific interface, verify it exists - if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P) - { - NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); - if (!intf) - LogMsg("Note: InterfaceID %p for question %##s (%s) not currently found in active interface list", - question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); - } - - // Note: In the case where we already have the answer to this question in our cache, that may be all the client - // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would - // be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval). - // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate - // that to go out immediately. - question->next = mDNSNULL; - question->qnamehash = DomainNameHashValue(&question->qname); // MUST do this before FindDuplicateQuestion() - question->DelayAnswering = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname)); - question->LastQTime = m->timenow; - question->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question - question->ExpectUnicastResp = 0; - question->LastAnswerPktNum = m->PktNum; - question->RecentAnswerPkts = 0; - question->CurrentAnswers = 0; - question->LargeAnswers = 0; - question->UniqueAnswers = 0; - question->LOAddressAnswers = 0; - question->FlappingInterface1 = mDNSNULL; - question->FlappingInterface2 = mDNSNULL; - // Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion() - question->AuthInfo = GetAuthInfoForQuestion(m, question); - if (question->SuppressUnusable) - question->SuppressQuery = ShouldSuppressQuery(m, &question->qname, question->qtype, question->InterfaceID); - else - question->SuppressQuery = 0; - question->DuplicateOf = FindDuplicateQuestion(m, question); - question->NextInDQList = mDNSNULL; - question->SendQNow = mDNSNULL; - question->SendOnAll = mDNSfalse; - question->RequestUnicast = 0; - question->LastQTxTime = m->timenow; - question->CNAMEReferrals = 0; - - // We'll create our question->LocalSocket on demand, if needed. - // We won't need one for duplicate questions, or from questions answered immediately out of the cache. - // We also don't need one for LLQs because (when we're using NAT) we want them all to share a single - // NAT mapping for receiving inbound add/remove events. - question->LocalSocket = mDNSNULL; - question->qDNSServer = mDNSNULL; - question->unansweredQueries = 0; - question->nta = mDNSNULL; - question->servAddr = zeroAddr; - question->servPort = zeroIPPort; - question->tcp = mDNSNULL; - question->NoAnswer = NoAnswer_Normal; - - question->state = LLQ_InitialRequest; - question->ReqLease = 0; - question->expire = 0; - question->ntries = 0; - question->id = zeroOpaque64; - question->validDNSServers = zeroOpaque64; - question->triedAllServersOnce = 0; - question->noServerResponse = 0; - question->StopTime = 0; - if (question->WakeOnResolve) - { - question->WakeOnResolveCount = InitialWakeOnResolveCount; - mDNS_PurgeBeforeResolve(m, question); - } - else - question->WakeOnResolveCount = 0; - - question->ValidationState = (question->ValidationRequired ? DNSSECValRequired : DNSSECValNotRequired); - question->ValidationStatus = 0; - + LogMsg("mDNS_StartQuery_internal: Error! Tried to add a question %##s (%s) %p that's already in the active list", + question->qname.c, DNSTypeName(question->qtype), question); + return(mStatus_AlreadyRegistered); + } + *q = question; + - if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo; + // Intialize the question. The only ordering constraint we have today is that + // InitDNSSECProxyState should be called after the DNS server is selected (in + // InitCommonState -> InitDNSConfig) as DNS server selection affects DNSSEC + // validation. - for (i=0; iDupSuppress[i].InterfaceID = mDNSNULL; + purge = InitCommonState(m, question); + InitWABState(question); + InitLLQState(question); + InitDNSSECProxyState(m, question); - debugf("mDNS_StartQuery: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)", - question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow, - NextQSendTime(question) - m->timenow, - question->DelayAnswering ? question->DelayAnswering - m->timenow : 0, - question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf); + // FindDuplicateQuestion should be called last after all the intialization + // as the duplicate logic could be potentially based on any field in the + // question. + question->DuplicateOf = FindDuplicateQuestion(m, question); + if (question->DuplicateOf) + question->AuthInfo = question->DuplicateOf->AuthInfo; - if (question->DelayAnswering) - LogInfo("mDNS_StartQuery_internal: Delaying answering for %d ticks while cache stabilizes for %##s (%s)", - question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype)); + if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + { + if (!m->NewLocalOnlyQuestions) + m->NewLocalOnlyQuestions = question; + } + else + { + if (!m->NewQuestions) + m->NewQuestions = question; - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + // If the question's id is non-zero, then it's Wide Area + // MUST NOT do this Wide Area setup until near the end of + // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA, + // NS, etc.) and if we haven't finished setting up our own question and setting + // m->NewQuestions if necessary then we could end up recursively re-entering + // this routine with the question list data structures in an inconsistent state. + if (!mDNSOpaque16IsZero(question->TargetQID)) { - if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; + FinalizeUnicastQuestion(m, question, purge); } else { - if (!m->NewQuestions) m->NewQuestions = question; - - // If the question's id is non-zero, then it's Wide Area - // MUST NOT do this Wide Area setup until near the end of - // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA, - // NS, etc.) and if we haven't finished setting up our own question and setting - // m->NewQuestions if necessary then we could end up recursively re-entering - // this routine with the question list data structures in an inconsistent state. - if (!mDNSOpaque16IsZero(question->TargetQID)) - { - // Duplicate questions should have the same DNSServers so that when we find - // a matching resource record, all of them get the answers. Calling GetServerForQuestion - // for the duplicate question may get a different DNS server from the original question - mDNSu32 timeout = SetValidDNSServers(m, question); - // We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have - // a networking change/search domain change that calls this function again we keep - // reinitializing the timeout value which means it may never timeout. If this becomes - // a common case in the future, we can easily fix this by adding extra state that - // indicates that we have already set the StopTime. - if (question->TimeoutQuestion) - question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond); - if (question->DuplicateOf) - { - question->validDNSServers = question->DuplicateOf->validDNSServers; - question->qDNSServer = question->DuplicateOf->qDNSServer; - LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), Timeout %d, DNS Server %#a:%d", - question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), timeout, - question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL, - mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); - } - else - { - question->qDNSServer = GetServerForQuestion(m, question); - LogInfo("mDNS_StartQuery_internal: 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)); - } - // If we are talking to a server on the local host, unsupress the query. This happens if we have - // a DNS server running locally while we don't have any interfaces UP. - // - // TBD: Re-organise the code so that we can move this logic to ShouldSuppressQuery - if (question->SuppressQuery && question->qDNSServer && mDNSAddressIsLoopback(&question->qDNSServer->addr)) - { - LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) unsuppressed due to local DNS Server %#a:%d", - question, question->qname.c, DNSTypeName(question->qtype), &question->qDNSServer->addr, - mDNSVal16(question->qDNSServer->port)); - question->SuppressQuery = 0; - } - ActivateUnicastQuery(m, question, mDNSfalse); - - // If there is a negative cache entry for this question and if it does - // not have cached nsecs, then we can't validate possibly. Hence, flush - // them so that we can reissue the question again with EDNS0/DO bit set. - if (!question->DuplicateOf && DNSSECQuestion(question)) - mDNS_CheckForCachedNSECS(m, question); - - // If long-lived query, and we don't have our NAT mapping active, start it now - if (question->LongLived && !m->LLQNAT.clientContext) - { - m->LLQNAT.Protocol = NATOp_MapUDP; - m->LLQNAT.IntPort = m->UnicastPort4; - m->LLQNAT.RequestedPort = m->UnicastPort4; - m->LLQNAT.clientCallback = LLQNATCallback; - m->LLQNAT.clientContext = (void*)1; // Means LLQ NAT Traversal is active - mDNS_StartNATOperation_internal(m, &m->LLQNAT); - } - -#if APPLE_OSX_mDNSResponder - if (question->LongLived) - UpdateAutoTunnelDomainStatuses(m); -#endif - - } - else + if (purge) { - if (question->TimeoutQuestion) - question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); + LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c); + mDNS_PurgeForQuestion(m, question); } - if (question->StopTime) SetNextQueryStopTime(m, question); - SetNextQueryTime(m,question); } - - return(mStatus_NoError); } + + return(mStatus_NoError); } // CancelGetZoneData is an internal routine (i.e. must be called with the lock already held) @@ -9891,13 +11828,15 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break; if (!q) { - if (!m->LLQNAT.clientContext) // Should never happen, but just in case... - LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL"); + if (!m->LLQNAT.clientCallback) // Should never happen, but just in case... + { + LogMsg("mDNS_StopQuery ERROR LLQNAT.clientCallback NULL"); + } else { LogInfo("Stopping LLQNAT"); mDNS_StopNATOperation_internal(m, &m->LLQNAT); - m->LLQNAT.clientContext = mDNSNULL; // Means LLQ NAT Traversal not running + m->LLQNAT.clientCallback = mDNSNULL; // Means LLQ NAT Traversal not running } } @@ -9923,6 +11862,18 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que // wait until we send the refresh above which needs the nta if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; } + if (question->ValidationRequired && question->DNSSECAuthInfo) + { + LogInfo("mDNS_StopQuery_internal: freeing DNSSECAuthInfo %##s", question->qname.c); + question->DAIFreeCallback(m, question->DNSSECAuthInfo); + question->DNSSECAuthInfo = mDNSNULL; + } + if (question->AnonInfo) + { + FreeAnonInfo(question->AnonInfo); + question->AnonInfo = mDNSNULL; + } + return(mStatus_NoError); } @@ -10001,7 +11952,7 @@ mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const question, const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSu32 flags, + const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags, mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, mDNSQuestionCallback *Callback, void *Context) { @@ -10020,26 +11971,37 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu question->RetryWithSearchDomains = mDNSfalse; question->TimeoutQuestion = 0; question->WakeOnResolve = 0; - question->UseBrackgroundTrafficClass = useBackgroundTrafficClass; + question->UseBackgroundTrafficClass = useBackgroundTrafficClass; question->ValidationRequired = 0; question->ValidatingResponse = 0; + question->ProxyQuestion = 0; question->qnameOrig = mDNSNULL; + question->AnonInfo = mDNSNULL; question->QuestionCallback = Callback; question->QuestionContext = Context; - if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr); + + if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) + return(mStatus_BadParamErr); + + if (anondata) + { + question->AnonInfo = AllocateAnonInfo(&question->qname, anondata, mDNSPlatformStrLen(anondata), mDNSNULL); + if (!question->AnonInfo) + return(mStatus_BadParamErr); + } return(mDNS_StartQuery_internal(m, question)); } mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, const domainname *const srv, const domainname *const domain, - const mDNSInterfaceID InterfaceID, mDNSu32 flags, + const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags, mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, mDNSQuestionCallback *Callback, void *Context) { mStatus status; mDNS_Lock(m); - status = mDNS_StartBrowse_internal(m, question, srv, domain, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context); + status = mDNS_StartBrowse_internal(m, question, srv, domain, anondata, InterfaceID, flags, ForceMCast, useBackgroundTrafficClass, Callback, Context); mDNS_Unlock(m); return(status); } @@ -10205,10 +12167,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qSRV.RetryWithSearchDomains = mDNSfalse; query->qSRV.TimeoutQuestion = 0; query->qSRV.WakeOnResolve = 0; - query->qSRV.UseBrackgroundTrafficClass = mDNSfalse; + query->qSRV.UseBackgroundTrafficClass = mDNSfalse; query->qSRV.ValidationRequired = 0; query->qSRV.ValidatingResponse = 0; + query->qSRV.ProxyQuestion = 0; query->qSRV.qnameOrig = mDNSNULL; + query->qSRV.AnonInfo = mDNSNULL; query->qSRV.QuestionCallback = FoundServiceInfoSRV; query->qSRV.QuestionContext = query; @@ -10229,10 +12193,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qTXT.RetryWithSearchDomains = mDNSfalse; query->qTXT.TimeoutQuestion = 0; query->qTXT.WakeOnResolve = 0; - query->qTXT.UseBrackgroundTrafficClass = mDNSfalse; + query->qTXT.UseBackgroundTrafficClass = mDNSfalse; query->qTXT.ValidationRequired = 0; query->qTXT.ValidatingResponse = 0; + query->qTXT.ProxyQuestion = 0; query->qTXT.qnameOrig = mDNSNULL; + query->qTXT.AnonInfo = mDNSNULL; query->qTXT.QuestionCallback = FoundServiceInfoTXT; query->qTXT.QuestionContext = query; @@ -10253,10 +12219,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv4.RetryWithSearchDomains = mDNSfalse; query->qAv4.TimeoutQuestion = 0; query->qAv4.WakeOnResolve = 0; - query->qAv4.UseBrackgroundTrafficClass = mDNSfalse; + query->qAv4.UseBackgroundTrafficClass = mDNSfalse; query->qAv4.ValidationRequired = 0; query->qAv4.ValidatingResponse = 0; + query->qAv4.ProxyQuestion = 0; query->qAv4.qnameOrig = mDNSNULL; + query->qAv4.AnonInfo = mDNSNULL; query->qAv4.QuestionCallback = FoundServiceInfo; query->qAv4.QuestionContext = query; @@ -10276,10 +12244,12 @@ mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, query->qAv6.AppendSearchDomains = 0; query->qAv6.RetryWithSearchDomains = mDNSfalse; query->qAv6.TimeoutQuestion = 0; - query->qAv6.UseBrackgroundTrafficClass = mDNSfalse; + query->qAv6.UseBackgroundTrafficClass = mDNSfalse; query->qAv6.ValidationRequired = 0; query->qAv6.ValidatingResponse = 0; + query->qAv6.ProxyQuestion = 0; query->qAv6.qnameOrig = mDNSNULL; + query->qAv6.AnonInfo = mDNSNULL; query->qAv6.QuestionCallback = FoundServiceInfo; query->qAv6.QuestionContext = query; @@ -10336,10 +12306,13 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->RetryWithSearchDomains = mDNSfalse; question->TimeoutQuestion = 0; question->WakeOnResolve = 0; - question->UseBrackgroundTrafficClass = mDNSfalse; + question->UseBackgroundTrafficClass = mDNSfalse; question->ValidationRequired = 0; question->ValidatingResponse = 0; + question->ProxyQuestion = 0; question->qnameOrig = mDNSNULL; + question->AnonInfo = mDNSNULL; + question->pid = mDNSPlatformGetPID(); question->QuestionCallback = Callback; question->QuestionContext = Context; if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); @@ -10455,7 +12428,22 @@ mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m) mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) { char buffer[MAX_REVERSE_MAPPING_NAME]; - NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m); + NetworkInterfaceInfo *primary; + + if (!set->McastTxRx) + { + LogInfo("AdvertiseInterface: Returning, not multicast capable %s", set->ifname); + return; + } +#if TARGET_OS_EMBEDDED + if (!m->AutoTargetServices) + { + LogInfo("AdvertiseInterface: Returning due to AutoTargetServices zero for %s", set->ifname); + return; + } +#endif + + primary = FindFirstAdvertisedInterface(m); if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary // Send dynamic update for non-linklocal IPv4 Addresses @@ -10501,6 +12489,10 @@ mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) set->RR_A.RRSet = &primary->RR_A; // May refer to self +#if APPLE_OSX_mDNSResponder + D2D_start_advertising_interface(set); +#endif // APPLE_OSX_mDNSResponder + mDNS_Register_internal(m, &set->RR_A); mDNS_Register_internal(m, &set->RR_PTR); @@ -10540,6 +12532,41 @@ mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) if (set->RR_A.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A, mDNS_Dereg_normal); if (set->RR_PTR.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR, mDNS_Dereg_normal); if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal); + +#if APPLE_OSX_mDNSResponder + D2D_stop_advertising_interface(set); +#endif // APPLE_OSX_mDNSResponder + +} + +mDNSlocal void AdvertiseAllInterfaceRecords(mDNS *const m) +{ + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->Advertise) + { + LogInfo("AdvertiseInterface: Advertising for ifname %s", intf->ifname); + AdvertiseInterface(m, intf); + } + } +} + +mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) +{ +#if TARGET_OS_EMBEDDED + NetworkInterfaceInfo *intf; + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->Advertise) + { + LogInfo("DeadvertiseInterface: Deadvertising for ifname %s", intf->ifname); + DeadvertiseInterface(m, intf); + } + } +#else + (void) m; //unused +#endif } mDNSexport void mDNS_SetFQDN(mDNS *const m) @@ -10654,6 +12681,12 @@ mDNSlocal void RestartRecordGetZoneData(mDNS * const m) mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set) { int i; + // We initialize ThisQInterval to -1 indicating that the question has not been started + // yet. If the question (browse) is started later during interface registration, it will + // be stopped during interface deregistration. We can't sanity check to see if the + // question has been stopped or not before initializing it to -1 because we need to + // initialize it to -1 the very first time. + set->NetWakeBrowse.ThisQInterval = -1; for (i=0; i<3; i++) { @@ -10673,7 +12706,7 @@ mDNSexport void mDNS_ActivateNetWake_internal(mDNS *const m, NetworkInterfaceInf if (set->InterfaceActive) { LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip); - mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set); + mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, mDNSNULL, set->InterfaceID, 0, mDNSfalse, mDNSfalse, m->SPSBrowseCallback, set); } } @@ -10683,6 +12716,10 @@ mDNSexport void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceI while (p && p != set) p=p->next; if (!p) { LogMsg("mDNS_DeactivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; } + // Note: We start the browse only if the interface is NetWake capable and we use this to + // stop the resolves also. Hence, the resolves should not be started without the browse + // being started i.e, resolves should not happen unless NetWake capable which is + // guaranteed by BeginSleepProcessing. if (set->NetWakeBrowse.ThisQInterval >= 0) { int i; @@ -10791,7 +12828,11 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s // See mDNS: m->SuppressSending set too enthusiastically if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval); - if (flapping) LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + if (flapping) + { + LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + m->mDNSStats.InterfaceUpFlap++; + } LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); if (m->SuppressProbes == 0 || @@ -10803,16 +12844,13 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s // us to reconnect to the network. If we do this as part of the wake up code, it is possible // that the network link comes UP after 60 seconds and we never set the OWNER option m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); - - m->ClearSPSRecords = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond); - - // Clear the flag that ignores IPv6 neighbor advertisements after 2 seconds. - m->clearIgnoreNA = NonZeroTime(m->timenow + 2 * mDNSPlatformOneSecond); - LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner"); + m->mDNSStats.InterfaceUp++; for (q = m->Questions; q; q=q->next) // Scan our list of questions + { if (mDNSOpaque16IsZero(q->TargetQID)) + { if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface, { // then reactivate this question // If flapping, delay between first and second queries is nine seconds instead of one second @@ -10824,24 +12862,41 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s if (!q->ThisQInterval || q->ThisQInterval > initial) { q->ThisQInterval = initial; - q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it + +#if mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; +#else // mDNS_REQUEST_UNICAST_RESPONSE + q->RequestUnicast = SET_QU_IN_FIRST_QUERY; +#endif // mDNS_REQUEST_UNICAST_RESPONSE + } q->LastQTime = m->timenow - q->ThisQInterval + qdelay; q->RecentAnswerPkts = 0; + // Change the salt + ReInitAnonInfo(&q->AnonInfo, &q->qname); SetNextQueryTime(m,q); } + } + } // For all our non-specific authoritative resource records (and any dormant records specific to this interface) // we now need them to re-probe if necessary, and then re-announce. for (rr = m->ResourceRecords; rr; rr=rr->next) + { if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID) - mDNSCoreRestartRegistration(m, rr, numannounce); + { + // Change the salt + ReInitAnonInfo(&rr->resrec.AnonInfo, rr->resrec.name); + mDNSCoreRestartRegistration(m, rr, numannounce); + } + } +#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE + DNSSECProbe(m); +#endif } RestartRecordGetZoneData(m); - CheckSuppressUnusableQuestions(m); - mDNS_UpdateAllowSleep(m); mDNS_Unlock(m); @@ -10903,13 +12958,17 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se CacheGroup *cg; CacheRecord *rr; DNSQuestion *q; - DNSServer *s; LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;" " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip); + m->mDNSStats.InterfaceDown++; + if (set->McastTxRx && flapping) + { LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip); + m->mDNSStats.InterfaceDownFlap++; + } // 1. Deactivate any questions specific to this interface, and tag appropriate questions // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them @@ -10945,14 +13004,6 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se } } } - - // 3. Any DNS servers specific to this interface are now unusable - for (s = m->DNSServers; s; s = s->next) - if (s->interface == set->InterfaceID) - { - s->interface = mDNSInterface_Any; - s->teststate = DNSServer_Disabled; - } } } @@ -10973,13 +13024,57 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface); } - CheckSuppressUnusableQuestions(m); - mDNS_UpdateAllowSleep(m); mDNS_Unlock(m); } +mDNSlocal void SetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes) +{ + int i, len; + + if (!sr->AnonData) + return; + + len = mDNSPlatformStrLen(sr->AnonData); + if (sr->RR_PTR.resrec.AnonInfo) + { + LogMsg("SetAnonInfoSRS: Freeing AnonInfo for PTR record %##s, should have been freed already", sr->RR_PTR.resrec.name->c); + FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo); + } + sr->RR_PTR.resrec.AnonInfo = AllocateAnonInfo(sr->RR_PTR.resrec.name, sr->AnonData, len, mDNSNULL); + for (i=0; iSubTypes[i].resrec.AnonInfo) + { + LogMsg("SetAnonInfoSRS: Freeing AnonInfo for subtype record %##s, should have been freed already", sr->SubTypes[i].resrec.name->c); + FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo); + } + sr->SubTypes[i].resrec.AnonInfo = AllocateAnonInfo(sr->SubTypes[i].resrec.name, sr->AnonData, len, mDNSNULL); + } +} + +mDNSlocal void ResetAnonInfoSRS(ServiceRecordSet *sr, int NumSubTypes) +{ + int i; + + if (!sr->AnonData) + return; + if (sr->RR_PTR.resrec.AnonInfo) + { + FreeAnonInfo(sr->RR_PTR.resrec.AnonInfo); + sr->RR_PTR.resrec.AnonInfo = mDNSNULL; + } + for (i=0; iSubTypes[i].resrec.AnonInfo) + { + FreeAnonInfo(sr->SubTypes[i].resrec.AnonInfo); + sr->SubTypes[i].resrec.AnonInfo = mDNSNULL; + } + } +} + mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result) { ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext; @@ -11025,6 +13120,7 @@ mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus resu if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return; e = e->next; } + ResetAnonInfoSRS(sr, sr->NumSubTypes); // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse, // then we can now report the NameConflict to the client @@ -11055,6 +13151,9 @@ mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) artype = AuthRecordLocalOnly; else if (InterfaceID == mDNSInterface_P2P) artype = AuthRecordP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P) + && (flags & coreFlagIncludeAWDL)) + artype = AuthRecordAnyIncludeAWDLandP2P; else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P)) artype = AuthRecordAnyIncludeP2P; else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeAWDL)) @@ -11093,6 +13192,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, sr->Extras = mDNSNULL; sr->NumSubTypes = NumSubTypes; sr->SubTypes = SubTypes; + sr->flags = flags; artype = setAuthRecType(InterfaceID, flags); @@ -11101,6 +13201,11 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr); mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr); + if (flags & coreFlagWakeOnly) + { + sr->RR_PTR.AuthFlags = AuthFlagsWakeOnly; + } + if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp")) hostTTL = kHostNameSmallTTL; else @@ -11152,6 +13257,8 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, sr->SubTypes[i].Additional1 = &sr->RR_SRV; sr->SubTypes[i].Additional2 = &sr->RR_TXT; } + + SetAnonInfoSRS(sr, NumSubTypes); // 3. Set up the SRV record rdata. sr->RR_SRV.resrec.rdata->u.srv.priority = 0; @@ -11287,7 +13394,7 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS 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, sr->SubTypes, sr->NumSubTypes, - sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, 0); + sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, sr->flags); // mDNS_RegisterService() just reset sr->Extras to NULL. // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run @@ -11456,6 +13563,7 @@ mDNSlocal void RestartARPProbing(mDNS *const m, AuthRecord *const rr) rr->resrec.RecordType = kDNSRecordTypeUnique; rr->ProbeCount = DefaultProbeCountForTypeUnique; + rr->ProbeRestartCount++; // If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably // still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding. @@ -11506,9 +13614,21 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, mDNSSameEthAddress(&arp->sha, &intf->MAC) ? msg3 : msg4; LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s", intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - if (msg == msg1) RestartARPProbing(m, rr); - else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); - else if (msg == msg4) SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha); + if (msg == msg1) + { + if ( rr->ProbeRestartCount < MAX_PROBE_RESTARTS) + RestartARPProbing(m, rr); + else + LogSPS("Reached maximum number of restarts for probing - %s", ARDisplayString(m,rr)); + } + else if (msg == msg3) + { + mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + } + else if (msg == msg4) + { + SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha); + } } } @@ -11527,18 +13647,28 @@ mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, if (!mDNSSameIPv4Address(arp->spa, zerov4Addr)) for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && - rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa)) + rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa) && (rr->ProbeRestartCount < MAX_PROBE_RESTARTS)) { - RestartARPProbing(m, rr); - if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) - LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, - mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", - &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); + if (mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC)) + { + LogSPS("%-7s ARP from %.6a %.4a for %.4a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname, + &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + } else { - LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, - &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC)) + { + LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname, + mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request " : "Response ", + &arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr)); + } + else + { + LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } } } } @@ -11591,14 +13721,19 @@ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, spa && mDNSIPv6AddressIsZero(*spa) ? msg4 : msg5; LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s", intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - if (msg == msg1) RestartARPProbing(m, rr); - else if (msg == msg3) + if (msg == msg1) { - if (!(m->KnownBugs & mDNS_KnownBug_LimitedIPv6)) - mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + if (rr->ProbeRestartCount < MAX_PROBE_RESTARTS) + RestartARPProbing(m, rr); + else + LogSPS("Reached maximum number of restarts for probing - %s", ARDisplayString(m,rr)); } - else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha ); - else if (msg == msg5) SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); + else if (msg == msg3) + mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); + else if (msg == msg4) + SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha); + else if (msg == msg5) + SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); } } @@ -11615,17 +13750,27 @@ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, if (!mDNSSameIPv6Address(*spa, zerov6Addr)) for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && - rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa)) + rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa) && (rr->ProbeRestartCount < MAX_PROBE_RESTARTS)) { - RestartARPProbing(m, rr); - if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC)) - LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname, - ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr)); + if (mDNSSameEthAddress(&zeroEthAddr, &rr->WakeUp.HMAC)) + { + LogSPS("%-7s NDP from %.6a %.16a for %.16a -- Invalid H-MAC %.6a I-MAC %.6a %s", intf->ifname, + sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + } else { - LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, - sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); - ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + RestartARPProbing(m, rr); + if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC)) + { + LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname, + ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr)); + } + else + { + LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname, + sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr)); + ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC); + } } } } @@ -11652,6 +13797,7 @@ mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAdd #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 + #define TH_ACK 0x10 kr = mDNS_MatchKeepaliveInfo(m, dst, src, port, t->tcp.src, &seq, &ack); if (kr) @@ -11692,6 +13838,14 @@ mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAdd tcphlen = ((t->tcp.offset >> 4) * 4); if (end - ((mDNSu8 *)t + tcphlen) > 0) data = mDNStrue; wake = ((int)(pack - seq) > 0) && ((int)(pseq - ack) >= 0) && data; + + // If we got a regular keepalive on a connection that was registed with the KeepAlive API, respond with an ACK + if ((t->tcp.flags & TH_ACK) && (data == mDNSfalse) && + ((int)(ack - pseq) == 1)) + { + // Send an ACK; + mDNS_SendKeepaliveACK(m, kr); + } LogSPS("mDNSCoreReceiveRawTransportPacket: End %p, hlen %d, Datalen %d, pack %u, seq %u, pseq %u, ack %u, wake %d", end, tcphlen, end - ((mDNSu8 *)t + tcphlen), pack, seq, pseq, ack, wake); } @@ -11700,7 +13854,6 @@ mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAdd } else { - // Plan to wake if // (a) RST is not set, AND // (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone. @@ -11856,6 +14009,7 @@ mDNSlocal void ConstructSleepProxyServerName(mDNS *const m, domainlabel *name) m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags, &m->nicelabel); } +#ifndef SPC_DISABLED mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result) { if (result == mStatus_NameConflict) @@ -11883,6 +14037,7 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s } } } +#endif // Called with lock held mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features) @@ -11895,8 +14050,10 @@ mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, m if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; } // If turning off, or changing type, deregister old name +#ifndef SPC_DISABLED if (m->SPSState == 1 && sps != m->SPSType) { m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); } +#endif // SPC_DISABLED // Record our new SPS parameters m->SPSType = sps; @@ -11912,7 +14069,9 @@ mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, m m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort); if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; } } +#ifndef SPC_DISABLED if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree); +#endif // SPC_DISABLED } else if (m->SPSState) { @@ -11960,7 +14119,6 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, if (!rrcachestorage) rrcachesize = 0; m->p = p; - m->KnownBugs = 0; m->CanReceiveUnicastOn5353 = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise m->AdvertiseLocalAddresses = AdvertiseLocalAddresses; m->DivertMulticastAdvertisements = mDNSfalse; @@ -11971,6 +14129,7 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->MainCallback = Callback; m->MainContext = Context; m->rec.r.resrec.RecordType = 0; + m->rec.r.resrec.AnonInfo = mDNSNULL; // For debugging: To catch and report locking failures m->mDNS_busy = 0; @@ -12001,16 +14160,24 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->RandomQueryDelay = 0; m->RandomReconfirmDelay = 0; m->PktNum = 0; + m->MPktNum = 0; m->LocalRemoveEvents = mDNSfalse; m->SleepState = SleepState_Awake; m->SleepSeqNum = 0; m->SystemWakeOnLANEnabled = mDNSfalse; m->AnnounceOwner = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond); - m->ClearSPSRecords = 0; - m->clearIgnoreNA = NonZeroTime(timenow + 2 * mDNSPlatformOneSecond); m->DelaySleep = 0; m->SleepLimit = 0; +#if APPLE_OSX_mDNSResponder + m->StatStartTime = mDNSPlatformUTC(); + m->NextStatLogTime = m->StatStartTime + kDefaultNextStatsticsLogTime; + m->ActiveStatTime = 0; + m->UnicastPacketsSent = 0; + m->MulticastPacketsSent = 0; + m->RemoteSubnet = 0; +#endif // APPLE_OSX_mDNSResponder + // These fields only required for mDNS Searcher... m->Questions = mDNSNULL; m->NewQuestions = mDNSNULL; @@ -12071,15 +14238,27 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->Hostnames = mDNSNULL; m->AutoTunnelNAT.clientContext = mDNSNULL; - m->StartWABQueries = mDNSfalse; - m->mDNSHandlePeerEvents = mDNSfalse; + m->WABBrowseQueriesCount = 0; + m->WABLBrowseQueriesCount = 0; + m->WABRegQueriesCount = 0; +#if !TARGET_OS_EMBEDDED + m->mDNSOppCaching = mDNStrue; +#else + m->mDNSOppCaching = mDNSfalse; +#endif + m->AutoTargetServices = 0; // NAT traversal fields + m->LLQNAT.clientCallback = mDNSNULL; + m->LLQNAT.clientContext = mDNSNULL; m->NATTraversals = mDNSNULL; m->CurrentNATTraversal = mDNSNULL; m->retryIntervalGetAddr = 0; // delta between time sent and retry m->retryGetAddr = timenow + 0x78000000; // absolute time when we retry - m->ExternalAddress = zerov4Addr; + m->ExtAddress = zerov4Addr; + m->PCPNonce[0] = mDNSRandom(-1); + m->PCPNonce[1] = mDNSRandom(-1); + m->PCPNonce[2] = mDNSRandom(-1); m->NATMcastRecvskt = mDNSNULL; m->LastNATupseconds = 0; @@ -12138,8 +14317,10 @@ mDNSexport void mDNS_ConfigChanged(mDNS *const m) if (m->SPSState == 1) { domainlabel name, newname; +#ifndef SPC_DISABLED domainname type, domain; DeconstructServiceName(m->SPSRecords.RR_SRV.resrec.name, &name, &type, &domain); +#endif // SPC_DISABLED ConstructSleepProxyServerName(m, &newname); if (!SameDomainLabelCS(name.c, newname.c)) { @@ -12147,7 +14328,9 @@ mDNSexport void mDNS_ConfigChanged(mDNS *const m) // When SleepProxyServerCallback gets the mStatus_MemFree message, // it will reregister the service under the new name m->SPSState = 2; +#ifndef SPC_DISABLED mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid); +#endif // SPC_DISABLED } } @@ -12188,26 +14371,43 @@ mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const } } -mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q) +mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q) { const mDNSu32 slot = HashSlot(&q->qname); CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); CacheRecord *rp; + mDNSu8 validatingResponse = 0; + // For DNSSEC questions, purge the corresponding RRSIGs also. + if (DNSSECQuestion(q)) + { + validatingResponse = q->ValidatingResponse; + q->ValidatingResponse = mDNStrue; + } for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) { if (SameNameRecordAnswersQuestion(&rp->resrec, q)) { - LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp)); + LogInfo("mDNS_PurgeForQuestion: Flushing %s", CRDisplayString(m, rp)); mDNS_PurgeCacheResourceRecord(m, rp); } } + if (DNSSECQuestion(q)) + { + q->ValidatingResponse = validatingResponse; + } } -// If we need to validate the negative response, we need the NSECs to prove -// the non-existence. If we don't have the cached NSECs, purge them so that -// we can reissue the question with EDNS0/DO bit set. -mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q) +// For DNSSEC question, we need the DNSSEC records also. If the cache does not +// have the DNSSEC records, we need to re-issue the question with EDNS0/DO bit set. +// Just re-issuing the question for RRSIGs does not work in practice as the response +// may not contain the RRSIGs whose typeCovered field matches the question's qtype. +// +// For negative responses, we need the NSECs to prove the non-existence. If we don't +// have the cached NSECs, purge them. For positive responses, if we don't have the +// RRSIGs and if we have not already issued the question with EDNS0/DO bit set, purge +// them. +mDNSlocal void CheckForDNSSECRecords(mDNS *const m, DNSQuestion *q) { const mDNSu32 slot = HashSlot(&q->qname); CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); @@ -12215,12 +14415,16 @@ mDNSlocal void mDNS_CheckForCachedNSECS(mDNS *const m, DNSQuestion *q) for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next) { - if (SameNameRecordAnswersQuestion(&rp->resrec, q) && - rp->resrec.RecordType == kDNSRecordTypePacketNegative && - !rp->nsec) + if (SameNameRecordAnswersQuestion(&rp->resrec, q)) { - LogInfo("mDNS_CheckForCachedNSECS: Flushing %s", CRDisplayString(m, rp)); - mDNS_PurgeCacheResourceRecord(m, rp); + if (rp->resrec.RecordType != kDNSRecordTypePacketNegative || !rp->nsec) + { + if (!rp->CRDNSSECQuestion) + { + LogInfo("CheckForDNSSECRecords: Flushing %s", CRDisplayString(m, rp)); + mDNS_PurgeCacheResourceRecord(m, rp); + } + } } } } @@ -12268,12 +14472,44 @@ mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSSer } } +mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) +{ + McastResolver *mr; + DNSServer *ptr; + + if (delete) + { + for (ptr = m->DNSServers; ptr; ptr = ptr->next) + { + ptr->penaltyTime = 0; + NumUnicastDNSServers--; + ptr->flags |= DNSServer_FlagDelete; + } + // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at + // mcast resolvers. Today we get both mcast and ucast configuration using the same + // API + for (mr = m->McastResolvers; mr; mr = mr->next) + mr->flags |= McastResolver_FlagDelete; + } + else + { + for (ptr = m->DNSServers; ptr; ptr = ptr->next) + { + ptr->penaltyTime = 0; + NumUnicastDNSServers++; + ptr->flags &= ~DNSServer_FlagDelete; + } + for (mr = m->McastResolvers; mr; mr = mr->next) + mr->flags &= ~McastResolver_FlagDelete; + } +} + mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) { mDNSu32 slot; CacheGroup *cg; CacheRecord *cr; - + mDNSBool Restart = mDNSfalse; mDNSAddr v4, v6, r; domainname fqdn; DNSServer *ptr, **p = &m->DNSServers; @@ -12283,29 +14519,27 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) debugf("uDNS_SetupDNSConfig: entry"); - // Let the platform layer get the current DNS information - // The m->StartWABQueries is set when we get the first domain enumeration query (no need to hit the network - // with domain enumeration queries until we actually need that information). Even if it is not set, we still - // need to setup the search domains so that we can append them to queries that need them. - - uDNS_SetupSearchDomains(m, m->StartWABQueries ? UDNS_START_WAB_QUERY : 0); + // Let the platform layer get the current DNS information and setup the WAB queries if needed. + uDNS_SetupWABQueries(m); mDNS_Lock(m); - for (ptr = m->DNSServers; ptr; ptr = ptr->next) - { - ptr->penaltyTime = 0; - ptr->flags |= DNSServer_FlagDelete; + // We need to first mark all the entries to be deleted. If the configuration changed, then + // the entries would be undeleted appropriately. Otherwise, we need to clear them. + // + // Note: The last argument to mDNSPlatformSetDNSConfig is "mDNStrue" which means ack the + // configuration. We already processed search domains in uDNS_SetupWABQueries above and + // hence we are ready to ack the configuration as this is the last call to mDNSPlatformSetConfig + // for the dns configuration change notification. + SetConfigState(m, mDNStrue); + if (!mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL, mDNStrue)) + { + SetConfigState(m, mDNSfalse); + mDNS_Unlock(m); + LogInfo("uDNS_SetupDNSConfig: No configuration change"); + return mStatus_NoError; } - // We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at - // mcast resolvers. Today we get both mcast and ucast configuration using the same - // API - for (mr = m->McastResolvers; mr; mr = mr->next) - mr->flags |= McastResolver_FlagDelete; - - mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL); - // For now, we just delete the mcast resolvers. We don't deal with cache or // questions here. Neither question nor cache point to mcast resolvers. Questions // do inherit the timeout values from mcast resolvers. But we don't bother @@ -12354,6 +14588,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) // non-scoped question and vice versa. // for (q = m->Questions; q; q=q->next) + { if (!mDNSOpaque16IsZero(q->TargetQID)) { DNSServer *s, *t; @@ -12365,26 +14600,54 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) t = q->qDNSServer; if (t != s) { + mDNSBool old, new; // If DNS Server for this question has changed, reactivate it LogInfo("uDNS_SetupDNSConfig: Updating DNS Server from %#a:%d (%##s) to %#a:%d (%##s) for question %##s (%s) (scope:%p)", t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"", s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"", q->qname.c, DNSTypeName(q->qtype), q->InterfaceID); - DNSServerChangeForQuestion(m, q, s); - q->unansweredQueries = 0; - // We still need to pick a new DNSServer for the questions that have been - // suppressed, but it is wrong to activate the query as DNS server change - // could not possibly change the status of SuppressUnusable questions - if (!QuerySuppressed(q)) - { - debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); - ActivateUnicastQuery(m, q, mDNStrue); - // ActivateUnicastQuery is called for duplicate questions also as it does something - // special for AutoTunnel questions + old = q->SuppressQuery; + new = ShouldSuppressUnicastQuery(m, q, s); + if (old != new) + { + // Changing the DNS server affected the SuppressQuery status. We need to + // deliver RMVs for the previous ADDs (if any) before switching to the new + // DNSServer. To keep it simple, we walk all the questions and mark them + // to be restarted and then handle all of them at once. + q->Restart = 1; + q->SuppressQuery = new; for (qptr = q->next ; qptr; qptr = qptr->next) { - if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue); + if (qptr->DuplicateOf == q) + qptr->Restart = 1; + } + Restart = mDNStrue; + } + else + { + DNSServerChangeForQuestion(m, q, s); + q->unansweredQueries = 0; + + // If we had sent a query out to DNSServer "t" and we are changing to "s", we + // need to ignore the responses coming back from "t" as the DNS configuration + // has changed e.g., when a new interface is coming up and that becomes the primary + // interface, we switch to the DNS servers configured for the primary interface. In + // this case, we should not accept responses associated with the previous interface as + // the "name" could resolve differently on this new primary interface. Hence, discard + // in-flight responses. + q->TargetQID = mDNS_NewMessageID(m); + + if (!QuerySuppressed(q)) + { + debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype)); + ActivateUnicastQuery(m, q, mDNStrue); + // ActivateUnicastQuery is called for duplicate questions also as it does something + // special for AutoTunnel questions + for (qptr = q->next ; qptr; qptr = qptr->next) + { + if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue); + } } } } @@ -12396,23 +14659,45 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } } } + } + if (Restart) + RestartUnicastQuestions(m); FORALL_CACHERECORDS(slot, cg, cr) { - if (cr->resrec.InterfaceID) continue; - // We just mark them for purge or reconfirm. + if (cr->resrec.InterfaceID) + continue; + + // We already walked the questions and restarted/reactivated them if the dns server + // change affected the question. That should take care of updating the cache. But + // what if there is no active question at this point when the DNS server change + // happened ? There could be old cache entries lying around and if we don't flush + // them, a new question after the DNS server change could pick up these stale + // entries and get a wrong answer. // - // The new DNSServer may be a scoped or non-scoped one. We use the active question's - // InterfaceID for looking up the right DNS server - ptr = GetServerForName(m, cr->resrec.name, cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL); + // For cache entries that have active questions we might have skipped rescheduling + // the questions if they were suppressed (see above). To keep it simple, we walk + // all the cache entries to make sure that there are no stale entries. We use the + // active question's InterfaceID/ServiceID for looking up the right DNS server. + // Note that the unscoped value for ServiceID is -1. + // + // Note: If GetServerForName returns NULL, it could either mean that there are no + // DNS servers or no matching DNS servers for this question. In either case, + // the cache should get purged below when we process deleted DNS servers. + + ptr = GetServerForName(m, cr->resrec.name, + (cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL), + (cr->CRActiveQuestion ? cr->CRActiveQuestion->ServiceID : -1)); // Purge or Reconfirm if this cache entry would use the new DNS server if (ptr && (ptr != cr->resrec.rDNSServer)) { // As the DNSServers for this cache record is not the same anymore, we don't // want any new questions to pick this old value. If there is no active question, - // we can't possibly re-confirm, so purge in that case. - if (cr->CRActiveQuestion == mDNSNULL) + // we can't possibly re-confirm, so purge in that case. If it is a DNSSEC question, + // purge the cache as the DNSSEC capabilities of the DNS server may have changed. + + if (cr->CRActiveQuestion == mDNSNULL || DNSSECQuestion(cr->CRActiveQuestion)) { 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)); @@ -12480,9 +14765,8 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) } } *p = (*p)->next; - debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c); + LogInfo("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s) %d", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, NumUnicastDNSServers); mDNSPlatformMemFree(ptr); - NumUnicastDNSServers--; } else { @@ -12645,6 +14929,7 @@ mDNSexport void mDNS_StartExit(mDNS *const m) // Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers // to not do this. t->ExternalAddress = zerov4Addr; + t->NewAddress = zerov4Addr; t->ExternalPort = zeroIPPort; t->RequestedPort = zeroIPPort; t->Lifetime = 0; diff --git a/mDNSCore/mDNSDebug.h b/mDNSCore/mDNSDebug.h index 7b6c2c6..5467ae6 100755 --- a/mDNSCore/mDNSDebug.h +++ b/mDNSCore/mDNSDebug.h @@ -138,6 +138,8 @@ extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1 extern int mDNS_LoggingEnabled; extern int mDNS_PacketLoggingEnabled; +extern int mDNS_McastLoggingEnabled; +extern int mDNS_McastTracingEnabled; extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog extern const char ProgramName[]; diff --git a/mDNSCore/mDNSEmbeddedAPI.h b/mDNSCore/mDNSEmbeddedAPI.h index 3eeb88e..b041f07 100755 --- a/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSCore/mDNSEmbeddedAPI.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,8 +51,8 @@ */ -#ifndef __mDNSClientAPI_h -#define __mDNSClientAPI_h +#ifndef __mDNSEmbeddedAPI_h +#define __mDNSEmbeddedAPI_h #if defined(EFI32) || defined(EFI64) || defined(EFIX64) // EFI doesn't have stdarg.h unless it's building with GCC. @@ -76,6 +76,33 @@ extern "C" { #endif +// *************************************************************************** +// Feature removal compile options & limited resource targets + +// The following compile options are responsible for removing certain features from mDNSCore to reduce the +// memory footprint for use in embedded systems with limited resources. + +// UNICAST_DISABLED - disables unicast DNS functionality, including Wide Area Bonjour +// ANONYMOUS_DISABLED - disables anonymous functionality +// DNSSEC_DISABLED - disables DNSSEC functionality +// SPC_DISABLED - disables Bonjour Sleep Proxy client +// IDLESLEEPCONTROL_DISABLED - disables sleep control for Bonjour Sleep Proxy clients + +// In order to disable the above features pass the option to your compiler, e.g. -D UNICAST_DISABLED + +// Additionally, the LIMITED_RESOURCES_TARGET compile option will eliminate caching and +// and reduce the maximum DNS message sizes. + +#ifdef LIMITED_RESOURCES_TARGET +// Don't support jumbo frames +#define AbsoluteMaxDNSMessageData 1500 +// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes) +#define MaximumRDSize 264 +// Don't cache anything +#define AUTH_HASH_SLOTS 1 +#define CACHE_HASH_SLOTS 1 +#endif + // *************************************************************************** // Function scope indicators @@ -220,31 +247,21 @@ typedef enum // From RFC 1035 // mDNS defines its own names for these common types to simplify portability across // multiple platforms that may each have their own (different) names for these types. -typedef int mDNSBool; +typedef unsigned char mDNSBool; typedef signed char mDNSs8; typedef unsigned char mDNSu8; typedef signed short mDNSs16; typedef unsigned short mDNSu16; -// says -// __LP64__ _LP64 -// These macros are defined, with value 1, if (and only if) the compilation is -// for a target where long int and pointer both use 64-bits and int uses 32-bit. -// says -// Macro Name __LP64__ Value 1 -// A quick Google search for "defined(__LP64__)" OR "#ifdef __LP64__" gives 2590 hits and -// a search for "#if __LP64__" gives only 12, so I think we'll go with the majority and use defined() +// Source: http://www.unix.org/version2/whatsnew/lp64_wp.html +// http://software.intel.com/sites/products/documentation/hpc/mkl/lin/MKL_UG_structure/Support_for_ILP64_Programming.htm +// It can be safely assumed that int is 32bits on the platform #if defined(_ILP64) || defined(__ILP64__) typedef signed int32 mDNSs32; typedef unsigned int32 mDNSu32; -#elif defined(_LP64) || defined(__LP64__) +#else typedef signed int mDNSs32; typedef unsigned int mDNSu32; -#else -typedef signed long mDNSs32; -typedef unsigned long mDNSu32; -//typedef signed int mDNSs32; -//typedef unsigned int mDNSu32; #endif // To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct @@ -344,8 +361,8 @@ enum mStatus_BadKey = -65561, mStatus_TransientErr = -65562, // transient failures, e.g. sending packets shortly after a network transition or wake from sleep mStatus_ServiceNotRunning = -65563, // Background daemon not running - mStatus_NATPortMappingUnsupported = -65564, // NAT doesn't support NAT-PMP or UPnP - mStatus_NATPortMappingDisabled = -65565, // NAT supports NAT-PMP or UPnP but it's disabled by the administrator + mStatus_NATPortMappingUnsupported = -65564, // NAT doesn't support PCP, NAT-PMP or UPnP + mStatus_NATPortMappingDisabled = -65565, // NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator mStatus_NoRouter = -65566, mStatus_PollingMode = -65567, mStatus_Timeout = -65568, @@ -364,6 +381,10 @@ enum }; typedef mDNSs32 mStatus; +#define MaxIp 5 // Needs to be consistent with MaxInputIf in dns_services.h + +typedef enum { q_stop = 0, q_start } q_state; +typedef enum { reg_stop = 0, reg_start } reg_state; // RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters #define MAX_DOMAIN_LABEL 63 @@ -427,6 +448,13 @@ typedef struct { mDNSu8 c[256]; } UTF8str255; // Null-terminated C string #define kStaticCacheTTL 10 #define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL) +#define mDNS_KeepaliveRecord(rr) ((rr)->rrtype == kDNSType_NULL && SameDomainLabel(SecondLabel((rr)->name)->c, (mDNSu8 *)"\x0A_keepalive")) + +// Number of times keepalives are sent if no ACK is received before waking up the system +// this is analogous to net.inet.tcp.keepcnt +#define kKeepaliveRetryCount 10 +// The frequency at which keepalives are retried if no ACK is received +#define kKeepaliveRetryInterval 30 typedef struct AuthRecord_struct AuthRecord; typedef struct ServiceRecordSet_struct ServiceRecordSet; @@ -438,6 +466,7 @@ typedef struct ZoneData_struct ZoneData; typedef struct mDNS_struct mDNS; typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport; typedef struct NATTraversalInfo_struct NATTraversalInfo; +typedef struct ResourceRecord_struct ResourceRecord; // Structure to abstract away the differences between TCP/SSL sockets, and one for UDP sockets // The actual definition of these structures appear in the appropriate platform support code @@ -467,7 +496,9 @@ typedef packedstruct // We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used) // However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet // 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total +#ifndef AbsoluteMaxDNSMessageData #define AbsoluteMaxDNSMessageData 8940 +#endif #define NormalMaxDNSMessageData 1440 typedef packedstruct { @@ -597,6 +628,12 @@ typedef packedstruct // Typically 8 bytes of options are also present } IPv6NDP; // 24 bytes or more; IP protocol type 0x3A +typedef struct +{ + mDNSAddr ipaddr; + char ethaddr[18]; +} IPAddressMACMapping; + #define NDP_Sol 0x87 #define NDP_Adv 0x88 @@ -742,9 +779,12 @@ typedef packedstruct #define CRYPTO_ALG_MAX 0x0B -// alg - same as in RRSIG, DNS KEY or DS +// alg - same as in RRSIG, DNS KEY or DS. // RFC 4034 defines SHA1 // RFC 4509 defines SHA256 +// Note: NSEC3 also uses 1 for SHA1 and hence we will reuse for now till a new +// value is assigned. +// #define SHA1_DIGEST_TYPE 1 #define SHA256_DIGEST_TYPE 2 #define DIGEST_TYPE_MAX 3 @@ -812,16 +852,58 @@ typedef packedstruct mDNSu8 *data; } rdataDNSKey; +#define NSEC3_FIXED_SIZE 5 +#define NSEC3_FLAGS_OPTOUT 1 +#define NSEC3_MAX_ITERATIONS 2500 +typedef packedstruct +{ + mDNSu8 alg; + mDNSu8 flags; + mDNSu16 iterations; + mDNSu8 saltLength; + mDNSu8 *salt; + // hashLength, nxt, bitmap +} rdataNSEC3; + +// In the multicast usage of NSEC3, we know the actual size of RData +// 4 bytes : HashAlg, Flags,Iterations +// 5 bytes : Salt Length 1 byte, Salt 4 bytes +// 21 bytes : HashLength 1 byte, Hash 20 bytes +// 34 bytes : Window number, Bitmap length, Type bit map to include the first 256 types +#define MCAST_NSEC3_RDLENGTH (4 + 5 + 21 + 34) +#define SHA1_HASH_LENGTH 20 + +// Base32 encoding takes 5 bytes of the input and encodes as 8 bytes of output. +// For example, SHA-1 hash of 20 bytes will be encoded as 20/5 * 8 = 32 base32 +// bytes. For a max domain name size of 255 bytes of base32 encoding : (255/8)*5 +// is the max hash length possible. +#define NSEC3_MAX_HASH_LEN 155 +// In NSEC3, the names are hashed and stored in the first label and hence cannot exceed label +// size. +#define NSEC3_MAX_B32_LEN MAX_DOMAIN_LABEL + // We define it here instead of dnssec.h so that these values can be used // in files without bringing in all of dnssec.h unnecessarily. typedef enum { DNSSEC_Secure = 1, // Securely validated and has a chain up to the trust anchor DNSSEC_Insecure, // Cannot build a chain up to the trust anchor - DNSSEC_Indeterminate, // Cannot fetch DNSSEC RRs - DNSSEC_Bogus // failed to validate signatures + DNSSEC_Indeterminate, // Not used currently + DNSSEC_Bogus, // failed to validate signatures + DNSSEC_NoResponse // No DNSSEC records to start with } DNSSECStatus; +#define DNSSECRecordType(rrtype) (((rrtype) == kDNSType_RRSIG) || ((rrtype) == kDNSType_NSEC) || ((rrtype) == kDNSType_DNSKEY) || ((rrtype) == kDNSType_DS) || \ + ((rrtype) == kDNSType_NSEC3)) + +typedef enum +{ + platform_OSX = 1, // OSX Platform + platform_iOS, // iOS Platform + platform_Atv, // Atv Platform + platform_NonApple // Non-Apple (Windows, POSIX) Platform +} Platform_t; + // EDNS Option Code registrations are recorded in the "DNS EDNS0 Options" section of // @@ -829,6 +911,7 @@ typedef enum #define kDNSOpt_Lease 2 #define kDNSOpt_NSID 3 #define kDNSOpt_Owner 4 +#define kDNSOpt_Trace 65001 // 65001-65534 Reserved for Local/Experimental Use typedef struct { @@ -849,19 +932,27 @@ typedef struct mDNSOpaque48 password; // Optional password } OwnerOptData; +typedef struct +{ + mDNSu8 platf; // Running platform (see enum Platform_t) + mDNSu16 mDNSv; // mDNSResponder Version (defined in dns_sd.h) +} TracerOptData; + // Note: rdataOPT format may be repeated an arbitrary number of times in a single resource record typedef packedstruct { mDNSu16 opt; mDNSu16 optlen; - union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; } u; + union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; TracerOptData tracer; } u; } rdataOPT; // Space needed to put OPT records into a packet: -// Header 11 bytes (name 1, type 2, class 2, TTL 4, length 2) -// LLQ rdata 18 bytes (opt 2, len 2, vers 2, op 2, err 2, id 8, lease 4) -// Lease rdata 8 bytes (opt 2, len 2, lease 4) -// Owner rdata 12-24 (opt 2, len 2, owner 8-20) +// Header 11 bytes (name 1, type 2, class 2, TTL 4, length 2) +// LLQ rdata 18 bytes (opt 2, len 2, vers 2, op 2, err 2, id 8, lease 4) +// Lease rdata 8 bytes (opt 2, len 2, lease 4) +// Owner rdata 12-24 bytes (opt 2, len 2, owner 8-20) +// Trace rdata 7 bytes (opt 2, len 2, platf 1, mDNSv 2) + #define DNSOpt_Header_Space 11 #define DNSOpt_LLQData_Space (4 + 2 + 2 + 2 + 8 + 4) @@ -870,6 +961,7 @@ typedef packedstruct #define DNSOpt_OwnerData_ID_Wake_Space (4 + 2 + 6 + 6) #define DNSOpt_OwnerData_ID_Wake_PW4_Space (4 + 2 + 6 + 6 + 4) #define DNSOpt_OwnerData_ID_Wake_PW6_Space (4 + 2 + 6 + 6 + 6) +#define DNSOpt_TraceData_Space (4 + 1 + 2) #define ValidOwnerLength(X) ( (X) == DNSOpt_OwnerData_ID_Space - 4 || \ (X) == DNSOpt_OwnerData_ID_Wake_Space - 4 || \ @@ -881,6 +973,7 @@ typedef packedstruct #define DNSOpt_Data_Space(O) ( \ (O)->opt == kDNSOpt_LLQ ? DNSOpt_LLQData_Space : \ (O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space : \ + (O)->opt == kDNSOpt_Trace ? DNSOpt_TraceData_Space : \ (O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000) // NSEC record is defined in RFC 4034. @@ -893,20 +986,22 @@ typedef packedstruct // // This would be a waste, as types about 256 are not very common. But it would be odd, if we receive // a type above 256 (.US zone had TYPE65534 when this code was written) and not able to handle it. -// Hence, we handle any size by not fixing a strucure in place. The following is just a palceholder +// Hence, we handle any size by not fixing a strucure in place. The following is just a placeholder // and never used anywhere. // #define NSEC_MCAST_WINDOW_SIZE 32 typedef struct { - //domainname *next; - //char bitmap[32]; + domainname *next; //placeholders are uncommented because C89 in Windows requires that a struct has at least a member. + char bitmap[32]; } rdataNSEC; // StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes) // MaximumRDSize is 8K the absolute maximum we support (at least for now) #define StandardAuthRDSize 264 +#ifndef MaximumRDSize #define MaximumRDSize 8192 +#endif // InlineCacheRDSize is 68 // Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object @@ -1052,6 +1147,76 @@ typedef packedstruct mDNSu32 NATRep_lease; } NATPortMapReply; +// PCP Support for IPv4 mappings + +#define PCP_VERS 0x02 +#define PCP_WAITSECS_AFTER_EPOCH_INVALID 5 + +typedef enum +{ + PCPOp_Announce = 0, + PCPOp_Map = 1 +} PCPOp_t; + +typedef enum +{ + PCPProto_All = 0, + PCPProto_TCP = 6, + PCPProto_UDP = 17 +} PCPProto_t; + +typedef enum +{ + PCPResult_Success = 0, + PCPResult_UnsuppVersion = 1, + PCPResult_NotAuthorized = 2, + PCPResult_MalformedReq = 3, + PCPResult_UnsuppOpcode = 4, + PCPResult_UnsuppOption = 5, + PCPResult_MalformedOption = 6, + PCPResult_NetworkFailure = 7, + PCPResult_NoResources = 8, + PCPResult_UnsuppProtocol = 9, + PCPResult_UserExQuota = 10, + PCPResult_CantProvideExt = 11, + PCPResult_AddrMismatch = 12, + PCPResult_ExcesRemotePeer = 13 +} PCPResult_t; + +typedef packedstruct +{ + mDNSu8 version; + mDNSu8 opCode; + mDNSOpaque16 reserved; + mDNSu32 lifetime; + mDNSv6Addr clientAddr; + mDNSu32 nonce[3]; + mDNSu8 protocol; + mDNSu8 reservedMapOp[3]; + mDNSIPPort intPort; + mDNSIPPort extPort; + mDNSv6Addr extAddress; +} PCPMapRequest; + +typedef packedstruct +{ + mDNSu8 version; + mDNSu8 opCode; + mDNSu8 reserved; + mDNSu8 result; + mDNSu32 lifetime; + mDNSu32 epoch; + mDNSu32 clientAddrParts[3]; + mDNSu32 nonce[3]; + mDNSu8 protocol; + mDNSu8 reservedMapOp[3]; + mDNSIPPort intPort; + mDNSIPPort extPort; + mDNSv6Addr extAddress; +} PCPMapReply; + +// LNT Support + typedef enum { LNTDiscoveryOp = 1, @@ -1084,6 +1249,14 @@ typedef void (*NATTraversalClientCallback)(mDNS *m, NATTraversalInfo *n); // if m->timenow < ExpiryTime then we have an active mapping, and we'll renew halfway to expiry // if m->timenow >= ExpiryTime then our mapping has expired, and we're trying to create one +typedef enum +{ + NATTProtocolNone = 0, + NATTProtocolNATPMP = 1, + NATTProtocolUPNPIGD = 2, + NATTProtocolPCP = 3, +} NATTProtocol; + struct NATTraversalInfo_struct { // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. @@ -1093,6 +1266,9 @@ struct NATTraversalInfo_struct mDNSs32 retryInterval; // Current interval, between last packet we sent and the next one mDNSs32 retryPortMap; // If Protocol is nonzero, time to send our next mapping packet mStatus NewResult; // New error code; will be copied to Result just prior to invoking callback + NATTProtocol lastSuccessfulProtocol; // To send correct deletion request & update non-PCP external address operations + mDNSBool sentNATPMP; // Whether we just sent a NAT-PMP packet, so we won't send another if + // we receive another NAT-PMP "Unsupported Version" packet #ifdef _LEGACY_NAT_TRAVERSAL_ tcpLNTInfo tcpInfo; // Legacy NAT traversal (UPnP) TCP connection @@ -1107,10 +1283,11 @@ struct NATTraversalInfo_struct // is reported as the same as our InternalPort, since that is effectively our externally-visible port too. // Again, RequestedPort retains the external port we'd like to get the next time we find ourself behind a NAT gateway. // To improve stability of port mappings, RequestedPort is updated any time we get a successful - // mapping response from the NAT-PMP or UPnP gateway. For example, if we ask for port 80, and + // mapping response from the PCP, NAT-PMP or UPnP gateway. For example, if we ask for port 80, and // get assigned port 81, then thereafter we'll contine asking for port 81. mDNSInterfaceID InterfaceID; mDNSv4Addr ExternalAddress; // Initially set to onesIPv4Addr, until first callback + mDNSv4Addr NewAddress; // May be updated with actual value assigned by gateway mDNSIPPort ExternalPort; mDNSu32 Lifetime; mStatus Result; @@ -1124,6 +1301,12 @@ struct NATTraversalInfo_struct void *clientContext; }; +// *************************************************************************** +#if 0 +#pragma mark - +#pragma mark - DNSServer & McastResolver structures and constants +#endif + enum { DNSServer_Untested = 0, @@ -1153,10 +1336,27 @@ typedef struct McastResolver mDNSu32 timeout; // timeout value for questions } McastResolver; +// scoped values for DNSServer matching +enum +{ + kScopeNone = 0, // DNS server used by unscoped questions + kScopeInterfaceID = 1, // Scoped DNS server used only by scoped questions + kScopeServiceID = 2 // Service specific DNS server used only by questions + // have a matching serviceID +}; + +// Note: DNSSECAware is set if we are able to get a valid response to +// a DNSSEC question. In some cases it is possible that the proxy +// strips the EDNS0 option and we just get a plain response with no +// signatures. But we still mark DNSSECAware in that case. As DNSSECAware +// is only used to determine whether DNSSEC_VALIDATION_SECURE_OPTIONAL +// should be turned off or not, it is sufficient that we are getting +// responses back. typedef struct DNSServer { struct DNSServer *next; - mDNSInterfaceID interface; // For specialized uses; we can have DNS servers reachable over specific interfaces + mDNSInterfaceID interface; // DNS requests should be sent on this interface + mDNSs32 serviceID; mDNSAddr addr; mDNSIPPort port; mDNSOpaque16 testid; @@ -1164,15 +1364,29 @@ typedef struct DNSServer mDNSu32 teststate; // Have we sent bug-detection query to this server? mDNSs32 lasttest; // Time we sent last bug-detection query to this server domainname domain; // name->server matching for "split dns" - mDNSs32 penaltyTime; // amount of time this server is penalized - mDNSBool scoped; // interface should be matched against question only - // if scoped is set + mDNSs32 penaltyTime; // amount of time this server is penalized + mDNSu32 scoped; // See the scoped enum above mDNSu32 timeout; // timeout value for questions mDNSBool cellIntf; // Resolver from Cellular Interface ? mDNSu16 resGroupID; // ID of the resolver group that contains this DNSServer + mDNSBool req_A; // If set, send v4 query (DNSConfig allows A queries) + mDNSBool req_AAAA; // If set, send v6 query (DNSConfig allows AAAA queries) + mDNSBool req_DO; // If set, okay to send DNSSEC queries (EDNS DO bit is supported) + mDNSBool retransDO; // Total Retransmissions for queries sent with DO option + mDNSBool DNSSECAware; // set if we are able to receive a response to a request + // sent with DO option. } DNSServer; -typedef struct // Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit +typedef struct +{ + mDNSu8 *AnonData; + int AnonDataLen; + mDNSu32 salt; + ResourceRecord *nsec3RR; + mDNSInterfaceID SendNow; // The interface ID that this record should be sent on +} AnonymousInfo; + +struct ResourceRecord_struct { mDNSu8 RecordType; // See enum above mDNSu16 rrtype; @@ -1197,7 +1411,9 @@ typedef struct // Size is 36 bytes when compiling for 3 const domainname *name; RData *rdata; // Pointer to storage for this rdata DNSServer *rDNSServer; // Unicast DNS server authoritative for this entry;null for multicast -} ResourceRecord; + AnonymousInfo *AnonInfo; // Anonymous Information +}; + // Unless otherwise noted, states may apply to either independent record registrations or service registrations typedef enum @@ -1239,7 +1455,9 @@ struct AuthGroup_struct // Header object for a list of AuthRecords w mDNSu8 namestorage[AUTH_GROUP_NAME_SIZE]; }; +#ifndef AUTH_HASH_SLOTS #define AUTH_HASH_SLOTS 499 +#endif #define FORALL_AUTHRECORDS(SLOT,AG,AR) \ for ((SLOT) = 0; (SLOT) < AUTH_HASH_SLOTS; (SLOT)++) \ for ((AG)=m->rrauth.rrauth_hash[(SLOT)]; (AG); (AG)=(AG)->next) \ @@ -1262,10 +1480,16 @@ typedef enum AuthRecordAny, // registered for *Any, NOT including P2P interfaces AuthRecordAnyIncludeP2P, // registered for *Any, including P2P interfaces AuthRecordAnyIncludeAWDL, // registered for *Any, including AWDL interface + AuthRecordAnyIncludeAWDLandP2P, // registered for *Any, including AWDL and P2P interfaces AuthRecordLocalOnly, AuthRecordP2P // discovered over D2D/P2P framework } AuthRecType; +typedef enum +{ + AuthFlagsWakeOnly = 0x1 // WakeOnly service +} AuthRecordFlags; + struct AuthRecord_struct { // For examples of how to set up this structure for use in mDNS_Register(), @@ -1287,6 +1511,7 @@ struct AuthRecord_struct mDNSu8 AutoTarget; // Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name mDNSu8 AllowRemoteQuery; // Set if we allow hosts not on the local link to query this record mDNSu8 ForceMCast; // Set by client to advertise solely via multicast, even for apparently unicast names + mDNSu8 AuthFlags; OwnerOptData WakeUp; // WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record mDNSAddr AddressProxy; // For reverse-mapping Sleep Proxy PTR records, address in question @@ -1297,6 +1522,7 @@ struct AuthRecord_struct // Field Group 3: Transient state for Authoritative Records mDNSu8 Acknowledged; // Set if we've given the success callback to the client + mDNSu8 ProbeRestartCount; // Number of times we have restarted probing mDNSu8 ProbeCount; // Number of probes remaining before this record is valid (kDNSRecordTypeUnique) mDNSu8 AnnounceCount; // Number of announcements remaining (kDNSRecordTypeShared) mDNSu8 RequireGoodbye; // Set if this RR has been announced on the wire and will require a goodbye packet @@ -1376,16 +1602,17 @@ struct AuthRecord_struct // Note: Question_uDNS(Q) is used in *only* one place -- on entry to mDNS_StartQuery_internal, to decide whether to set TargetQID. // Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero. #define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name)) -#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || \ +#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || (Q)->ProxyQuestion || \ ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) #define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P) -#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P || (rr)->ARType == AuthRecordAnyIncludeAWDL) +#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P || (rr)->ARType == AuthRecordAnyIncludeAWDL || (rr)->ARType == AuthRecordAnyIncludeAWDLandP2P) // Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address -// is not available locally for A or AAAA question respectively -#define QuerySuppressed(Q) ((Q)->SuppressUnusable && (Q)->SuppressQuery) +// is not available locally for A or AAAA question respectively. Also, if the +// query is disallowed for the "pid" that we are sending on behalf of, suppress it. +#define QuerySuppressed(Q) (((Q)->SuppressUnusable && (Q)->SuppressQuery) || ((Q)->DisallowPID)) #define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel) @@ -1414,8 +1641,9 @@ struct CacheRecord_struct mDNSs32 LastUsed; // In platform time units 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 - mDNSu16 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer - mDNSu16 rcode; // Error code needed for NSEC proofs + mDNSu8 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer + mDNSu8 CRDNSSECQuestion; // Set to 1 if this was created in response to a DNSSEC question + mDNSOpaque16 responseFlags; // Second 16 bit in the DNS response #if ENABLE_MULTI_PACKET_QUERY_SNOOPING mDNSu32 MPUnansweredQ; // Multi-packet query handling: Number of times we've seen a query for this record mDNSs32 MPLastUnansweredQT; // Multi-packet query handling: Last time we incremented MPUnansweredQ @@ -1424,6 +1652,7 @@ struct CacheRecord_struct #endif CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set CacheRecord *nsec; // NSEC records needed for non-existence proofs + CacheRecord *soa; // SOA record to return for proxy questions mDNSAddr sourceAddress; // node from which we received this record // Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit @@ -1511,6 +1740,9 @@ struct ServiceRecordSet_struct ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration mDNSu32 NumSubTypes; AuthRecord *SubTypes; + const mDNSu8 *AnonData; + mDNSu32 flags; // saved for subsequent calls to mDNS_RegisterService() if records + // need to be re-registered. AuthRecord RR_ADV; // e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local. AuthRecord RR_PTR; // e.g. _printer._tcp.local. PTR Name._printer._tcp.local. AuthRecord RR_SRV; // e.g. Name._printer._tcp.local. SRV 0 0 port target @@ -1609,8 +1841,11 @@ typedef struct DomainAuthInfo // layer. These values are used within mDNSResponder and not sent across to the application. QC_addnocache is for // delivering a response without adding to the cache. QC_forceresponse is superset of QC_addnocache where in // addition to not entering in the cache, it also forces the negative response through. -typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_dnssec } QC_result; +typedef enum { QC_rmv = 0, QC_add, QC_addnocache, QC_forceresponse, QC_dnssec , QC_nodnssec, QC_suppressed } QC_result; typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord); +typedef void AsyncDispatchFunc(mDNS *const m, void *context); +typedef void DNSSECAuthInfoFreeCallback(mDNS *const m, void *context); +extern void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func); #define NextQSendTime(Q) ((Q)->LastQTime + (Q)->ThisQInterval) #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf) @@ -1621,7 +1856,33 @@ typedef void mDNSQuestionCallback (mDNS *const m, DNSQuestion *question, const R // questions from /etc/hosts, then we go straight to DNSSECValDone from the initial state. typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress, DNSSECValDone } DNSSECValState; -#define DNSSECQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse) +// ValidationRequired can be set to the following values: +// +// SECURE validation is set to determine whether something is secure or bogus +// INSECURE validation is set internally by dnssec code to indicate that it is currently proving something +// is insecure +#define DNSSEC_VALIDATION_NONE 0x00 +#define DNSSEC_VALIDATION_SECURE 0x01 +#define DNSSEC_VALIDATION_SECURE_OPTIONAL 0x02 +#define DNSSEC_VALIDATION_INSECURE 0x03 + +// For both ValidationRequired and ValidatingResponse question, we validate DNSSEC responses. +// For ProxyQuestion with DNSSECOK, we just receive the DNSSEC records to pass them along without +// validation and if the CD bit is not set, we also validate. +#define DNSSECQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse || ((q)->ProxyQuestion && (q)->ProxyDNSSECOK)) + +// ValidatingQuestion is used when we need to know whether we are validating the DNSSEC responses for a question +#define ValidatingQuestion(q) ((q)->ValidationRequired || (q)->ValidatingResponse) + +#define DNSSECOptionalQuestion(q) ((q)->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL) + +// Given the resource record and the question, should we follow the CNAME ? +#define FollowCNAME(q, rr, AddRecord) (AddRecord && (q)->qtype != kDNSType_CNAME && \ + (rr)->RecordType != kDNSRecordTypePacketNegative && \ + (rr)->rrtype == kDNSType_CNAME) + +// RFC 4122 defines it to be 16 bytes +#define UUID_SIZE 16 struct DNSQuestion_struct { @@ -1638,32 +1899,47 @@ struct DNSQuestion_struct mDNSs32 LastAnswerPktNum; // The sequence number of the last response packet containing an answer to this Q mDNSu32 RecentAnswerPkts; // Number of answers since the last time we sent this query mDNSu32 CurrentAnswers; // Number of records currently in the cache that answer this question + mDNSu32 BrowseThreshold; // If we have received at least this number of answers, + // set the next question interval to MaxQuestionInterval mDNSu32 LargeAnswers; // Number of answers with rdata > 1024 bytes mDNSu32 UniqueAnswers; // Number of answers received with kDNSClass_UniqueRRSet bit set - mDNSInterfaceID FlappingInterface1; // Set when an interface goes away, to flag if remove events are delivered for this Q - mDNSInterfaceID FlappingInterface2; // Set when an interface goes away, to flag if remove events are delivered for this Q + mDNSInterfaceID FlappingInterface1; // Set when an interface goes away, to flag if remove events are delivered for this Q + mDNSInterfaceID FlappingInterface2; // Set when an interface goes away, to flag if remove events are delivered for this Q DomainAuthInfo *AuthInfo; // Non-NULL if query is currently being done using Private DNS DNSQuestion *DuplicateOf; DNSQuestion *NextInDQList; + AnonymousInfo *AnonInfo; // Anonymous Information DupSuppressInfo DupSuppress[DupSuppressInfoSize]; mDNSInterfaceID SendQNow; // The interface this query is being sent on right now mDNSBool SendOnAll; // Set if we're sending this question on all active interfaces + mDNSBool CachedAnswerNeedsUpdate; // See SendQueries(). Set if we're sending this question + // because a cached answer needs to be refreshed. mDNSu32 RequestUnicast; // Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set mDNSs32 LastQTxTime; // Last time this Q was sent on one (but not necessarily all) interfaces mDNSu32 CNAMEReferrals; // Count of how many CNAME redirections we've done mDNSBool SuppressQuery; // This query should be suppressed and not sent on the wire mDNSu8 LOAddressAnswers; // Number of answers from the local only auth records that are - // answering A, AAAA and CNAME (/etc/hosts) - mDNSu8 WakeOnResolveCount; // Number of wakes that should be sent on resolve + // answering A, AAAA, CNAME, or PTR (/etc/hosts) + mDNSu8 WakeOnResolveCount; // Number of wakes that should be sent on resolve mDNSs32 StopTime; // Time this question should be stopped by giving them a negative answer - // Wide Area fields. These are used internally by the uDNS core + // DNSSEC fields + DNSSECValState ValidationState; // Current state of the Validation process + DNSSECStatus ValidationStatus; // Validation status for "ValidationRequired" questions (dnssec) + mDNSu8 ValidatingResponse; // Question trying to validate a response (dnssec) on behalf of + // ValidationRequired question + void *DNSSECAuthInfo; + DNSSECAuthInfoFreeCallback *DAIFreeCallback; + + // Wide Area fields. These are used internally by the uDNS core (Unicast) UDPSocket *LocalSocket; + + // |-> DNS Configuration related fields used in uDNS (Subset of Wide Area/Unicast fields) DNSServer *qDNSServer; // Caching server for this query (in the absence of an SRV saying otherwise) mDNSOpaque64 validDNSServers; // Valid DNSServers for this question 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 + mDNSu16 triedAllServersOnce; // Tried all DNS servers once + mDNSu8 unansweredQueries; // The number of unanswered queries to this server 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 @@ -1672,6 +1948,7 @@ struct DNSQuestion_struct mDNSIPPort tcpSrcPort; // Local Port TCP packet received on;need this as tcp struct is disposed // by tcpCallback before calling into mDNSCoreReceive mDNSu8 NoAnswer; // Set if we want to suppress answers until tunnel setup has completed + mDNSu8 Restart; // This question should be restarted soon // LLQ-specific fields. These fields are only meaningful when LongLived flag is set LLQ_State state; @@ -1681,12 +1958,14 @@ struct DNSQuestion_struct // for TCP: there is some ambiguity in the use of this variable, but in general, it is // the number of TCP/TLS connection attempts for this LLQ state, or // the number of packets sent for this TCP/TLS connection - DNSSECValState ValidationState; // Current state of the Validation process - DNSSECStatus ValidationStatus; // Validation status for "ValidationRequired" questions (dnssec) - mDNSu8 ValidatingResponse; // Question trying to validate a response (dnssec) on behalf of - // ValidationRequired question mDNSOpaque64 id; + // DNS Proxy fields + mDNSOpaque16 responseFlags; // Temporary place holder for the error we get back from the DNS server + // till we populate in the cache + mDNSBool DisallowPID; // Is the query allowed for the "PID" that we are sending on behalf of ? + mDNSs32 ServiceID; // Service identifier to match against the DNS server + // Client API fields: The client must set up these fields *before* calling mDNS_StartQuery() mDNSInterfaceID InterfaceID; // Non-zero if you want to issue queries only on a single specific IP interface mDNSu32 flags; // flags from original DNSService*() API request. @@ -1701,15 +1980,19 @@ struct DNSQuestion_struct mDNSBool ForceMCast; // Set by client to force mDNS query, even for apparently uDNS names mDNSBool ReturnIntermed; // Set by client to request callbacks for intermediate CNAME/NXDOMAIN results mDNSBool SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire - mDNSBool RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords + mDNSu8 RetryWithSearchDomains; // Retry with search domains if there is no entry in the cache or AuthRecords mDNSu8 TimeoutQuestion; // Timeout this question if there is no reply in configured time mDNSu8 WakeOnResolve; // Send wakeup on resolve - mDNSu8 UseBrackgroundTrafficClass; // Use background traffic class for request + mDNSu8 UseBackgroundTrafficClass; // Use background traffic class for request mDNSs8 SearchListIndex; // Index into SearchList; Used by the client layer but not touched by core - mDNSs8 AppendSearchDomains; // Search domains can be appended for this query - mDNSs8 AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query - mDNSu8 ValidationRequired; // Requires DNSSEC validation. - domainname *qnameOrig; // Copy of the original question name if it is not fully qualified + mDNSs8 AppendSearchDomains; // Search domains can be appended for this query + mDNSs8 AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query + mDNSu8 ValidationRequired; // Requires DNSSEC validation. + mDNSu8 ProxyQuestion; // Proxy Question + mDNSu8 ProxyDNSSECOK; // Proxy Question with EDNS0 DNSSEC OK bit set + mDNSs32 pid; // Process ID of the client that is requesting the question + mDNSu8 uuid[UUID_SIZE]; // Unique ID of the client that is requesting the question (valid only if pid is zero) + domainname *qnameOrig; // Copy of the original question name if it is not fully qualified mDNSQuestionCallback *QuestionCallback; void *QuestionContext; }; @@ -1853,10 +2136,14 @@ struct NetworkInterfaceInfo_struct mDNSu8 McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ? mDNSu8 NetWake; // Set if Wake-On-Magic-Packet is enabled on this interface mDNSu8 Loopback; // Set if this is the loopback interface + mDNSu8 IgnoreIPv4LL; // Set if IPv4 Link-Local addresses have to be ignored. + mDNSu8 SendGoodbyes; // Send goodbyes on this interface while sleeping }; -#define SLE_DELETE 0x00000001 -#define SLE_WAB_QUERY_STARTED 0x00000002 +#define SLE_DELETE 0x00000001 +#define SLE_WAB_BROWSE_QUERY_STARTED 0x00000002 +#define SLE_WAB_LBROWSE_QUERY_STARTED 0x00000004 +#define SLE_WAB_REG_QUERY_STARTED 0x00000008 typedef struct SearchListElem { @@ -1888,13 +2175,9 @@ extern SearchListElem *SearchList; // This really ought to be part of mDNS_ typedef void mDNSCallback (mDNS *const m, mStatus result); +#ifndef CACHE_HASH_SLOTS #define CACHE_HASH_SLOTS 499 - -enum // Bit flags -- i.e. values should be 1, 2, 4, 8, etc. -{ - mDNS_KnownBug_LimitedIPv6 = 1, - mDNS_KnownBug_LossySyslog = 2 // -}; +#endif enum { @@ -1903,6 +2186,74 @@ enum SleepState_Sleeping = 2 }; +typedef enum +{ + kStatsActionIncrement, + kStatsActionDecrement, + kStatsActionClear, + kStatsActionSet +} DNSSECStatsAction; + +typedef enum +{ + kStatsTypeMemoryUsage, + kStatsTypeLatency, + kStatsTypeExtraPackets, + kStatsTypeStatus, + kStatsTypeProbe, + kStatsTypeMsgSize +} DNSSECStatsType; + +typedef struct +{ + mDNSu32 TotalMemUsed; + mDNSu32 Latency0; // 0 to 4 ms + mDNSu32 Latency5; // 5 to 9 ms + mDNSu32 Latency10; // 10 to 19 ms + mDNSu32 Latency20; // 20 to 49 ms + mDNSu32 Latency50; // 50 to 99 ms + mDNSu32 Latency100; // >= 100 ms + mDNSu32 ExtraPackets0; // 0 to 2 packets + mDNSu32 ExtraPackets3; // 3 to 6 packets + mDNSu32 ExtraPackets7; // 7 to 9 packets + mDNSu32 ExtraPackets10; // >= 10 packets + mDNSu32 SecureStatus; + mDNSu32 InsecureStatus; + mDNSu32 IndeterminateStatus; + mDNSu32 BogusStatus; + mDNSu32 NoResponseStatus; + mDNSu32 NumProbesSent; // Number of probes sent + mDNSu32 MsgSize0; // DNSSEC message size <= 1024 + mDNSu32 MsgSize1; // DNSSEC message size <= 2048 + mDNSu32 MsgSize2; // DNSSEC message size > 2048 +} DNSSECStatistics; + +typedef struct +{ + mDNSu32 NameConflicts; // Normal Name conflicts + mDNSu32 KnownUniqueNameConflicts; // Name Conflicts for KnownUnique Records + mDNSu32 DupQuerySuppressions; // Duplicate query suppressions + mDNSu32 KnownAnswerSuppressions; // Known Answer suppressions + mDNSu32 KnownAnswerMultiplePkts; // Known Answer in queries spannign multiple packets + mDNSu32 PoofCacheDeletions; // Number of times the cache was deleted due to POOF + mDNSu32 UnicastBitInQueries; // Queries with QU bit set + mDNSu32 NormalQueries; // Queries with QU bit not set + mDNSu32 MatchingAnswersForQueries; // Queries for which we had a response + mDNSu32 UnicastResponses; // Unicast responses to queries + mDNSu32 MulticastResponses; // Multicast responses to queries + mDNSu32 UnicastDemotedToMulticast; // Number of times unicast demoted to multicast + mDNSu32 Sleeps; // Total sleeps + mDNSu32 Wakes; // Total wakes + mDNSu32 InterfaceUp; // Total Interface UP events + mDNSu32 InterfaceUpFlap; // Total Interface UP events with flaps + mDNSu32 InterfaceDown; // Total Interface Down events + mDNSu32 InterfaceDownFlap; // Total Interface Down events with flaps + mDNSu32 CacheRefreshQueries; // Number of queries that we sent for refreshing cache + mDNSu32 CacheRefreshed; // Number of times the cache was refreshed due to a response + mDNSu32 WakeOnResolves; // Number of times we did a wake on resolve +} mDNSStatistics; +extern void LogMDNSStatistics(mDNS *const m); + struct mDNS_struct { // Internal state fields. These hold the main internal state of mDNSCore; @@ -1911,7 +2262,6 @@ struct mDNS_struct // all required data is passed as parameters to that function. mDNS_PlatformSupport *p; // Pointer to platform-specific data of indeterminite size - mDNSu32 KnownBugs; mDNSBool CanReceiveUnicastOn5353; mDNSBool AdvertiseLocalAddresses; mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only @@ -1950,6 +2300,7 @@ struct mDNS_struct mDNSs32 RandomQueryDelay; // For de-synchronization of query packets on the wire mDNSu32 RandomReconfirmDelay; // For de-synchronization of reconfirmation queries on the wire mDNSs32 PktNum; // Unique sequence number assigned to each received packet + mDNSs32 MPktNum; // Unique sequence number assigned to each received Multicast packet mDNSu8 LocalRemoveEvents; // Set if we may need to deliver remove events for local-only questions and/or local-only records mDNSu8 SleepState; // Set if we're sleeping mDNSu8 SleepSeqNum; // "Epoch number" of our current period of wakefulness @@ -1957,16 +2308,24 @@ struct mDNS_struct mDNSu8 SentSleepProxyRegistration; // Set if we registered (or tried to register) with a Sleep Proxy mDNSu8 SystemSleepOnlyIfWakeOnLAN; // Set if we may only sleep if we managed to register with a Sleep Proxy mDNSs32 AnnounceOwner; // After waking from sleep, include OWNER option in packets until this time - mDNSs32 clearIgnoreNA; // After waking from sleep, clear the ignore neighbor advertisement after this time mDNSs32 DelaySleep; // To inhibit re-sleeping too quickly right after wake mDNSs32 SleepLimit; // Time window to allow deregistrations, etc., // during which underying platform layer should inhibit system sleep + mDNSs32 TimeSlept; // Time we went to sleep. + + mDNSs32 StatStartTime; // Time we started gathering statistics during this interval. + mDNSs32 NextStatLogTime; // Next time to log statistics. + mDNSs32 ActiveStatTime; // Total time awake/gathering statistics for this log period. + mDNSs32 UnicastPacketsSent; // Number of unicast packets sent. + mDNSs32 MulticastPacketsSent; // Number of multicast packets sent. + mDNSs32 RemoteSubnet; // Multicast packets received from outside our subnet. + mDNSs32 NextScheduledSPRetry; // Time next sleep proxy registration action is required. // Only valid if SleepLimit is nonzero and DelaySleep is zero. mDNSs32 NextScheduledStopTime; // Next time to stop a question - mDNSs32 ClearSPSRecords; // Time to clear stored Addr/AAAA records that were registered with a Sleep Proxy - + + // These fields only required for mDNS Searcher... DNSQuestion *Questions; // List of all registered questions, active and inactive DNSQuestion *NewQuestions; // Fresh questions not yet answered from cache @@ -1977,6 +2336,7 @@ struct mDNS_struct DNSQuestion *ValidationQuestion; // Questions that are being validated (dnssec) mDNSu32 rrcache_size; // Total number of available cache entries mDNSu32 rrcache_totalused; // Number of cache entries currently occupied + mDNSu32 rrcache_totalused_unicast; // Number of cache entries currently occupied by unicast mDNSu32 rrcache_active; // Number of cache entries currently occupied by records that answer active questions mDNSu32 rrcache_report; CacheEntity *rrcache_free; @@ -2001,6 +2361,7 @@ struct mDNS_struct mDNSs32 ProbeFailTime; mDNSu32 NumFailedProbes; mDNSs32 SuppressProbes; + Platform_t mDNS_plat; // Unicast-specific data mDNSs32 NextuDNSEvent; // uDNS next event @@ -2023,18 +2384,21 @@ struct mDNS_struct NATTraversalInfo AutoTunnelNAT; // Shared between all AutoTunnel DomainAuthInfo structs mDNSv6Addr AutoTunnelRelayAddr; - mDNSBool StartWABQueries; // Start WAB queries for the purpose of domain enumeration + mDNSu32 WABBrowseQueriesCount; // Number of WAB Browse domain enumeration queries (b, db) callers + mDNSu32 WABLBrowseQueriesCount; // Number of legacy WAB Browse domain enumeration queries (lb) callers + mDNSu32 WABRegQueriesCount; // Number of WAB Registration domain enumeration queries (r, dr) callers mDNSu8 SearchDomainsHash[MD5_LEN]; // NAT-Traversal fields NATTraversalInfo LLQNAT; // Single shared NAT Traversal to receive inbound LLQ notifications NATTraversalInfo *NATTraversals; NATTraversalInfo *CurrentNATTraversal; - mDNSs32 retryIntervalGetAddr; // delta between time sent and retry - mDNSs32 retryGetAddr; // absolute time when we retry - mDNSv4Addr ExternalAddress; + mDNSs32 retryIntervalGetAddr; // delta between time sent and retry for NAT-PMP & UPnP/IGD external address request + mDNSs32 retryGetAddr; // absolute time when we retry for NAT-PMP & UPnP/IGD external address request + mDNSv4Addr ExtAddress; // the external address discovered via NAT-PMP or UPnP/IGD + mDNSu32 PCPNonce[3]; // the nonce if using PCP - UDPSocket *NATMcastRecvskt; // For receiving NAT-PMP AddrReply multicasts from router on port 5350 + UDPSocket *NATMcastRecvskt; // For receiving PCP & NAT-PMP announcement multicasts from router on port 5350 mDNSu32 LastNATupseconds; // NAT engine uptime in seconds, from most recent NAT packet mDNSs32 LastNATReplyLocalTime; // Local time in ticks when most recent NAT packet was received mDNSu16 LastNATMapResultCode; // Most recent error code for mappings @@ -2054,7 +2418,7 @@ struct mDNS_struct mDNSu8 *UPnPSOAPAddressString; // holds both address and port for SOAP messages // Sleep Proxy client fields - AuthRecord *SPSRRSet; // To help the client keep track of the records registered with the sleep proxy + AuthRecord *SPSRRSet; // To help the client keep track of the records registered with the sleep proxy // Sleep Proxy Server fields mDNSu8 SPSType; // 0 = off, 10-99 encodes desirability metric @@ -2065,7 +2429,9 @@ struct mDNS_struct mDNSu8 SPSState; // 0 = off, 1 = running, 2 = shutting down, 3 = suspended during sleep mDNSInterfaceID SPSProxyListChanged; UDPSocket *SPSSocket; +#ifndef SPC_DISABLED ServiceRecordSet SPSRecords; +#endif mDNSQuestionCallback *SPSBrowseCallback; // So the platform layer can do something useful with SPS browse results int ProxyRecords; // Total number of records we're holding as proxy #define MAX_PROXY_RECORDS 10000 /* DOS protection: 400 machines at 25 records each */ @@ -2075,9 +2441,17 @@ struct mDNS_struct uuid_t asl_uuid; // uuid for ASL logging void *WCF; #endif + // DNS Proxy fields + mDNSu32 dp_ipintf[MaxIp]; // input interface index list from the DNS Proxy Client + mDNSu32 dp_opintf; // output interface index from the DNS Proxy Client + TrustAnchor *TrustAnchors; int notifyToken; - mDNSBool mDNSHandlePeerEvents; // Handle AWDL Peer Events + int uds_listener_skt; // Listening socket for incoming UDS clients + mDNSBool mDNSOppCaching; // Opportunistic Caching + mDNSu32 AutoTargetServices; // # of services that have AutoTarget set + DNSSECStatistics DNSSECStats; + mDNSStatistics mDNSStats; // Fixed storage, to avoid creating large objects on the stack // The imsg is declared as a union with a pointer type to enforce CPU-appropriate alignment @@ -2102,6 +2476,7 @@ extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value extern const mDNSInterfaceID mDNSInterface_Unicast; // Special value extern const mDNSInterfaceID mDNSInterfaceMark; // Special value extern const mDNSInterfaceID mDNSInterface_P2P; // Special value +extern const mDNSInterfaceID uDNSInterfaceMark; // Special value extern const mDNSIPPort DiscardPort; extern const mDNSIPPort SSHPort; @@ -2151,6 +2526,7 @@ extern mDNSu8 NumUnicastDNSServers; #define localdomain (*(const domainname *)"\x5" "local") #define DeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp") +#define LocalDeviceInfoName (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp" "\x5" "local") #define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp") // *************************************************************************** @@ -2282,6 +2658,7 @@ extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question); extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question); extern mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question); extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr); +extern mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval); extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr); extern void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr); extern mDNSs32 mDNS_TimeNow(const mDNS *const m); @@ -2346,7 +2723,8 @@ enum { coreFlagIncludeP2P = 0x1, // include P2P interfaces when using mDNSInterface_Any coreFlagIncludeAWDL = 0x2, // include AWDL interface when using mDNSInterface_Any - coreFlagKnownUnique = 0x4 // client guarantees that SRV and TXT record names are unique + coreFlagKnownUnique = 0x4, // client guarantees that SRV and TXT record names are unique + coreFlagWakeOnly = 0x8 // Service won't be registered with sleep proxy }; extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr, @@ -2370,7 +2748,7 @@ extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID Inter const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context); extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, - const domainname *const srv, const domainname *const domain, + const domainname *const srv, const domainname *const domain, const mDNSu8 *anondata, const mDNSInterfaceID InterfaceID, mDNSu32 flags, mDNSBool ForceMCast, mDNSBool useBackgroundTrafficClass, mDNSQuestionCallback *Callback, void *Context); @@ -2399,9 +2777,8 @@ extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainT #define mDNS_StopAdvertiseDomains mDNS_Deregister extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m); -extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr); +extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr, mDNSBool *myself); -extern DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID); extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question); extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); @@ -2513,9 +2890,13 @@ extern char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataB #define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer) extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2); extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText); -extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1918 private addresses +extern mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr); // returns true for RFC1918 private addresses #define mDNSAddrIsRFC1918(X) ((X)->type == mDNSAddrType_IPv4 && mDNSv4AddrIsRFC1918(&(X)->ip.v4)) +// For PCP +extern void mDNSAddrMapIPv4toIPv6(mDNSv4Addr* in, mDNSv6Addr* out); +extern mDNSBool mDNSAddrIPv4FromMappedIPv6(mDNSv6Addr *in, mDNSv4Addr *out); + #define mDNSSameIPPort(A,B) ((A).NotAnInteger == (B).NotAnInteger) #define mDNSSameOpaque16(A,B) ((A).NotAnInteger == (B).NotAnInteger) #define mDNSSameOpaque32(A,B) ((A).NotAnInteger == (B).NotAnInteger) @@ -2589,7 +2970,7 @@ extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr); // returns true for RFC1 extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel); -extern void RecreateNATMappings(mDNS *const m); +extern void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks); // Hostname/Unicast Interface Configuration @@ -2611,8 +2992,10 @@ extern void RecreateNATMappings(mDNS *const m); extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext); extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn); extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router); -extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID); -extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q); +extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSs32 serviceID, const mDNSAddr *addr, + const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, + mDNSBool reqAAAA, mDNSBool reqDO); +extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags); extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID); extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout); @@ -2689,6 +3072,7 @@ extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, mDNSBool useBackgroundTrafficClass); +extern mDNSBool mDNSPlatformPeekUDP (mDNS *const m, UDPSocket *src); extern void mDNSPlatformLock (const mDNS *const m); extern void mDNSPlatformUnlock (const mDNS *const m); @@ -2732,9 +3116,12 @@ extern void mDNSPlatformWriteLogMsg(const char *ident, const char *msg, mDNSLogL // Utility function for ASL logging mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...); -// Function to toggle IPv6 advertisements -mDNSexport void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt); -#endif +// Log unicast and multicast traffic statistics once a day. Also used for DNSSEC statistics. +#define kDefaultNextStatsticsLogTime (24 * 60 * 60) + +extern void mDNSLogStatistics(mDNS *const m); + +#endif // APPLE_OSX_mDNSResponder // Platform support modules should provide the following functions to map between opaque interface IDs // and interface indexes in order to support the DNS-SD API. If your target platform does not support @@ -2775,6 +3162,7 @@ extern void mDNSPlatformTCPCloseConnection(TCPSocket *sock); extern long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed); extern long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len); extern UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport); +extern mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock); extern void mDNSPlatformUDPClose(UDPSocket *sock); extern void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd); extern void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID); @@ -2782,7 +3170,9 @@ extern void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 extern void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID); extern void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst); extern void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu32 seq, mDNSu32 ack, mDNSu16 win); -extern mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti); +extern mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti); +extern mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr); +extern mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname); // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd extern mStatus mDNSPlatformTLSSetupCerts(void); @@ -2791,7 +3181,8 @@ extern void mDNSPlatformTLSTearDownCerts(void); // Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain // in browse/registration calls must implement these routines to get the "default" browse/registration list. -extern void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains); +extern mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, + DNameListElem **BrowseDomains, mDNSBool ackConfig); extern mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router); extern void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status); @@ -2894,6 +3285,9 @@ extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr); extern void CheckSuppressUnusableQuestions(mDNS *const m); extern void RetrySearchDomainQuestions(mDNS *const m); extern mDNSBool DomainEnumQuery(const domainname *qname); +extern mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkInterfaceInfo *const intf, mDNSBool updateMac, char *ethAddr); +extern void UpdateKeepaliveRMACAsync(mDNS *const m, void *context); +extern void UpdateRMACCallback(mDNS *const m, void *context); // Used only in logging to restrict the number of /etc/hosts entries printed extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result); @@ -2913,14 +3307,32 @@ extern void StartServerTunnel(mDNS *const m, DomainAuthInfo *const info); extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m); extern void RemoveAutoTunnel6Record(mDNS *const m); extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr); -#endif - // For now this LocalSleepProxy stuff is specific to Mac OS X. // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer +extern mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf); +extern void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q); +extern void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q); +extern void mDNSPlatformLogToFile(int log_level, const char *buffer); +extern mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf); +#endif + +typedef void ProxyCallback (mDNS *const m, void *socket, void *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, + const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID, void *context); +extern void mDNSPlatformInitDNSProxySkts(mDNS *const m, ProxyCallback *UDPCallback, ProxyCallback *TCPCallback); +extern void mDNSPlatformCloseDNSProxySkts(mDNS *const m); +extern void mDNSPlatformDisposeProxyContext(void *context); +extern mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *start, mDNSu8 *limit); + +// Sleep Assertions are specific to Mac OS X #if APPLE_OSX_mDNSResponder -extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname); +extern void mDNSPlatformSleepAssertion(mDNS *const m, double timeout); #endif +extern mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q); +extern mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q); +extern void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q); +extern mDNSs32 mDNSPlatformGetPID(void); + // *************************************************************************** #if 0 #pragma mark - @@ -3051,6 +3463,13 @@ typedef enum mDNSSleepProxyMetric_IncidentalSoftware = 80 } mDNSSleepProxyMetric; +typedef enum +{ + mDNS_NoWake = 0, // System does not support Wake on LAN + mDNS_WakeOnAC = 1, // System supports Wake on LAN when connected to AC power only + mDNS_WakeOnBattery = 2 // System supports Wake on LAN on battery +} mDNSWakeForNetworkAccess; + extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower, mDNSu8 features); #define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP,F) \ do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP),(F)); mDNS_Unlock(m); } while(0) @@ -3125,24 +3544,36 @@ struct CompileTimeAssertionChecks_mDNS // 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_RDataBody [(sizeof(RDataBody) == 264) ? 1 : -1]; - char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 64) ? 1 : -1]; + char sizecheck_ResourceRecord [(sizeof(ResourceRecord) <= 72) ? 1 : -1]; char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1]; - char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 216) ? 1 : -1]; - char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 216) ? 1 : -1]; - char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 786) ? 1 : -1]; - char sizecheck_ZoneData [(sizeof(ZoneData) <= 1624) ? 1 : -1]; - char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 192) ? 1 : -1]; + char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 232) ? 1 : -1]; + char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 232) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 832) ? 1 : -1]; + +// Checks commented out when sizeof(DNSQuestion) change cascaded into having to change yet another +// set of hardcoded size values because these structures contain one or more DNSQuestion +// instances. +// char sizecheck_ZoneData [(sizeof(ZoneData) <= 1648) ? 1 : -1]; + char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 200) ? 1 : -1]; char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; - char sizecheck_DNSServer [(sizeof(DNSServer) <= 328) ? 1 : -1]; - char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6850) ? 1 : -1]; - char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5500) ? 1 : -1]; + char sizecheck_DNSServer [(sizeof(DNSServer) <= 340) ? 1 : -1]; +// char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 6988) ? 1 : -1]; + char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5540) ? 1 : -1]; char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1]; - char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3200) ? 1 : -1]; +// char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3302) ? 1 : -1]; #if APPLE_OSX_mDNSResponder - char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1148) ? 1 : -1]; +// char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1160) ? 1 : -1]; #endif }; +// Routine to initialize device-info TXT record contents +mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr); + +#if APPLE_OSX_mDNSResponder +extern void D2D_start_advertising_interface(NetworkInterfaceInfo *interface); +extern void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface); +#endif + // *************************************************************************** #ifdef __cplusplus diff --git a/mDNSCore/nsec.c b/mDNSCore/nsec.c index b230971..a9f16b3 100644 --- a/mDNSCore/nsec.c +++ b/mDNSCore/nsec.c @@ -23,6 +23,12 @@ #include "mDNSEmbeddedAPI.h" #include "DNSCommon.h" #include "nsec.h" +#include "nsec3.h" + +// Define DNSSEC_DISABLED to remove all the DNSSEC functionality +// and use the stub functions implemented later in this file. + +#ifndef DNSSEC_DISABLED // Implementation Notes // @@ -67,6 +73,13 @@ mDNSlocal CacheRecord *NSECParentForQuestion(mDNS *const m, DNSQuestion *q) return mDNSNULL; } +mDNSlocal void UpdateParent(DNSSECVerifier *dv) +{ + AuthChainLink(dv->parent, dv->ac); + ResetAuthChain(dv); + dv->parent->NumPackets += dv->NumPackets; +} + // Note: This should just call the parent callback which will free the DNSSECVerifier. mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) { @@ -78,13 +91,21 @@ mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu } if (dv->ac) { - // Before we call the callback, we need to update the + // Before we free the "dv", we need to update the // parent with our AuthChain information - AuthChainLink(dv->parent, dv->ac); - dv->ac = mDNSNULL; - dv->actail = &dv->ac; + UpdateParent(dv); + } + // "status" indicates whether we are able to successfully verify + // the NSEC/NSEC3 signatures. For NSEC3, the OptOut flag may be set + // for which we need to deliver insecure result. + if ((dv->parent->flags & NSEC3_OPT_OUT) && (status == DNSSEC_Secure)) + { + dv->parent->DVCallback(m, dv->parent, DNSSEC_Insecure); + } + else + { + dv->parent->DVCallback(m, dv->parent, status); } - dv->parent->DVCallback(m, dv->parent, status); // The callback we called in the previous line should recursively // free all the DNSSECVerifiers starting from dv->parent and above. // So, set that to NULL and free the "dv" itself here. @@ -108,8 +129,7 @@ mDNSlocal void VerifyNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu // them based on the name hash like other records as in most cases the returned NSECs has a different name than we asked for // (except for NODATA error where the name exists but type does not exist). // -mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, - DNSSECVerifierCallback callback) +mDNSexport void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, DNSSECVerifierCallback callback) { DNSSECVerifier *dv = mDNSNULL; CacheRecord **rp; @@ -142,8 +162,13 @@ mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNS rrtype = rv->rrtype; } - dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, (callback ? callback : VerifyNSECCallback), mDNSNULL); - if (!dv) { LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); return; } + dv = AllocateDNSSECVerifier(m, name, rrtype, pdv->q.InterfaceID, DNSSEC_VALIDATION_SECURE, + (callback ? callback : VerifyNSECCallback), mDNSNULL); + if (!dv) + { + LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); + return; + } dv->parent = pdv; @@ -164,11 +189,14 @@ mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNS rp=&(*rp)->next; } - if (!dv->rrset || !dv->rrsig) + if (!dv->rrset) { - LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset %p, rrsig %p", dv->rrset, dv->rrsig); + LogMsg("VerifyNSEC: ERROR!! AddRRSetToVerifier missing rrset"); goto error; } + // Expired signatures. + if (!dv->rrsig) + goto error; // Next step is to fetch the keys dv->next = RRVS_key; @@ -176,7 +204,7 @@ mDNSlocal void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNS StartDNSSECVerification(m, dv); return; error: - pdv->DVCallback(m, pdv, DNSSEC_Indeterminate); + pdv->DVCallback(m, pdv, DNSSEC_Bogus); if (dv) { dv->parent = mDNSNULL; @@ -202,7 +230,9 @@ mDNSlocal void DeleteCachedNSECS(mDNS *const m, CacheRecord *cr) // failure (mDNSfalse) mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode) { - CacheRecord *cr, *next; + CacheRecord *cr; + mDNSBool nsecs_seen = mDNSfalse; + mDNSBool nsec3s_seen = mDNSfalse; if (rcode != kDNSFlag1_RC_NoErr && rcode != kDNSFlag1_RC_NXDomain) { @@ -214,8 +244,8 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C // NSECs and its RRSIGs for (cr = crlist; cr; cr = cr->next) { - next = cr->next; - if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_RRSIG) + if (cr->resrec.rrtype != kDNSType_NSEC && cr->resrec.rrtype != kDNSType_NSEC3 && + cr->resrec.rrtype != kDNSType_SOA && cr->resrec.rrtype != kDNSType_RRSIG) { LogMsg("AddNSECSForCacheRecord: ERROR!! Adding Wrong record %s", CRDisplayString(m, cr)); return mDNSfalse; @@ -224,24 +254,37 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C { RDataBody2 *const rdb = (RDataBody2 *)cr->smallrdatastorage.data; rdataRRSig *rrsig = &rdb->rrsig; - if (swap16(rrsig->typeCovered) != kDNSType_NSEC) + mDNSu16 tc = swap16(rrsig->typeCovered); + if (tc != kDNSType_NSEC && tc != kDNSType_NSEC3 && tc != kDNSType_SOA) { LogMsg("AddNSECSForCacheRecord:ERROR!! Adding RRSIG with Wrong type %s", CRDisplayString(m, cr)); return mDNSfalse; } } + else if (cr->resrec.rrtype == kDNSType_NSEC) + { + nsecs_seen = mDNStrue; + } + else if (cr->resrec.rrtype == kDNSType_NSEC3) + { + nsec3s_seen = mDNStrue; + } LogDNSSEC("AddNSECSForCacheRecord: Found a valid record %s", CRDisplayString(m, cr)); } + if ((nsecs_seen && nsec3s_seen) || (!nsecs_seen && !nsec3s_seen)) + { + LogDNSSEC("AddNSECSForCacheRecord:ERROR nsecs_seen %d, nsec3s_seen %d", nsecs_seen, nsec3s_seen); + return mDNSfalse; + } DeleteCachedNSECS(m, negcr); LogDNSSEC("AddNSECSForCacheRecord: Adding NSEC Records for %s", CRDisplayString(m, negcr)); negcr->nsec = crlist; - negcr->rcode = rcode; return mDNStrue; } // Return the number of labels that matches starting from the right (excluding the // root label) -mDNSlocal int CountLabelsMatch(const domainname *const d1, const domainname *const d2) +mDNSexport int CountLabelsMatch(const domainname *const d1, const domainname *const d2) { int count, c1, c2; int match, i, skip1, skip2; @@ -271,98 +314,6 @@ mDNSlocal int CountLabelsMatch(const domainname *const d1, const domainname *con return match; } -// RFC 4034: -// -// Section 6.1: -// -// For the purposes of DNS security, owner names are ordered by treating -// individual labels as unsigned left-justified octet strings. The -// absence of a octet sorts before a zero value octet, and uppercase -// US-ASCII letters are treated as if they were lowercase US-ASCII -// letters. -// -// To compute the canonical ordering of a set of DNS names, start by -// sorting the names according to their most significant (rightmost) -// labels. For names in which the most significant label is identical, -// continue sorting according to their next most significant label, and -// so forth. -// -// Returns 0 if the names are same -// Returns -1 if d1 < d2 -// Returns 1 if d1 > d2 -// -// subdomain is set if there is at least one label match (starting from the end) -// and d1 has more labels than d2 e.g., a.b.com is a subdomain of b.com -// -mDNSlocal int DNSSECCanonicalOrder(const domainname *const d1, const domainname *const d2, int *subdomain) -{ - int count, c1, c2; - int i, skip1, skip2; - - c1 = CountLabels(d1); - skip1 = c1 - 1; - c2 = CountLabels(d2); - skip2 = c2 - 1; - - if (subdomain) *subdomain = 0; - - // Compare as many labels as possible starting from the rightmost - count = c1 < c2 ? c1 : c2; - for (i = count; i > 0; i--) - { - mDNSu8 *a, *b; - int j, len, lena, lenb; - - a = (mDNSu8 *)SkipLeadingLabels(d1, skip1); - b = (mDNSu8 *)SkipLeadingLabels(d2, skip2); - lena = *a; - lenb = *b; - // Compare label by label. Note that "z" > "yak" because z > y, but z < za - // (lena - lenb check below) because 'za' has two characters. Hence compare the - // letters first and then compare the length of the label at the end. - len = lena < lenb ? lena : lenb; - a++; b++; - for (j = 0; j < len; j++) - { - mDNSu8 ac = *a++; - mDNSu8 bc = *b++; - if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; - if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; - if (ac != bc) - { - verbosedebugf("DNSSECCanonicalOrder: returning ac %c, bc %c", ac, bc); - return ((ac < bc) ? -1 : 1); - } - } - if ((lena - lenb) != 0) - { - verbosedebugf("DNSSECCanonicalOrder: returning lena %d lenb %d", lena, lenb); - return ((lena < lenb) ? -1 : 1); - } - - // Continue with the next label - skip1--; - skip2--; - } - // We have compared label by label. Both of them are same if we are here. - // - // Two possibilities. - // - // 1) Both names have same number of labels. In that case, return zero. - // 2) The number of labels is not same. As zero label sorts before, names - // with more number of labels is greater. - - // a.b.com is a subdomain of b.com - if ((c1 > c2) && subdomain) - *subdomain = 1; - - verbosedebugf("DNSSECCanonicalOrder: returning c1 %d c2 %d\n", c1, c2); - if (c1 != c2) - return ((c1 < c2) ? -1 : 1); - else - return 0; -} - // Empty Non-Terminal (ENT): if the qname is bigger than nsec owner's name and a // subdomain of the nsec's nxt field, then the qname is a empty non-terminal. For // example, if you are looking for (in RFC 4035 example zone) "y.w.example A" @@ -373,7 +324,7 @@ mDNSlocal int DNSSECCanonicalOrder(const domainname *const d1, const domainname // This function is normally called before checking for wildcard matches. If you // find this NSEC, there is no need to look for a wildcard record // that could possibly answer the question. -mDNSexport mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname) +mDNSlocal mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *qname) { const domainname *oname = rr->name; const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data; @@ -456,13 +407,13 @@ mDNSlocal int NSECNameExists(mDNS *const m, ResourceRecord *rr, domainname *name // We are here because the owner name is the same as "name". Make sure the // NSEC has the right NS and SOA bits set. - if (ns && !soa && qtype != kDNSType_DS) + if (qtype != kDNSType_DS && ns && !soa) { LogDNSSEC("NSECNameExists: Parent side NSEC %s can't be used for question %##s (%s)", RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); return -1; } - else if (ns && soa && qtype == kDNSType_DS) + else if (qtype == kDNSType_DS && soa) { LogDNSSEC("NSECNameExists: Child side NSEC %s can't be used for question %##s (%s)", RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); @@ -568,6 +519,7 @@ mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv) CacheRecord **rp; const domainname *ce; DNSQuestion q; + CacheRecord **nsec3 = mDNSNULL; LogDNSSEC("WildcardAnswerProof: Question %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); // @@ -591,9 +543,13 @@ mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv) ncr = NSECParentForQuestion(m, &q); if (!ncr) { - LogMsg("NSECWildCardProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + LogMsg("WildcardAnswerProof: Can't find NSEC Parent for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); goto error; } + else + { + LogDNSSEC("WildcardAnswerProof: found %s", CRDisplayString(m, ncr)); + } rp = &(ncr->nsec); while (*rp) { @@ -603,29 +559,45 @@ mDNSexport void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv) if (!NSECNameExists(m, &cr->resrec, &dv->origName, dv->origType)) break; } + else if ((*rp)->resrec.rrtype == kDNSType_NSEC3) + { + nsec3 = rp; + } rp=&(*rp)->next; } if (!(*rp)) { - LogMsg("NSECWildCardProof: ERROR!! No NSECs found for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); - goto error; - } - ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName); - if (!ce) - { - LogMsg("NSECWildCardProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); - goto error; + mDNSBool ret = mDNSfalse; + if (nsec3) + { + ret = NSEC3WildcardAnswerProof(m, ncr, dv); + } + if (!ret) + { + LogDNSSEC("WildcardAnswerProof: NSEC3 wildcard proof failed for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + goto error; + } + rp = nsec3; } - if (!SameDomainName(ce, dv->wildcardName)) + else { - LogMsg("NSECWildCardProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c); - goto error; + ce = NSECClosestEncloser(&((*rp)->resrec), &dv->origName); + if (!ce) + { + LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser NULL for %##s (%s)", q.qname.c, DNSTypeName(q.qtype)); + goto error; + } + if (!SameDomainName(ce, dv->wildcardName)) + { + LogMsg("WildcardAnswerProof: ERROR!! Closest Encloser %##s does not match wildcard name %##s", q.qname.c, dv->wildcardName->c); + goto error; + } } VerifyNSEC(m, &((*rp)->resrec), mDNSNULL, dv, ncr, mDNSNULL); return; error: - dv->DVCallback(m, dv, DNSSEC_Insecure); + dv->DVCallback(m, dv, DNSSEC_Bogus); } // We have a NSEC. Need to see if it proves that NODATA exists for the given name. Note that this @@ -669,7 +641,7 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname } else { - if (ns && soa) + if (soa) { LogDNSSEC("NSECNoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)", RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); @@ -688,10 +660,9 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname { // Name does not exist. Before we check for a wildcard match, make sure that // this is not an ENT. - // if (NSECAnswersENT(rr, name)) { - LogDNSSEC("NSECNoDataError: ERROR!! name %##s exists %s", name->c, RRDisplayString(m, rr)); + LogDNSSEC("NSECNoDataError: name %##s exists %s", name->c, RRDisplayString(m, rr)); return mDNSfalse; } @@ -716,17 +687,16 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname LogMsg("NSECNoDataError: ERROR!! qtype %s exists in wildcard %s", DNSTypeName(qtype), RRDisplayString(m, rr)); return mDNSfalse; } - // It is odd for a wildcard to match when we are looking up DS - // See RFC 4592 - if (qtype == kDNSType_DS) + if (qtype == kDNSType_DS && RRAssertsExistence(rr, kDNSType_SOA)) { - LogMsg("NSECNoDataError: ERROR!! DS qtype exists in wildcard %s", RRDisplayString(m, rr)); + LogDNSSEC("NSECNoDataError: Child side wildcard NSEC %s, can't use for parent qname %##s (%s)", + RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); return mDNSfalse; } - // Don't use the parent side record for this - if (RRAssertsNonexistence(rr, kDNSType_SOA) && + else if (qtype != kDNSType_DS && RRAssertsNonexistence(rr, kDNSType_SOA) && RRAssertsExistence(rr, kDNSType_NS)) { + // Don't use the parent side record for this LogDNSSEC("NSECNoDataError: Parent side wildcard NSEC %s, can't use for child qname %##s (%s)", RRDisplayString(m, rr), name->c, DNSTypeName(qtype)); return mDNSfalse; @@ -740,7 +710,7 @@ mDNSlocal mDNSBool NSECNoDataError(mDNS *const m, ResourceRecord *rr, domainname } } -mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +mDNSexport void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) { RRVerifier *rv; DNSSECVerifier *pdv; @@ -758,32 +728,21 @@ mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu { // Before we free the "dv", we need to update the // parent with our AuthChain information - AuthChainLink(dv->parent, dv->ac); - dv->ac = mDNSNULL; - dv->actail = &dv->ac; + UpdateParent(dv); } pdv = dv->parent; + + // We don't care about the "dv" that was allocated in VerifyNSEC + // as it just verifies one of the nsecs. Get the original verifier and + // verify the other NSEC like we did the first time. + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); + if (status != DNSSEC_Secure) { goto error; } - if (!(pdv->flags & NSEC_PROVES_NONAME_EXISTS)) - { - LogMsg("NoDataNSECCCallback: ERROR!! NSEC_PROVES_NONAME_EXISTS not set"); - goto error; - } - if (!(pdv->flags & WILDCARD_PROVES_NONAME_EXISTS)) - { - LogMsg("NoDataNSECCCallback: ERROR!! WILDCARD_PROVES_NONAME_EXISTS not set"); - goto error; - } - - // We don't care about the "dv" that was allocated in VerifyNSEC. - // Get the original verifier and verify the other NSEC like we did - // the first time. - dv->parent = mDNSNULL; - FreeDNSSECVerifier(m, dv); ncr = NSECParentForQuestion(m, &pdv->q); if (!ncr) @@ -791,21 +750,23 @@ mDNSlocal void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatu LogMsg("NoDataNSECCallback: Can't find NSEC Parent for %##s (%s)", pdv->q.qname.c, DNSTypeName(pdv->q.qtype)); goto error; } - rv = pdv->pendingNSEC; - pdv->pendingNSEC = mDNSNULL; - // Verify the pendingNSEC and we don't need to come back here. Let the regular - // NSECCallback call the original callback. - VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL); + pdv->pendingNSEC = rv->next; + // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one, + // we don't need to come back here; let the regular NSECCallback call the original callback. + rv->next = mDNSNULL; + LogDNSSEC("NoDataNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype)); + if (!pdv->pendingNSEC) + VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL); + else + VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NoDataNSECCallback); return; error: - dv->parent->DVCallback(m, dv->parent, status); - dv->parent = mDNSNULL; - FreeDNSSECVerifier(m, dv); + pdv->DVCallback(m, pdv, status); } -mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) +mDNSexport void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status) { RRVerifier *rv; DNSSECVerifier *pdv; @@ -823,21 +784,20 @@ mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECSt { // Before we free the "dv", we need to update the // parent with our AuthChain information - AuthChainLink(dv->parent, dv->ac); - dv->ac = mDNSNULL; - dv->actail = &dv->ac; + UpdateParent(dv); } pdv = dv->parent; + // We don't care about the "dv" that was allocated in VerifyNSEC + // as it just verifies one of the nsecs. Get the original verifier and + // verify the other NSEC like we did the first time. + dv->parent = mDNSNULL; + FreeDNSSECVerifier(m, dv); + if (status != DNSSEC_Secure) { goto error; } - // We don't care about the "dv" that was allocated in VerifyNSEC. - // Get the original verifier and verify the other NSEC like we did - // the first time. - dv->parent = mDNSNULL; - FreeDNSSECVerifier(m, dv); ncr = NSECParentForQuestion(m, &pdv->q); if (!ncr) @@ -846,16 +806,20 @@ mDNSlocal void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECSt goto error; } rv = pdv->pendingNSEC; - pdv->pendingNSEC = mDNSNULL; - // Verify the pendingNSEC and we don't need to come back here. Let the regular - // NSECCallback call the original callback. - VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL); + pdv->pendingNSEC = rv->next; + // We might have more than one pendingNSEC in the case of NSEC3. If this is the last one, + // we don't need to come back here; let the regular NSECCallback call the original callback. + rv->next = mDNSNULL; + LogDNSSEC("NameErrorNSECCallback: Verifying %##s (%s)", rv->name.c, DNSTypeName(rv->rrtype)); + if (!pdv->pendingNSEC) + VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, mDNSNULL); + else + VerifyNSEC(m, mDNSNULL, rv, pdv, ncr, NameErrorNSECCallback); + return; error: - dv->parent->DVCallback(m, dv->parent, status); - dv->parent = mDNSNULL; - FreeDNSSECVerifier(m, dv); + pdv->DVCallback(m, pdv, status); } // We get a NODATA error with no records in answer section. This proves @@ -910,6 +874,11 @@ mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) } rp=&(*rp)->next; } + if (!nsec_noname && !nsec_wild) + { + LogDNSSEC("NoDataProof: No valid NSECs for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype)); + goto error; + } // If the type exists, then we have to verify just that NSEC if (!(dv->flags & NSEC_PROVES_NOTYPE_EXISTS)) { @@ -926,6 +895,12 @@ mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) LogMsg("NoDataProof: wildcard %##s does not match closest encloser %##s", wildcard->c, ce->c); goto error; } + // If a single NSEC can prove both, then we just have validate that one NSEC. + if (nsec_wild == nsec_noname) + { + nsec_noname = mDNSNULL; + dv->flags &= ~NSEC_PROVES_NONAME_EXISTS; + } } if ((dv->flags & (WILDCARD_PROVES_NONAME_EXISTS|NSEC_PROVES_NONAME_EXISTS)) == @@ -937,21 +912,24 @@ mDNSlocal void NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) // First verify wildcard NSEC and then when we are done, we // will verify the noname nsec dv->pendingNSEC = r; + LogDNSSEC("NoDataProof: Verifying wild and noname %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NoDataNSECCallback); } else if ((dv->flags & WILDCARD_PROVES_NONAME_EXISTS) || (dv->flags & NSEC_PROVES_NOTYPE_EXISTS)) { + LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL); } else if (dv->flags & NSEC_PROVES_NONAME_EXISTS) { + LogDNSSEC("NoDataProof: Verifying noname %s", RRDisplayString(m, nsec_noname)); VerifyNSEC(m, nsec_noname, mDNSNULL, dv, ncr, mDNSNULL); } return; error: LogDNSSEC("NoDataProof: Error return"); - dv->DVCallback(m, dv, DNSSEC_Insecure); + dv->DVCallback(m, dv, DNSSEC_Bogus); } mDNSlocal mDNSBool NSECNoWildcard(mDNS *const m, ResourceRecord *rr, domainname *qname, mDNSu16 qtype) @@ -1055,77 +1033,234 @@ mDNSlocal void NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *nc RRVerifier *r = AllocateRRVerifier(nsec_noname, &status); if (!r) goto error; dv->pendingNSEC = r; + LogDNSSEC("NoDataProof: Verifying wild %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, NameErrorNSECCallback); } else { + LogDNSSEC("NoDataProof: Verifying only one %s", RRDisplayString(m, nsec_wild)); VerifyNSEC(m, nsec_wild, mDNSNULL, dv, ncr, mDNSNULL); } return; error: - dv->DVCallback(m, dv, DNSSEC_Insecure); + dv->DVCallback(m, dv, DNSSEC_Bogus); +} + +mDNSexport CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype) +{ + CacheGroup *cg; + CacheRecord *cr; + mDNSu32 slot, namehash; + + slot = HashSlot(name); + namehash = DomainNameHashValue(name); + + cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name); + if (!cg) + { + LogDNSSEC("NSECRecordForName: cg NULL for %##s", name); + return mDNSNULL; + } + for (cr = cg->members; cr; cr = cr->next) + { + if (cr->resrec.RecordType == kDNSRecordTypePacketNegative && cr->resrec.rrtype == qtype) + { + CacheRecord *ncr; + for (ncr = cr->nsec; ncr; ncr = ncr->next) + { + if (ncr->resrec.rrtype == kDNSType_NSEC && + SameDomainName(ncr->resrec.name, name)) + { + // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit + // should be absent + if (RRAssertsExistence(&ncr->resrec, kDNSType_SOA) || + RRAssertsExistence(&ncr->resrec, kDNSType_DS)) + { + LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but DS or SOA bit set", CRDisplayString(m, ncr), name, + DNSTypeName(qtype)); + return mDNSNULL; + } + // Section 2.3 of RFC 4035 states that: + // + // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST + // have an NSEC resource record. + // + // So, if we have an NSEC record matching the question name with the NS bit set, + // then this is a delegation. + // + if (RRAssertsExistence(&ncr->resrec, kDNSType_NS)) + { + LogDNSSEC("NSECRecordForName: found record %s for %##s (%s)", CRDisplayString(m, ncr), name, DNSTypeName(qtype)); + return ncr; + } + else + { + LogDNSSEC("NSECRecordForName: found record %s for %##s (%s), but NS bit is not set", CRDisplayString(m, ncr), name, + DNSTypeName(qtype)); + return mDNSNULL; + } + } + } + } + } + return mDNSNULL; +} + +mDNSlocal void StartInsecureProof(mDNS * const m, DNSSECVerifier *dv) +{ + domainname trigger; + DNSSECVerifier *prevdv = mDNSNULL; + + // Remember the name that triggered the insecure proof + AssignDomainName(&trigger, &dv->q.qname); + while (dv->parent) + { + prevdv = dv; + dv = dv->parent; + } + if (prevdv) + { + prevdv->parent = mDNSNULL; + FreeDNSSECVerifier(m, prevdv); + } + // For Optional DNSSEC, we are opportunistically verifying dnssec. We don't care + // if something results in bogus as we still want to deliver results to the + // application e.g., CNAME processing results in bogus because the path is broken, + // but we still want to follow CNAMEs so that we can deliver the final results to + // the application. + if (dv->ValidationRequired == DNSSEC_VALIDATION_SECURE_OPTIONAL) + { + LogDNSSEC("StartInsecureProof: Aborting insecure proof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype)); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return; + } + + LogDNSSEC("StartInsecureProof for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype)); + // Don't start the insecure proof again after we finish the one that we start here by + // setting InsecureProofDone. + dv->InsecureProofDone = 1; + ProveInsecure(m, dv, mDNSNULL, &trigger); + return; } mDNSexport void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *cr) { LogDNSSEC("ValidateWithNSECS: called for %s", CRDisplayString(m, cr)); - // "parent" is set when we are validating a NSEC. In the process of validating that - // nsec, we encountered another NSEC. For example, we are looking up the A record for - // www.example.com, we got an NSEC at some stage. We come here to validate the NSEC - // the first time. While validating the NSEC we remember the original validation result - // in the parent. But while validating the NSEC, we got another NSEC back e.g., not - // a secure delegation i.e., we got an NSEC proving that DS does not exist. We prove - // that again. But if we receive more NSECs after this, we stop. + + // If we are encountering a break in the chain of trust i.e., NSEC/NSEC3s for + // DS query, then do the insecure proof. This is important because if we + // validate these NSECs normally and prove that they are "secure", we will + // end up delivering the secure result to the original question where as + // these NSEC/NSEC3s actually prove that DS does not exist and hence insecure. // - if (dv->parent) + // This break in the chain can happen after we have partially validated the + // path (dv->ac is non-NULL) or the first time (dv->ac is NULL) after we + // fetched the DNSKEY (dv->key is non-NULL). We don't want to do this + // if we have just started the non-existence proof (dv->key is NULL) as + // it does not indicate a break in the chain of trust. + // + // If we are already doing a insecurity proof, don't start another one. In + // the case of NSECs, it is possible that insecurity proof starts and it + // gets NSECs and as part of validating that we receive more NSECS in which + // case we don't want to start another insecurity proof. + if (dv->ValidationRequired != DNSSEC_VALIDATION_INSECURE && + (!dv->parent || dv->parent->ValidationRequired != DNSSEC_VALIDATION_INSECURE)) { - if (dv->parent->parent) + if ((dv->ac && dv->q.qtype == kDNSType_DS) || + (!dv->ac && dv->key && dv->q.qtype == kDNSType_DS)) { - LogMsg("ValidateWithNSECS: ERROR!! dv parent is set already"); - dv->DVCallback(m, dv, DNSSEC_Indeterminate); + LogDNSSEC("ValidateWithNSECS: Starting insecure proof: name %##s ac %p, key %p, parent %p", dv->q.qname.c, + dv->ac, dv->key, dv->parent); + StartInsecureProof(m, dv); return; } - else - { - DNSSECVerifier *pdv = dv; - dv = AllocateDNSSECVerifier(m, &pdv->q.qname, pdv->q.qtype, pdv->q.InterfaceID, VerifyNSECCallback, mDNSNULL); - if (!dv) - { - LogMsg("VerifyNSEC: mDNSPlatformMemAlloc failed"); - pdv->DVCallback(m, pdv, DNSSEC_Indeterminate); - return; - } - LogDNSSEC("ValidateWithNSECS: Parent set, Verifying dv %p %##s (%s)", dv, pdv->q.qname.c, DNSTypeName(pdv->q.qtype)); - dv->parent = pdv; - } + } + // "parent" is set when we are validating a NSEC and we should not be here in + // the normal case when parent is set. For example, we are looking up the A + // record for www.example.com and following can happen. + // + // a) Record does not exist and we get a NSEC + // b) While validating (a), we get an NSEC for the first DS record that we look up + // c) Record exists but we get NSECs for the first DS record + // d) We are able to partially validate (a) or (b), but we get NSECs somewhere in + // the chain + // + // For (a), parent is not set as we are not validating the NSEC yet. Hence we would + // start the validation now. + // + // For (b), the parent is set, but should be caught by the above "if" block because we + // should have gotten the DNSKEY at least. In the case of nested insecurity proof, + // we would end up here and fail with bogus. + // + // For (c), the parent is not set and should be caught by the above "if" block because we + // should have gotten the DNSKEY at least. + // + // For (d), the above "if" block would catch it as "dv->ac" is non-NULL. + // + // Hence, we should not come here in the normal case. Possible pathological cases are: + // Insecure proof getting NSECs while validating NSECs, getting NSECs for DNSKEY for (c) + // above etc. + if (dv->parent) + { + LogDNSSEC("ValidateWithNSECS: dv parent set for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype)); + dv->DVCallback(m, dv, DNSSEC_Bogus); + return; } if (cr->resrec.RecordType == kDNSRecordTypePacketNegative) { + mDNSu8 rcode; CacheRecord *neg = cr->nsec; + mDNSBool nsecs_seen = mDNSfalse; + while (neg) { + // The list can only have NSEC or NSEC3s. This was checked when we added the + // NSECs to the cache record. + if (neg->resrec.rrtype == kDNSType_NSEC) + nsecs_seen = mDNStrue; LogDNSSEC("ValidateWithNSECS: NSECCached Record %s", CRDisplayString(m, neg)); neg = neg->next; } - if (cr->rcode == kDNSFlag1_RC_NoErr) + rcode = (mDNSu8)(cr->responseFlags.b[1] & kDNSFlag1_RC_Mask); + if (rcode == kDNSFlag1_RC_NoErr) { - NoDataProof(m, dv, cr); + if (nsecs_seen) + NoDataProof(m, dv, cr); + else + NSEC3NoDataProof(m, dv, cr); } - else if (cr->rcode == kDNSFlag1_RC_NXDomain) + else if (rcode == kDNSFlag1_RC_NXDomain) { - NameErrorProof(m, dv, cr); + if (nsecs_seen) + NameErrorProof(m, dv, cr); + else + NSEC3NameErrorProof(m, dv, cr); } else { - LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", cr->rcode); - dv->DVCallback(m, dv, DNSSEC_Insecure); + LogDNSSEC("ValidateWithNSECS: Rcode %d invalid", rcode); + dv->DVCallback(m, dv, DNSSEC_Bogus); } } else { LogMsg("ValidateWithNSECS: Not a valid cache record %s for NSEC proofs", CRDisplayString(m, cr)); - dv->DVCallback(m, dv, DNSSEC_Insecure); + dv->DVCallback(m, dv, DNSSEC_Bogus); return; } } + +#else // !DNSSEC_DISABLED + +mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode) +{ + (void)m; + (void)crlist; + (void)negcr; + (void)rcode; + + return mDNSfalse; +} + +#endif // !DNSSEC_DISABLED diff --git a/mDNSCore/nsec.h b/mDNSCore/nsec.h index b85e103..3dbb841 100644 --- a/mDNSCore/nsec.h +++ b/mDNSCore/nsec.h @@ -20,9 +20,14 @@ #include "dnssec.h" extern mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, CacheRecord *negcr, mDNSu8 rcode); -extern mDNSBool NSECAnswersENT(const ResourceRecord *const rr, domainname *name); extern void WildcardAnswerProof(mDNS *const m, DNSSECVerifier *dv); extern void ValidateWithNSECS(mDNS *const m, DNSSECVerifier *dv, CacheRecord *rr); -mDNSexport mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q); +extern mDNSBool NSECAnswersDS(mDNS *const m, ResourceRecord *rr, DNSQuestion *q); +extern int CountLabelsMatch(const domainname *const d1, const domainname *const d2); +extern void NameErrorNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); +extern void VerifyNSEC(mDNS *const m, ResourceRecord *rr, RRVerifier *rv, DNSSECVerifier *pdv, CacheRecord *ncr, + DNSSECVerifierCallback callback); +extern CacheRecord *NSECRecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype); +extern void NoDataNSECCallback(mDNS *const m, DNSSECVerifier *dv, DNSSECStatus status); #endif // __NSEC_H diff --git a/mDNSCore/nsec3.c b/mDNSCore/nsec3.c new file mode 100644 index 0000000..a039418 --- /dev/null +++ b/mDNSCore/nsec3.c @@ -0,0 +1,769 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// *************************************************************************** +// nsec3.c: This file contains support functions to validate NSEC3 records for +// NODATA and NXDOMAIN error. +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" +#include "CryptoAlg.h" +#include "nsec3.h" +#include "nsec.h" + +// Define DNSSEC_DISABLED to remove all the DNSSEC functionality +// and use the stub functions implemented later in this file. + +#ifndef DNSSEC_DISABLED + +typedef enum +{ + NSEC3ClosestEncloser, + NSEC3Covers, + NSEC3CEProof +} NSEC3FindValues; + +//#define NSEC3_DEBUG 1 + +#if NSEC3_DEBUG +mDNSlocal void PrintHash(mDNSu8 *digest, int digestlen, char *buffer, int buflen) +{ + int length = 0; + for (int j = 0; j < digestlen; j++) + { + length += mDNS_snprintf(buffer+length, buflen-length-1, "%x", digest[j]); + } +} +#endif + +mDNSlocal mDNSBool NSEC3OptOut(CacheRecord *cr) +{ + const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data; + rdataNSEC3 *nsec3 = (rdataNSEC3 *)rdb->data; + return (nsec3->flags & NSEC3_FLAGS_OPTOUT); +} + +mDNSlocal int NSEC3SameName(const mDNSu8 *name, int namelen, const mDNSu8 *nsecName, int nsecLen) +{ + int i; + + // Note: With NSEC3, the lengths should always be same. + if (namelen != nsecLen) + { + LogMsg("NSEC3SameName: ERROR!! namelen %d, nsecLen %d", namelen, nsecLen); + return ((namelen < nsecLen) ? -1 : 1); + } + + for (i = 0; i < namelen; i++) + { + mDNSu8 ac = *name++; + mDNSu8 bc = *nsecName++; + if (mDNSIsUpperCase(ac)) ac += 'a' - 'A'; + if (mDNSIsUpperCase(bc)) bc += 'a' - 'A'; + if (ac != bc) + { + verbosedebugf("NSEC3SameName: returning ac %c, bc %c", ac, bc); + return ((ac < bc) ? -1 : 1); + } + } + return 0; +} + +// Does the NSEC3 in "ncr" covers the "name" ? +// hashName is hash of the "name" and b32Name is the base32 encoded equivalent. +mDNSlocal mDNSBool NSEC3CoversName(mDNS *const m, CacheRecord *ncr, const mDNSu8 *hashName, int hashLen, const mDNSu8 *b32Name, + int b32len) +{ + mDNSu8 *nxtName; + int nxtLength; + int ret, ret1, ret2; + const mDNSu8 b32nxtname[NSEC3_MAX_B32_LEN+1]; + int b32nxtlen; + + NSEC3Parse(&ncr->resrec, mDNSNULL, &nxtLength, &nxtName, mDNSNULL, mDNSNULL); + + if (nxtLength != hashLen || ncr->resrec.name->c[0] != b32len) + return mDNSfalse; + + // Compare the owner names and the "nxt" names. + // + // Owner name is base32 encoded and hence use the base32 encoded name b32name. + // nxt name is binary and hence use the binary value in hashName. + ret1 = NSEC3SameName(&ncr->resrec.name->c[1], ncr->resrec.name->c[0], b32Name, b32len); + ret2 = DNSMemCmp(nxtName, hashName, hashLen); + +#if NSEC3_DEBUG + { + char nxtbuf1[50]; + char nxtbuf2[50]; + + PrintHash(nxtName, nxtLength, nxtbuf1, sizeof(nxtbuf1)); + PrintHash((mDNSu8 *)hashName, hashLen, nxtbuf2, sizeof(nxtbuf2)); + LogMsg("NSEC3CoversName: Owner name %s, name %s", &ncr->resrec.name->c[1], b32Name); + LogMsg("NSEC3CoversName: Nxt hash name %s, name %s", nxtbuf1, nxtbuf2); + } +#endif + + // "name" is greater than the owner name and smaller than nxtName. This also implies + // that nxtName > owner name implying that it is normal NSEC3. + if (ret1 < 0 && ret2 > 0) + { + LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Normal)", CRDisplayString(m, ncr), b32Name); + return mDNStrue; + } + // Need to compare the owner name and "nxt" to see if this is the last + // NSEC3 in the zone. Only the owner name is in base32 and hence we need to + // convert the nxtName to base32. + b32nxtlen = baseEncode((char *)b32nxtname, sizeof(b32nxtname), nxtName, nxtLength, ENC_BASE32); + if (!b32nxtlen) + { + LogDNSSEC("NSEC3CoversName: baseEncode of nxtName of %s failed", CRDisplayString(m, ncr)); + return mDNSfalse; + } + if (b32len != b32nxtlen) + { + LogDNSSEC("NSEC3CoversName: baseEncode of nxtName for %s resulted in wrong length b32nxtlen %d, b32len %d", + CRDisplayString(m, ncr), b32len, b32nxtlen); + return mDNSfalse; + } + LogDNSSEC("NSEC3CoversName: Owner name %s, b32nxtname %s, ret1 %d, ret2 %d", &ncr->resrec.name->c[1], b32nxtname, ret1, ret2); + + // If it is the last NSEC3 in the zone nxt < "name" and NSEC3SameName returns -1. + // + // - ret1 < 0 means "name > owner" + // - ret2 > 0 means "name < nxt" + // + // Note: We also handle the case of only NSEC3 in the zone where NSEC3SameName returns zero. + ret = NSEC3SameName(b32nxtname, b32nxtlen, &ncr->resrec.name->c[1], ncr->resrec.name->c[0]); + if (ret <= 0 && + (ret1 < 0 || ret2 > 0)) + { + LogDNSSEC("NSEC3CoversName: NSEC3 %s covers %s (Last), ret1 %d, ret2 %d", CRDisplayString(m, ncr), b32Name, ret1, ret2); + return mDNStrue; + } + + return mDNSfalse; +} + +// This function can be called with NSEC3ClosestEncloser, NSEC3Covers and NSEC3CEProof +// +// Passing in NSEC3ClosestEncloser means "find an exact match for the origName". +// Passing in NSEC3Covers means "find an NSEC3 that covers the origName". +// +// i.e., in both cases the nsec3 records are iterated to find the best match and returned. +// With NSEC3ClosestEncloser, as we are just looking for a name match, extra checks for +// the types being present or absent will not be checked. +// +// If NSEC3CEProof is passed, the name is tried as such first by iterating through all NSEC3s +// finding a ClosestEncloser or CloserEncloser and then one label skipped from the left and +// retried again till both the closest and closer encloser is found. +// +// ncr is the negative cache record that has the NSEC3 chain +// origName is the name for which we are trying to find the ClosestEncloser etc. +// closestEncloser and closerEncloser are the return values of the function +// ce is the closest encloser that will be returned if we find one +mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *ncr, domainname *origName, CacheRecord **closestEncloser, + CacheRecord **closerEncloser, const domainname **ce, mDNSu16 qtype) +{ + int i; + int labelCount = CountLabels(origName); + CacheRecord *cr; + rdataNSEC3 *nsec3; + + (void) qtype; // unused + // Pick the first NSEC for the iterations, salt etc. + for (cr = ncr->nsec; cr; cr = cr->next) + { + if (cr->resrec.rrtype == kDNSType_NSEC3) + { + const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data; + nsec3 = (rdataNSEC3 *)rdb->data; + break; + } + } + if (!cr) + { + LogMsg("NSEC3Find: cr NULL"); + return mDNSfalse; + } + + // Note: The steps defined in this function are for "NSEC3CEProof". As part of NSEC3CEProof, + // we need to find both the closestEncloser and closerEncloser which can also be found + // by passing NSEC3ClosestEncloser and NSEC3Covers respectively. + // + // Section 8.3 of RFC 5155. + // 1. Set SNAME=QNAME. Clear the flag. + // + // closerEncloser is the "flag". "name" below is SNAME. + + if (closestEncloser) + { + *ce = mDNSNULL; + *closestEncloser = mDNSNULL; + } + if (closerEncloser) + *closerEncloser = mDNSNULL; + + // If we are looking for a closestEncloser or a covering NSEC3, we don't have + // to truncate the name. For the give name, try to find the closest or closer + // encloser. + if (val != NSEC3CEProof) + { + labelCount = 0; + } + + for (i = 0; i < labelCount + 1; i++) + { + int hlen; + const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + const domainname *name; + const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1]; + int b32len; + + name = SkipLeadingLabels(origName, i); + if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen)) + { + LogMsg("NSEC3Find: NSEC3HashName failed for ##s", name->c); + continue; + } + + b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32); + if (!b32len) + { + LogMsg("NSEC3Find: baseEncode of name %##s failed", name->c); + continue; + } + + + for (cr = ncr->nsec; cr; cr = cr->next) + { + const domainname *nsecZone; + int result, subdomain; + + if (cr->resrec.rrtype != kDNSType_NSEC3) + continue; + + nsecZone = SkipLeadingLabels(cr->resrec.name, 1); + if (!nsecZone) + { + LogMsg("NSEC3Find: SkipLeadingLabel failed for %s, current name %##s", + CRDisplayString(m, cr), name->c); + continue; + } + + // NSEC3 owner names are formed by hashing the owner name and then appending + // the zone name to it. If we skip the first label, the rest should be + // the zone name. See whether it is the subdomain of the name we are looking + // for. + result = DNSSECCanonicalOrder(origName, nsecZone, &subdomain); + + // The check can't be a strict subdomain check. When NSEC3ClosestEncloser is + // passed in, there can be an exact match. If it is a subdomain or an exact + // match, we should continue with the proof. + if (!(subdomain || !result)) + { + LogMsg("NSEC3Find: NSEC3 %s not a subdomain of %##s, result %d", CRDisplayString(m, cr), + origName->c, result); + continue; + } + + // 2.1) If there is no NSEC3 RR in the response that matches SNAME + // (i.e., an NSEC3 RR whose owner name is the same as the hash of + // SNAME, prepended as a single label to the zone name), clear + // the flag. + // + // Note: We don't try to determine the actual zone name. We know that + // the labels following the hash (nsecZone) is the ancestor and we don't + // know where the zone cut is. Hence, we verify just the hash to be + // the same. + + if (val == NSEC3ClosestEncloser || val == NSEC3CEProof) + { + if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len)) + { + int bmaplen; + mDNSu8 *bmap; + + // For NSEC3ClosestEncloser, we are finding an exact match and + // "type" specific checks should be done by the caller. + if (val != NSEC3ClosestEncloser) + { + // DNAME bit must not be set and NS bit may be set only if SOA bit is set + NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap); + if (BitmapTypeCheck(bmap, bmaplen, kDNSType_DNAME)) + { + LogDNSSEC("NSEC3Find: DNAME bit set in %s, ignoring", CRDisplayString(m, cr)); + return mDNSfalse; + } + // This is the closest encloser and should come from the right zone. + if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS) && + !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA)) + { + LogDNSSEC("NSEC3Find: NS bit set without SOA bit in %s, ignoring", CRDisplayString(m, cr)); + return mDNSfalse; + } + } + + LogDNSSEC("NSEC3Find: ClosestEncloser %s found for name %##s", CRDisplayString(m, cr), name->c); + if (closestEncloser) + { + *ce = name; + *closestEncloser = cr; + } + if (val == NSEC3ClosestEncloser) + return mDNStrue; + else + break; + } + } + + if ((val == NSEC3Covers || val == NSEC3CEProof) && !(*closerEncloser)) + { + if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len)) + { + // 2.2) If there is an NSEC3 RR in the response that covers SNAME, set the flag. + if (closerEncloser) + *closerEncloser = cr; + if (val == NSEC3Covers) + return mDNStrue; + else + break; + } + } + } + // 2.3) If there is a matching NSEC3 RR in the response and the flag + // was set, then the proof is complete, and SNAME is the closest + // encloser. + if (val == NSEC3CEProof) + { + if (*closestEncloser && *closerEncloser) + { + LogDNSSEC("NSEC3Find: Found closest and closer encloser"); + return mDNStrue; + } + + // 2.4) If there is a matching NSEC3 RR in the response, but the flag + // is not set, then the response is bogus. + // + // Note: We don't have to wait till we finish trying all the names. If the matchName + // happens, we found the closest encloser which means we should have found the closer + // encloser before. + + if (*closestEncloser && !(*closerEncloser)) + { + LogDNSSEC("NSEC3Find: Found closest, but not closer encloser"); + return mDNSfalse; + } + } + // 3. Truncate SNAME by one label from the left, go to step 2. + } + LogDNSSEC("NSEC3Find: Cannot find name %##s (%s)", origName->c, DNSTypeName(qtype)); + return mDNSfalse; +} + +mDNSlocal mDNSBool NSEC3ClosestEncloserProof(mDNS *const m, CacheRecord *ncr, domainname *name, CacheRecord **closestEncloser, CacheRecord **closerEncloser, + const domainname **ce, mDNSu16 qtype) +{ + if (!NSEC3Find(m, NSEC3CEProof, ncr, name, closestEncloser, closerEncloser, ce, qtype)) + { + LogDNSSEC("NSEC3ClosestEncloserProof: ERROR!! Cannot do closest encloser proof"); + return mDNSfalse; + } + + // Note: It is possible that closestEncloser and closerEncloser are the same. + if (!closestEncloser || !closerEncloser || !ce) + { + LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %p or CloserEncloser %p ce %p, something is NULL", *closestEncloser, + *closerEncloser, *ce); + return mDNSfalse; + } + + // If the name exists, we should not have gotten the name error + if (SameDomainName((*ce), name)) + { + LogMsg("NSEC3ClosestEncloserProof: ClosestEncloser %s same as origName %##s", CRDisplayString(m, *closestEncloser), + (*ce)->c); + return mDNSfalse; + } + return mDNStrue; +} + +mDNSlocal mDNSBool VerifyNSEC3(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr, CacheRecord *closestEncloser, + CacheRecord *closerEncloser, CacheRecord *wildcard, DNSSECVerifierCallback callback) +{ + mStatus status; + RRVerifier *r; + + // We have three NSEC3s. If any of two are same, we should just prove one of them. + // This is just not an optimization; DNSSECNegativeValidationCB does not handle + // identical NSEC3s very well. + + if (closestEncloser == closerEncloser) + closestEncloser = mDNSNULL; + if (closerEncloser == wildcard) + closerEncloser = mDNSNULL; + if (closestEncloser == wildcard) + closestEncloser = mDNSNULL; + + dv->pendingNSEC = mDNSNULL; + if (closestEncloser) + { + r = AllocateRRVerifier(&closestEncloser->resrec, &status); + if (!r) + return mDNSfalse; + r->next = dv->pendingNSEC; + dv->pendingNSEC = r; + } + if (closerEncloser) + { + r = AllocateRRVerifier(&closerEncloser->resrec, &status); + if (!r) + return mDNSfalse; + r->next = dv->pendingNSEC; + dv->pendingNSEC = r; + } + if (wildcard) + { + r = AllocateRRVerifier(&wildcard->resrec, &status); + if (!r) + return mDNSfalse; + r->next = dv->pendingNSEC; + dv->pendingNSEC = r; + } + if (!dv->pendingNSEC) + { + LogMsg("VerifyNSEC3: ERROR!! pending NSEC null"); + return mDNSfalse; + } + r = dv->pendingNSEC; + dv->pendingNSEC = r->next; + r->next = mDNSNULL; + + LogDNSSEC("VerifyNSEC3: Verifying %##s (%s)", r->name.c, DNSTypeName(r->rrtype)); + if (!dv->pendingNSEC) + VerifyNSEC(m, mDNSNULL, r, dv, ncr, mDNSNULL); + else + VerifyNSEC(m, mDNSNULL, r, dv, ncr, callback); + return mDNStrue; +} + +mDNSexport void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) +{ + CacheRecord *closerEncloser; + CacheRecord *closestEncloser; + CacheRecord *wildcard; + const domainname *ce = mDNSNULL; + domainname wild; + + if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype)) + { + goto error; + } + LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser %s, ce %##s", CRDisplayString(m, closestEncloser), ce->c); + LogDNSSEC("NSEC3NameErrorProof: CloserEncloser %s", CRDisplayString(m, closerEncloser)); + + // *.closestEncloser should be covered by some nsec3 which would then prove + // that the wildcard does not exist + wild.c[0] = 1; + wild.c[1] = '*'; + wild.c[2] = 0; + if (!AppendDomainName(&wild, ce)) + { + LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c); + goto error; + } + if (!NSEC3Find(m, NSEC3Covers, ncr, &wild, mDNSNULL, &wildcard, mDNSNULL, dv->q.qtype)) + { + LogMsg("NSEC3NameErrorProof: Cannot find encloser for wildcard"); + goto error; + } + else + { + LogDNSSEC("NSEC3NameErrorProof: Wildcard %##s covered by %s", wild.c, CRDisplayString(m, wildcard)); + if (wildcard == closestEncloser) + { + LogDNSSEC("NSEC3NameErrorProof: ClosestEncloser matching Wildcard %s", CRDisplayString(m, wildcard)); + } + } + if (NSEC3OptOut(closerEncloser)) + { + dv->flags |= NSEC3_OPT_OUT; + } + if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NameErrorNSECCallback)) + goto error; + else + return; + +error: + dv->DVCallback(m, dv, DNSSEC_Bogus); +} + +// Section 8.5, 8.6 of RFC 5155 first paragraph +mDNSlocal mDNSBool NSEC3NoDataError(mDNS *const m, CacheRecord *ncr, domainname *name, mDNSu16 qtype, CacheRecord **closestEncloser) +{ + const domainname *ce = mDNSNULL; + + *closestEncloser = mDNSNULL; + // Note: This also covers ENT in which case the bitmap is empty + if (NSEC3Find(m, NSEC3ClosestEncloser, ncr, name, closestEncloser, mDNSNULL, &ce, qtype)) + { + int bmaplen; + mDNSu8 *bmap; + mDNSBool ns, soa; + + NSEC3Parse(&(*closestEncloser)->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap); + if (BitmapTypeCheck(bmap, bmaplen, qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME)) + { + LogMsg("NSEC3NoDataError: qtype %s exists in %s", DNSTypeName(qtype), CRDisplayString(m, *closestEncloser)); + return mDNSfalse; + } + ns = BitmapTypeCheck(bmap, bmaplen, kDNSType_NS); + soa = BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA); + if (qtype != kDNSType_DS) + { + // For non-DS type questions, we don't want to use the parent side records to + // answer it + if (ns && !soa) + { + LogDNSSEC("NSEC3NoDataError: Parent side NSEC %s, can't use for child qname %##s (%s)", + CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype)); + return mDNSfalse; + } + } + else + { + if (soa) + { + LogDNSSEC("NSEC3NoDataError: Child side NSEC %s, can't use for parent qname %##s (%s)", + CRDisplayString(m, *closestEncloser), name->c, DNSTypeName(qtype)); + return mDNSfalse; + } + } + LogDNSSEC("NSEC3NoDataError: Name -%##s- exists, but qtype %s does not exist in %s", name->c, DNSTypeName(qtype), CRDisplayString(m, *closestEncloser)); + return mDNStrue; + } + return mDNSfalse; +} + +mDNSexport void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr) +{ + CacheRecord *closerEncloser = mDNSNULL; + CacheRecord *closestEncloser = mDNSNULL; + CacheRecord *wildcard = mDNSNULL; + const domainname *ce = mDNSNULL; + domainname wild; + + // Section 8.5, 8.6 of RFC 5155 + if (NSEC3NoDataError(m, ncr, &dv->q.qname, dv->q.qtype, &closestEncloser)) + { + goto verify; + } + // Section 8.6, 8.7: if we can't find the NSEC3 RR, verify the closest encloser proof + // for QNAME and the "next closer" should have the opt out + if (!NSEC3ClosestEncloserProof(m, ncr, &dv->q.qname, &closestEncloser, &closerEncloser, &ce, dv->q.qtype)) + { + goto error; + } + + // Section 8.7: find a matching NSEC3 for *.closestEncloser + wild.c[0] = 1; + wild.c[1] = '*'; + wild.c[2] = 0; + if (!AppendDomainName(&wild, ce)) + { + LogMsg("NSEC3NameErrorProof: Can't append domainname to closest encloser name %##s", ce->c); + goto error; + } + if (!NSEC3Find(m, NSEC3ClosestEncloser, ncr, &wild, &wildcard, mDNSNULL, &ce, dv->q.qtype)) + { + // Not a wild card case. Section 8.6 second para applies. + LogDNSSEC("NSEC3NoDataProof: Cannot find encloser for wildcard, perhaps not a wildcard case"); + if (!NSEC3OptOut(closerEncloser)) + { + LogDNSSEC("NSEC3DataProof: opt out not set for %##s (%s), bogus", dv->q.qname.c, DNSTypeName(dv->q.qtype)); + goto error; + } + LogDNSSEC("NSEC3DataProof: opt out set, proof complete for %##s (%s)", dv->q.qname.c, DNSTypeName(dv->q.qtype)); + dv->flags |= NSEC3_OPT_OUT; + } + else + { + int bmaplen; + mDNSu8 *bmap; + NSEC3Parse(&wildcard->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap); + if (BitmapTypeCheck(bmap, bmaplen, dv->q.qtype) || BitmapTypeCheck(bmap, bmaplen, kDNSType_CNAME)) + { + LogDNSSEC("NSEC3NoDataProof: qtype %s exists in %s", DNSTypeName(dv->q.qtype), CRDisplayString(m, wildcard)); + goto error; + } + if (dv->q.qtype == kDNSType_DS && BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA)) + { + LogDNSSEC("NSEC3NoDataProof: Child side wildcard NSEC3 %s, can't use for parent qname %##s (%s)", + CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype)); + goto error; + } + else if (dv->q.qtype != kDNSType_DS && !BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) && + BitmapTypeCheck(bmap, bmaplen, kDNSType_NS)) + { + // Don't use the parent side record for this + LogDNSSEC("NSEC3NoDataProof: Parent side wildcard NSEC3 %s, can't use for child qname %##s (%s)", + CRDisplayString(m, wildcard), dv->q.qname.c, DNSTypeName(dv->q.qtype)); + goto error; + } + LogDNSSEC("NSEC3NoDataProof: Wildcard %##s matched by %s", wild.c, CRDisplayString(m, wildcard)); + } +verify: + + if (!VerifyNSEC3(m, dv, ncr, closestEncloser, closerEncloser, wildcard, NoDataNSECCallback)) + goto error; + else + return; +error: + dv->DVCallback(m, dv, DNSSEC_Bogus); +} + +mDNSexport mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv) +{ + int skip; + const domainname *nc; + CacheRecord *closerEncloser; + + (void) m; + + // Find the next closer name and prove that it is covered by the NSEC3 + skip = CountLabels(&dv->origName) - CountLabels(dv->wildcardName) - 1; + if (skip) + nc = SkipLeadingLabels(&dv->origName, skip); + else + nc = &dv->origName; + + LogDNSSEC("NSEC3WildcardAnswerProof: wildcard name %##s", nc->c); + + if (!NSEC3Find(m, NSEC3Covers, ncr, (domainname *)nc, mDNSNULL, &closerEncloser, mDNSNULL, dv->q.qtype)) + { + LogMsg("NSEC3WildcardAnswerProof: Cannot find closer encloser"); + return mDNSfalse; + } + if (!closerEncloser) + { + LogMsg("NSEC3WildcardAnswerProof: closerEncloser NULL"); + return mDNSfalse; + } + if (NSEC3OptOut(closerEncloser)) + { + dv->flags |= NSEC3_OPT_OUT; + } + // NSEC3 Verification is done by the caller + return mDNStrue; +} + +mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype) +{ + CacheGroup *cg; + CacheRecord *cr; + CacheRecord *ncr; + mDNSu32 slot, namehash; + + slot = HashSlot(name); + namehash = DomainNameHashValue(name); + + cg = CacheGroupForName(m, (const mDNSu32)slot, namehash, name); + if (!cg) + { + LogDNSSEC("NSEC3RecordForName: cg NULL for %##s", name); + return mDNSNULL; + } + for (ncr = cg->members; ncr; ncr = ncr->next) + { + if (ncr->resrec.RecordType != kDNSRecordTypePacketNegative || + ncr->resrec.rrtype != qtype) + { + continue; + } + for (cr = ncr->nsec; cr; cr = cr->next) + { + int hlen, b32len; + const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + const mDNSu8 b32Name[NSEC3_MAX_B32_LEN+1]; + const RDataBody2 *const rdb = (RDataBody2 *)cr->resrec.rdata->u.data; + rdataNSEC3 *nsec3; + + if (cr->resrec.rrtype != kDNSType_NSEC3) + continue; + + nsec3 = (rdataNSEC3 *)rdb->data; + + if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen)) + { + LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name->c); + return mDNSNULL; + } + + b32len = baseEncode((char *)b32Name, sizeof(b32Name), (mDNSu8 *)hashName, hlen, ENC_BASE32); + if (!b32len) + { + LogMsg("NSEC3RecordIsDelegation: baseEncode of name %##s failed", name->c); + return mDNSNULL; + } + // Section 2.3 of RFC 4035 states that: + // + // Each owner name in the zone that has authoritative data or a delegation point NS RRset MUST + // have an NSEC resource record. + // + // This applies to NSEC3 record. So, if we have an NSEC3 record matching the question name with the + // NS bit set, then this is a delegation. + // + if (!NSEC3SameName(&cr->resrec.name->c[1], cr->resrec.name->c[0], (const mDNSu8 *)b32Name, b32len)) + { + int bmaplen; + mDNSu8 *bmap; + + LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s matches name %##s, b32name %s", CRDisplayString(m, cr), name->c, b32Name); + NSEC3Parse(&cr->resrec, mDNSNULL, mDNSNULL, mDNSNULL, &bmaplen, &bmap); + + // See the Insecure Delegation Proof section in dnssec-bis: DS bit and SOA bit + // should be absent + if (BitmapTypeCheck(bmap, bmaplen, kDNSType_SOA) || + BitmapTypeCheck(bmap, bmaplen, kDNSType_DS)) + { + LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s has DS or SOA bit set, ignoring", CRDisplayString(m, cr)); + return mDNSNULL; + } + if (BitmapTypeCheck(bmap, bmaplen, kDNSType_NS)) + return cr; + else + return mDNSNULL; + } + // If opt-out is not set, then it does not cover any delegations + if (!(nsec3->flags & NSEC3_FLAGS_OPTOUT)) + continue; + // Opt-out allows insecure delegations to exist without the NSEC3 RR at the + // hashed owner name (see RFC 5155 section 6.0). + if (NSEC3CoversName(m, cr, hashName, hlen, b32Name, b32len)) + { + LogDNSSEC("NSEC3RecordIsDelegation: CacheRecord %s covers name %##s with optout", CRDisplayString(m, cr), name->c); + return cr; + } + } + } + return mDNSNULL; +} + +#else // !DNSSEC_DISABLED + +#endif // !DNSSEC_DISABLED diff --git a/mDNSCore/nsec3.h b/mDNSCore/nsec3.h new file mode 100644 index 0000000..ce3b85a --- /dev/null +++ b/mDNSCore/nsec3.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __NSEC3_H +#define __NSEC3_H + +#include "dnssec.h" + +extern mDNSBool NSEC3WildcardAnswerProof(mDNS *const m, CacheRecord *ncr, DNSSECVerifier *dv); +extern void NSEC3NameErrorProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr); +extern void NSEC3NoDataProof(mDNS *const m, DNSSECVerifier *dv, CacheRecord *ncr); +extern CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, mDNSu16 qtype); + +#endif // __NSEC3_H diff --git a/mDNSCore/uDNS.c b/mDNSCore/uDNS.c index 040a1c9..be7a5a3 100755 --- a/mDNSCore/uDNS.c +++ b/mDNSCore/uDNS.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,6 +57,8 @@ mDNSu8 NumUnicastDNSServers = 0; (m)->NextuDNSEvent = ((rr)->LastAPTime + (rr)->ThisAPInterval); \ } +#ifndef UNICAST_DISABLED + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - General Utility Functions @@ -106,7 +108,9 @@ mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random) #pragma mark - Name Server List Management #endif -mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID) +mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr, + const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, + mDNSBool reqAAAA, mDNSBool reqDO) { DNSServer **p = &m->DNSServers; DNSServer *tmp = mDNSNULL; @@ -117,39 +121,65 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons return mDNSNULL; } - if (!d) d = (const domainname *)""; + if (!d) + d = (const domainname *)""; - LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d, resGroupID %d", addr, d->c, interface, scoped, resGroupID); + LogInfo("mDNS_AddDNSServer(%d): Adding %#a for %##s, InterfaceID %p, serviceID %u, scoped %d, resGroupID %d req_A is %s req_AAAA is %s cell %s req_DO is %s", + NumUnicastDNSServers, addr, d->c, interface, serviceID, scoped, resGroupID, reqA ? "True" : "False", reqAAAA ? "True" : "False", + cellIntf ? "True" : "False", reqDO ? "True" : "False"); - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); - while (*p) // Check if we already have this {interface,address,port,domain} tuple registered + while (*p) // Check if we already have this {interface,address,port,domain} tuple registered + reqA/reqAAAA bits { - if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled && - mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d)) + if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->serviceID == serviceID && (*p)->teststate != DNSServer_Disabled && + mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d) && + (*p)->req_A == reqA && (*p)->req_AAAA == reqAAAA) { - if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); - (*p)->flags &= ~DNSServer_FlagDelete; + if (!((*p)->flags & DNSServer_FlagDelete)) + debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface); tmp = *p; *p = tmp->next; tmp->next = mDNSNULL; } else + { p=&(*p)->next; + } } - if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer + // NumUnicastDNSServers is the count of active DNS servers i.e., ones that are not marked + // with DNSServer_FlagDelete. We should increment it: + // + // 1) When we add a new DNS server + // 2) When we resurrect a old DNS server that is marked with DNSServer_FlagDelete + // + // Don't increment when we resurrect a DNS server that is not marked with DNSServer_FlagDelete. + // We have already accounted for it when it was added for the first time. This case happens when + // we add DNS servers with the same address multiple times (mis-configuration). + + if (!tmp || (tmp->flags & DNSServer_FlagDelete)) + NumUnicastDNSServers++; + + + if (tmp) + { + tmp->flags &= ~DNSServer_FlagDelete; + *p = tmp; // move to end of list, to ensure ordering from platform layer + } else { // allocate, add to list *p = mDNSPlatformMemAllocate(sizeof(**p)); - if (!*p) LogMsg("Error: mDNS_AddDNSServer - malloc"); + if (!*p) + { + LogMsg("Error: mDNS_AddDNSServer - malloc"); + } else { - NumUnicastDNSServers++; (*p)->scoped = scoped; (*p)->interface = interface; + (*p)->serviceID = serviceID; (*p)->addr = *addr; (*p)->port = port; (*p)->flags = DNSServer_FlagNew; @@ -157,6 +187,14 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL; (*p)->timeout = timeout; (*p)->cellIntf = cellIntf; + (*p)->req_A = reqA; + (*p)->req_AAAA = reqAAAA; + (*p)->req_DO = reqDO; + // We start off assuming that the DNS server is not DNSSEC aware and + // when we receive the first response to a DNSSEC question, we set + // it to true. + (*p)->DNSSECAware = mDNSfalse; + (*p)->retransDO = 0; AssignDomainName(&(*p)->domain, d); (*p)->next = mDNSNULL; } @@ -172,17 +210,21 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons // PenalizeDNSServer is called when the number of queries to the unicast // DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an // error e.g., SERV_FAIL from DNS server. -mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q) +mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags) { DNSServer *new; DNSServer *orig = q->qDNSServer; - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("PenalizeDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); LogInfo("PenalizeDNSServer: Penalizing DNS server %#a question for question %p %##s (%s) SuppressUnusable %d", (q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL), q, q->qname.c, DNSTypeName(q->qtype), q->SuppressUnusable); + // If we get error from any DNS server, remember the error. If all of the servers, + // return the error, then return the first error. + if (mDNSOpaque16IsZero(q->responseFlags)) + q->responseFlags = responseFlags; + // After we reset the qDNSServer to NULL, we could get more SERV_FAILS that might end up // peanlizing again. if (!q->qDNSServer) goto end; @@ -307,8 +349,7 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname { DomainAuthInfo **p = &m->AuthInfoList; - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("GetAuthInfoForName_internal: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); // First purge any dead keys from the list while (*p) @@ -419,60 +460,226 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, #pragma mark - NAT Traversal #endif -mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info) +// Keep track of when to request/refresh the external address using NAT-PMP or UPnP/IGD, +// and do so when necessary +mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) { mStatus err = mStatus_NoError; + + if (!m->NATTraversals) + { + m->retryGetAddr = NonZeroTime(m->timenow + 0x78000000); + LogInfo("uDNS_RequestAddress: Setting retryGetAddr to future"); + } + else if (m->timenow - m->retryGetAddr >= 0) + { + if (mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) + { + static NATAddrRequest req = {NATMAP_VERS, NATOp_AddrRequest}; + static mDNSu8* start = (mDNSu8*)&req; + mDNSu8* end = start + sizeof(NATAddrRequest); + err = mDNSPlatformSendUDP(m, start, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); + debugf("uDNS_RequestAddress: Sent NAT-PMP external address request %d", err); + +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) + { + LNT_SendDiscoveryMsg(m); + debugf("uDNS_RequestAddress: LNT_SendDiscoveryMsg"); + } + else + { + mStatus lnterr = LNT_GetExternalAddress(m); + if (lnterr) + LogMsg("uDNS_RequestAddress: LNT_GetExternalAddress returned error %d", lnterr); + + err = err ? err : lnterr; // NAT-PMP error takes precedence + } +#endif // _LEGACY_NAT_TRAVERSAL_ + } - // send msg if we have a router and it is a private address - if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) + // Always update the interval and retry time, so that even if we fail to send the + // packet, we won't spin in an infinite loop repeatedly failing to send the packet + if (m->retryIntervalGetAddr < NATMAP_INIT_RETRY) + { + m->retryIntervalGetAddr = NATMAP_INIT_RETRY; + } + else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) + { + m->retryIntervalGetAddr *= 2; + } + else + { + m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; + } + + m->retryGetAddr = NonZeroTime(m->timenow + m->retryIntervalGetAddr); + } + else { - union { NATAddrRequest NATAddrReq; NATPortMapRequest NATPortReq; } u = { { NATMAP_VERS, NATOp_AddrRequest } } ; - const mDNSu8 *end = (mDNSu8 *)&u + sizeof(NATAddrRequest); + debugf("uDNS_RequestAddress: Not time to send address request"); + } + + // Always update NextScheduledNATOp, even if we didn't change retryGetAddr, so we'll + // be called when we need to send the request(s) + if (m->NextScheduledNATOp - m->retryGetAddr > 0) + m->NextScheduledNATOp = m->retryGetAddr; + + return err; +} - if (info) // For NATOp_MapUDP and NATOp_MapTCP, fill in additional fields +mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool usePCP) +{ + mStatus err = mStatus_NoError; + + if (!info) + { + LogMsg("uDNS_SendNATMsg called unexpectedly with NULL info"); + return mStatus_BadParamErr; + } + + // send msg if the router's address is private (which means it's non-zero) + if (mDNSv4AddrIsRFC1918(&m->Router.ip.v4)) + { + if (!usePCP) + { + if (!info->sentNATPMP) + { + if (info->Protocol) + { + static NATPortMapRequest NATPortReq; + static const mDNSu8* end = (mDNSu8 *)&NATPortReq + sizeof(NATPortMapRequest); + mDNSu8 *p = (mDNSu8 *)&NATPortReq.NATReq_lease; + + NATPortReq.vers = NATMAP_VERS; + NATPortReq.opcode = info->Protocol; + NATPortReq.unused = zeroID; + NATPortReq.intport = info->IntPort; + NATPortReq.extport = info->RequestedPort; + p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF); + p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); + p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); + p[3] = (mDNSu8)( info->NATLease & 0xFF); + + err = mDNSPlatformSendUDP(m, (mDNSu8 *)&NATPortReq, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); + debugf("uDNS_SendNATMsg: Sent NAT-PMP mapping request %d", err); + } + + // In case the address request already went out for another NAT-T, + // set the NewAddress to the currently known global external address, so + // Address-only operations will get the callback immediately + info->NewAddress = m->ExtAddress; + + // Remember that we just sent a NAT-PMP packet, so we won't resend one later. + // We do this because the NAT-PMP "Unsupported Version" response has no + // information about the (PCP) request that triggered it, so we must send + // NAT-PMP requests for all operations. Without this, we'll send n PCP + // requests for n operations, receive n NAT-PMP "Unsupported Version" + // responses, and send n NAT-PMP requests for each of those responses, + // resulting in (n + n^2) packets sent. We only want to send 2n packets: + // n PCP requests followed by n NAT-PMP requests. + info->sentNATPMP = mDNStrue; + } + } + else { - mDNSu8 *p = (mDNSu8 *)&u.NATPortReq.NATReq_lease; - u.NATPortReq.opcode = info->Protocol; - u.NATPortReq.unused = zeroID; - u.NATPortReq.intport = info->IntPort; - u.NATPortReq.extport = info->RequestedPort; + PCPMapRequest req; + mDNSu8* start = (mDNSu8*)&req; + mDNSu8* end = start + sizeof(req); + mDNSu8* p = (mDNSu8*)&req.lifetime; + + req.version = PCP_VERS; + req.opCode = PCPOp_Map; + req.reserved = zeroID; + p[0] = (mDNSu8)((info->NATLease >> 24) & 0xFF); p[1] = (mDNSu8)((info->NATLease >> 16) & 0xFF); p[2] = (mDNSu8)((info->NATLease >> 8) & 0xFF); p[3] = (mDNSu8)( info->NATLease & 0xFF); - end = (mDNSu8 *)&u + sizeof(NATPortMapRequest); - } - - err = mDNSPlatformSendUDP(m, (mDNSu8 *)&u, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); + + mDNSAddrMapIPv4toIPv6(&m->AdvertisedV4.ip.v4, &req.clientAddr); + + req.nonce[0] = m->PCPNonce[0]; + req.nonce[1] = m->PCPNonce[1]; + req.nonce[2] = m->PCPNonce[2]; + + req.protocol = (info->Protocol == NATOp_MapUDP ? PCPProto_UDP : PCPProto_TCP); + + req.reservedMapOp[0] = 0; + req.reservedMapOp[1] = 0; + req.reservedMapOp[2] = 0; + + req.intPort = info->Protocol ? info->IntPort : DiscardPort; + req.extPort = info->RequestedPort; + + // Since we only support IPv4, even if using the all-zeros address, map it, so + // the PCP gateway will give us an IPv4 address & not an IPv6 address. + mDNSAddrMapIPv4toIPv6(&info->NewAddress, &req.extAddress); + + err = mDNSPlatformSendUDP(m, start, end, 0, mDNSNULL, &m->Router, NATPMPPort, mDNSfalse); + debugf("uDNS_SendNATMsg: Sent PCP Mapping request %d", err); + + // Unset the sentNATPMP flag, so that we'll send a NAT-PMP packet if we + // receive a NAT-PMP "Unsupported Version" packet. This will result in every + // renewal, retransmission, etc. being tried first as PCP, then if a NAT-PMP + // "Unsupported Version" response is received, fall-back & send the request + // using NAT-PMP. + info->sentNATPMP = mDNSfalse; #ifdef _LEGACY_NAT_TRAVERSAL_ - if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) LNT_SendDiscoveryMsg(m); - else if (info) err = LNT_MapPort(m, info); - else err = LNT_GetExternalAddress(m); + if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) + { + LNT_SendDiscoveryMsg(m); + debugf("uDNS_SendNATMsg: LNT_SendDiscoveryMsg"); + } + else + { + mStatus lnterr = LNT_MapPort(m, info); + if (lnterr) + LogMsg("uDNS_SendNATMsg: LNT_MapPort returned error %d", lnterr); + + err = err ? err : lnterr; // PCP error takes precedence + } #endif // _LEGACY_NAT_TRAVERSAL_ + } } + return(err); } -mDNSexport void RecreateNATMappings(mDNS *const m) +mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks) { + mDNSu32 when = NonZeroTime(m->timenow + waitTicks); NATTraversalInfo *n; for (n = m->NATTraversals; n; n=n->next) { n->ExpiryTime = 0; // Mark this mapping as expired n->retryInterval = NATMAP_INIT_RETRY; - n->retryPortMap = m->timenow; + n->retryPortMap = when; + n->lastSuccessfulProtocol = NATTProtocolNone; + if (!n->Protocol) n->NewResult = mStatus_NoError; #ifdef _LEGACY_NAT_TRAVERSAL_ if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } #endif // _LEGACY_NAT_TRAVERSAL_ } + m->PCPNonce[0] = mDNSRandom(-1); + m->PCPNonce[1] = mDNSRandom(-1); + m->PCPNonce[2] = mDNSRandom(-1); + m->retryIntervalGetAddr = 0; + m->retryGetAddr = when; + +#ifdef _LEGACY_NAT_TRAVERSAL_ + LNT_ClearState(m); +#endif // _LEGACY_NAT_TRAVERSAL_ + m->NextScheduledNATOp = m->timenow; // Need to send packets immediately } mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr) { static mDNSu16 last_err = 0; + NATTraversalInfo *n; if (err) { @@ -488,11 +695,9 @@ mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv err = NATErr_NetFail; // fake error to handle routers that pathologically report success with the zero address } - if (!mDNSSameIPv4Address(m->ExternalAddress, ExtAddr)) - { - m->ExternalAddress = ExtAddr; - RecreateNATMappings(m); // Also sets NextScheduledNATOp for us - } + // Globally remember the most recently discovered address, so it can be used in each + // new NATTraversal structure + m->ExtAddress = ExtAddr; if (!err) // Success, back-off to maximum interval m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; @@ -501,10 +706,40 @@ mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv // else back-off normally in case of pathological failures m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; - if (m->NextScheduledNATOp - m->retryIntervalGetAddr > 0) - m->NextScheduledNATOp = m->retryIntervalGetAddr; + if (m->NextScheduledNATOp - m->retryGetAddr > 0) + m->NextScheduledNATOp = m->retryGetAddr; last_err = err; + + for (n = m->NATTraversals; n; n=n->next) + { + // We should change n->NewAddress only when n is one of: + // 1) a mapping operation that most recently succeeded using NAT-PMP or UPnP/IGD, + // because such an operation needs the update now. If the lastSuccessfulProtocol + // is currently none, then natTraversalHandlePortMapReplyWithAddress() will be + // called should NAT-PMP or UPnP/IGD succeed in the future. + // 2) an address-only operation that did not succeed via PCP, because when such an + // operation succeeds via PCP, it's for the TCP discard port just to learn the + // address. And that address may be different than the external address + // discovered via NAT-PMP or UPnP/IGD. If the lastSuccessfulProtocol + // is currently none, we must update the NewAddress as PCP may not succeed. + if (!mDNSSameIPv4Address(n->NewAddress, ExtAddr) && + (n->Protocol ? + (n->lastSuccessfulProtocol == NATTProtocolNATPMP || n->lastSuccessfulProtocol == NATTProtocolUPNPIGD) : + (n->lastSuccessfulProtocol != NATTProtocolPCP))) + { + // Needs an update immediately + n->NewAddress = ExtAddr; + n->ExpiryTime = 0; + n->retryInterval = NATMAP_INIT_RETRY; + n->retryPortMap = m->timenow; +#ifdef _LEGACY_NAT_TRAVERSAL_ + if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; } +#endif // _LEGACY_NAT_TRAVERSAL_ + + m->NextScheduledNATOp = m->timenow; // Need to send packets immediately + } + } } // Both places that call NATSetNextRenewalTime() update m->NextScheduledNATOp correctly afterwards @@ -516,16 +751,15 @@ mDNSlocal void NATSetNextRenewalTime(mDNS *const m, NATTraversalInfo *n) n->retryPortMap = m->timenow + n->retryInterval; } -// Note: When called from handleLNTPortMappingResponse() only pkt->err, pkt->extport and pkt->NATRep_lease fields are filled in -mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease) +mDNSlocal void natTraversalHandlePortMapReplyWithAddress(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSv4Addr extaddr, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol) { - const char *prot = n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "?"; + const char *prot = n->Protocol == 0 ? "Add" : n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "???"; (void)prot; n->NewResult = err; if (err || lease == 0 || mDNSIPPortIsZero(extport)) { - LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d error %d", - n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease, err); + LogInfo("natTraversalHandlePortMapReplyWithAddress: %p Response %s Port %5d External %.4a:%d lease %d error %d", + n, prot, mDNSVal16(n->IntPort), &extaddr, mDNSVal16(extport), lease, err); n->retryInterval = NATMAP_MAX_RETRY_INTERVAL; n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL; // No need to set m->NextScheduledNATOp here, since we're only ever extending the m->retryPortMap time @@ -538,21 +772,33 @@ mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo * lease = 999999999UL / mDNSPlatformOneSecond; n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond); - if (!mDNSSameIPPort(n->RequestedPort, extport)) - LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d changed to %5d", - n, prot, mDNSVal16(n->IntPort), mDNSVal16(n->RequestedPort), mDNSVal16(extport)); + if (!mDNSSameIPv4Address(n->NewAddress, extaddr) || !mDNSSameIPPort(n->RequestedPort, extport)) + LogInfo("natTraversalHandlePortMapReplyWithAddress: %p %s Response %s Port %5d External %.4a:%d changed to %.4a:%d lease %d", + n, + (n->lastSuccessfulProtocol == NATTProtocolNone ? "None " : + n->lastSuccessfulProtocol == NATTProtocolNATPMP ? "NAT-PMP " : + n->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" : + n->lastSuccessfulProtocol == NATTProtocolPCP ? "PCP " : + /* else */ "Unknown " ), + prot, mDNSVal16(n->IntPort), &n->NewAddress, mDNSVal16(n->RequestedPort), + &extaddr, mDNSVal16(extport), lease); n->InterfaceID = InterfaceID; - n->RequestedPort = extport; - - LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d", - n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease); + n->NewAddress = extaddr; + if (n->Protocol) n->RequestedPort = extport; // Don't report the (PCP) external port to address-only operations + n->lastSuccessfulProtocol = protocol; NATSetNextRenewalTime(m, n); // Got our port mapping; now set timer to renew it at halfway point m->NextScheduledNATOp = m->timenow; // May need to invoke client callback immediately } } +// To be called for NAT-PMP or UPnP/IGD mappings, to use currently discovered (global) address +mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol) +{ + natTraversalHandlePortMapReplyWithAddress(m, n, InterfaceID, err, m->ExtAddress, extport, lease, protocol); +} + // Must be called with the mDNS_Lock held mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal) { @@ -587,7 +833,10 @@ mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalIn traversal->retryInterval = NATMAP_INIT_RETRY; traversal->retryPortMap = m->timenow; traversal->NewResult = mStatus_NoError; + traversal->lastSuccessfulProtocol = NATTProtocolNone; + traversal->sentNATPMP = mDNSfalse; traversal->ExternalAddress = onesIPv4Addr; + traversal->NewAddress = zerov4Addr; traversal->ExternalPort = zeroIPPort; traversal->Lifetime = 0; traversal->Result = mStatus_NoError; @@ -605,6 +854,14 @@ mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalIn m->retryIntervalGetAddr = NATMAP_INIT_RETRY; } + // If this is an address-only operation, initialize to the current global address, + // or (in non-PCP environments) we won't know the address until the next external + // address request/response. + if (!traversal->Protocol) + { + traversal->NewAddress = m->ExtAddress; + } + m->NextScheduledNATOp = m->timenow; // This will always trigger sending the packet ASAP, and generate client callback if necessary *n = traversal; // Append new NATTraversalInfo to the end of our list @@ -633,23 +890,44 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra if (m->CurrentNATTraversal == traversal) m->CurrentNATTraversal = m->CurrentNATTraversal->next; - if (traversal->Protocol) - for (p = m->NATTraversals; p; p=p->next) - if (traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort)) - { - if (!mDNSSameIPPort(traversal->IntPort, SSHPort)) - LogMsg("Warning: Removed port mapping request %p Prot %d Int %d TTL %d " - "duplicates existing port mapping request %p Prot %d Int %d TTL %d", - traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, - p, p->Protocol, mDNSVal16(p->IntPort), p->NATLease); - unmap = mDNSfalse; - } + // If there is a match for the operation being stopped, don't send a deletion request (unmap) + for (p = m->NATTraversals; p; p=p->next) + { + if (traversal->Protocol ? + ((traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort)) || + (!p->Protocol && traversal->Protocol == NATOp_MapTCP && mDNSSameIPPort(traversal->IntPort, DiscardPort))) : + (!p->Protocol || (p->Protocol == NATOp_MapTCP && mDNSSameIPPort(p->IntPort, DiscardPort)))) + { + LogInfo("Warning: Removed port mapping request %p Prot %d Int %d TTL %d " + "duplicates existing port mapping request %p Prot %d Int %d TTL %d", + traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease, + p, p->Protocol, mDNSVal16( p->IntPort), p->NATLease); + unmap = mDNSfalse; + } + } if (traversal->ExpiryTime && unmap) { traversal->NATLease = 0; traversal->retryInterval = 0; - uDNS_SendNATMsg(m, traversal); + + // In case we most recently sent NAT-PMP, we need to set sentNATPMP to false so + // that we'll send a NAT-PMP request to destroy the mapping. We do this because + // the NATTraversal struct has already been cut from the list, and the client + // layer will destroy the memory upon returning from this function, so we can't + // try PCP first and then fall-back to NAT-PMP. That is, if we most recently + // created/renewed the mapping using NAT-PMP, we need to destroy it using NAT-PMP + // now, because we won't get a chance later. + traversal->sentNATPMP = mDNSfalse; + + // Both NAT-PMP & PCP RFCs state that the suggested port in deletion requests + // should be zero. And for PCP, the suggested external address should also be + // zero, specifically, the all-zeros IPv4-mapped address, since we would only + // would have requested an IPv4 address. + traversal->RequestedPort = zeroIPPort; + traversal->NewAddress = zerov4Addr; + + uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP); } // Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up @@ -880,15 +1158,15 @@ mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *co // Don't reset the state to IntialRequest as we may write that to the dynamic store // and PrefPane might wrongly think that we are "Starting" instead of "Polling". If - // we are in polling state because of NAT-PMP disabled or DoubleNAT, next LLQNATCallback + // we are in polling state because of PCP/NAT-PMP disabled or DoubleNAT, next LLQNATCallback // would kick us back to LLQInitialRequest. So, resetting the state here may not be useful. // - // If we have a good NAT (neither NAT-PMP disabled nor Double-NAT), then we should not be + // If we have a good NAT (neither PCP/NAT-PMP disabled nor Double-NAT), then we should not be // possibly in polling state. To be safe, we want to retry from the start in that case // as there may not be another LLQNATCallback // // NOTE: We can be in polling state if we cannot resolve the SOA record i.e, servAddr is set to - // all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the NAT-PMP or + // all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the PCP/NAT-PMP or // Double-NAT state. if (!mDNSAddressIsOnes(&q->servAddr) && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && !m->LLQNAT.Result) @@ -1024,7 +1302,12 @@ mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEs InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); if (DNSSECQuestion(q) && q->qDNSServer && !q->qDNSServer->cellIntf) - end = putDNSSECOption(&tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); + { + if (q->ProxyQuestion) + end = DNSProxySetAttributes(q, &tcpInfo->request.h, &tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); + else + end = putDNSSECOption(&tcpInfo->request, end, tcpInfo->request.data + AbsoluteMaxDNSMessageData); + } AuthInfo = q->AuthInfo; // Need to add TSIG to this message } @@ -1241,7 +1524,7 @@ mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, con tcpInfo_t *info; mDNSBool useBackgroundTrafficClass; - useBackgroundTrafficClass = question ? question->UseBrackgroundTrafficClass : mDNSfalse; + useBackgroundTrafficClass = question ? question->UseBackgroundTrafficClass : mDNSfalse; if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0])) { LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; } @@ -1294,7 +1577,7 @@ mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) // Lock must be held mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) { - if (mDNSIPv4AddressIsOnes(m->LLQNAT.ExternalAddress)) + if (m->LLQNAT.clientContext != mDNSNULL) // LLQNAT just started, give it some time { LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes @@ -1303,8 +1586,8 @@ mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) return; } - // Either we don't have NAT-PMP support (ExternalPort is zero) or behind a Double NAT that may or - // may not have NAT-PMP support (NATResult is non-zero) + // Either we don't have {PCP, NAT-PMP, UPnP/IGD} support (ExternalPort is zero) or behind a Double NAT that may or + // may not have {PCP, NAT-PMP, UPnP/IGD} support (NATResult is non-zero) if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result) { LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d", @@ -1596,10 +1879,13 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.RetryWithSearchDomains = mDNSfalse; zd->question.TimeoutQuestion = 0; zd->question.WakeOnResolve = 0; - zd->question.UseBrackgroundTrafficClass = mDNSfalse; + zd->question.UseBackgroundTrafficClass = mDNSfalse; zd->question.ValidationRequired = 0; zd->question.ValidatingResponse = 0; + zd->question.ProxyQuestion = 0; zd->question.qnameOrig = mDNSNULL; + zd->question.AnonInfo = mDNSNULL; + zd->question.pid = mDNSPlatformGetPID(); zd->question.QuestionCallback = GetZoneData_QuestionCallback; zd->question.QuestionContext = zd; @@ -1966,8 +2252,7 @@ mDNSlocal void UpdateOneSRVRecord(mDNS *m, AuthRecord *rr) rr->resrec.name->c, newtarget, TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged); - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("UpdateOneSRVRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); if (!TargetChanged && !NATChanged) return; @@ -2281,10 +2566,13 @@ mDNSlocal void GetStaticHostname(mDNS *m) q->RetryWithSearchDomains = mDNSfalse; q->TimeoutQuestion = 0; q->WakeOnResolve = 0; - q->UseBrackgroundTrafficClass = mDNSfalse; + q->UseBackgroundTrafficClass = mDNSfalse; q->ValidationRequired = 0; q->ValidatingResponse = 0; + q->ProxyQuestion = 0; q->qnameOrig = mDNSNULL; + q->AnonInfo = mDNSNULL; + q->pid = mDNSPlatformGetPID(); q->QuestionCallback = FoundStaticHostname; q->QuestionContext = mDNSNULL; @@ -2338,7 +2626,7 @@ mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal); // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback } - if (!m->mDNS_busy) LogMsg("mDNS_RemoveDynDNSHostName: ERROR: Lock not held"); + mDNS_CheckLock(m); m->NextSRVUpdate = NonZeroTime(m->timenow); } @@ -2405,18 +2693,20 @@ mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, co // If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway // If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients // Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up - m->ExternalAddress = zerov4Addr; - m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - m->retryGetAddr = m->timenow + (v4addr ? 0 : mDNSPlatformOneSecond * 5); - m->NextScheduledNATOp = m->timenow; + mDNSu32 waitSeconds = v4addr ? 0 : 5; + NATTraversalInfo *n; + m->ExtAddress = zerov4Addr; m->LastNATMapResultCode = NATErr_None; -#ifdef _LEGACY_NAT_TRAVERSAL_ - LNT_ClearState(m); -#endif // _LEGACY_NAT_TRAVERSAL_ - LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: retryGetAddr in %d %d", + + RecreateNATMappings(m, mDNSPlatformOneSecond * waitSeconds); + + for (n = m->NATTraversals; n; n=n->next) + n->NewAddress = zerov4Addr; + + LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: recreating NAT mappings in %d seconds", v4Changed ? " v4Changed" : "", RouterChanged ? " RouterChanged" : "", - m->retryGetAddr - m->timenow, m->timenow); + waitSeconds); } if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap); @@ -2649,8 +2939,7 @@ mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr) AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name); limit -= RRAdditionalSize(m, AuthInfo); - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) { @@ -2878,7 +3167,7 @@ mDNSlocal AuthRecord *MarkRRForSending(mDNS *const m) else if (!AreRecordsMergeable(m, firstRR, rr, m->timenow + MERGE_DELAY_TIME)) continue; if (rr->SendRNow) LogMsg("MarkRRForSending: Resourcerecord %s already marked for sending", ARDisplayString(m, rr)); - rr->SendRNow = mDNSInterfaceMark; + rr->SendRNow = uDNSInterfaceMark; } // We parsed through all records and found something to send. The services/records might @@ -2898,10 +3187,10 @@ mDNSlocal AuthRecord *MarkRRForSending(mDNS *const m) for (rr = m->ResourceRecords; rr; rr = rr->next) { if ((rr->state != regState_Registered && rr->state != regState_Refresh) || - (rr->SendRNow == mDNSInterfaceMark) || + (rr->SendRNow == uDNSInterfaceMark) || (!AreRecordsMergeable(m, firstRR, rr, m->timenow + rr->ThisAPInterval/2))) continue; - rr->SendRNow = mDNSInterfaceMark; + rr->SendRNow = uDNSInterfaceMark; acc++; } if (acc) LogInfo("MarkRRForSending: Accelereated %d records", acc); @@ -2947,7 +3236,7 @@ mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m) zoneSize = 0; for (rr = startRR; rr; rr = rr->next) { - if (rr->SendRNow != mDNSInterfaceMark) continue; + if (rr->SendRNow != uDNSInterfaceMark) continue; rr->SendRNow = mDNSNULL; @@ -3006,7 +3295,7 @@ mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m) { LogInfo("SendGroupUpdates: Skipping message %s, spaceleft %d, rrSize %d", ARDisplayString(m, rr), spaceleft, rrSize); // Mark this as not sent so that the caller knows about it - rr->SendRNow = mDNSInterfaceMark; + rr->SendRNow = uDNSInterfaceMark; // We need to remove the merge delay so that we can send it immediately rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL; rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL; @@ -3075,7 +3364,7 @@ mDNSlocal void CheckGroupRecordUpdates(mDNS *const m) // SendRecordRegistrtion might delete the rr from list, hence // dereference nextRR before calling the function nextRR = rr->next; - if (rr->SendRNow == mDNSInterfaceMark) + if (rr->SendRNow == uDNSInterfaceMark) { // Any records marked for sending should be eligible to be sent out // immediately. Just being cautious @@ -3160,14 +3449,13 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu mDNSBool InvokeCallback = mDNStrue; mDNSIPPort UpdatePort = zeroIPPort; - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr); rr->updateError = err; #if APPLE_OSX_mDNSResponder - if (err == mStatus_BadSig || err == mStatus_BadKey) UpdateAutoTunnelDomainStatuses(m); + if (err == mStatus_BadSig || err == mStatus_BadKey || err == mStatus_BadTime) UpdateAutoTunnelDomainStatuses(m); #endif SetRecordRetry(m, rr, random); @@ -3298,19 +3586,45 @@ mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc. } -mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) +mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) { NATTraversalInfo *ptr; NATAddrReply *AddrReply = (NATAddrReply *)pkt; NATPortMapReply *PortMapReply = (NATPortMapReply *)pkt; mDNSu32 nat_elapsed, our_elapsed; - // Minimum packet is vers (1) opcode (1) err (2) upseconds (4) = 8 bytes - if (!AddrReply->err && len < 8) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; } - if (AddrReply->vers != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expected %d)", pkt[0], NATMAP_VERS); return; } + // Minimum NAT-PMP packet is vers (1) opcode (1) + err (2) = 4 bytes + if (len < 4) { LogMsg("NAT-PMP message too short (%d bytes)", len); return; } + + // Read multi-byte error value (field is identical in a NATPortMapReply) + AddrReply->err = (mDNSu16) ((mDNSu16)pkt[2] << 8 | pkt[3]); + + if (AddrReply->err == NATErr_Vers) + { + NATTraversalInfo *n; + LogInfo("NAT-PMP version unsupported message received"); + for (n = m->NATTraversals; n; n=n->next) + { + // Send a NAT-PMP request for this operation as needed + // and update the state variables + uDNS_SendNATMsg(m, n, mDNSfalse); + } + + m->NextScheduledNATOp = m->timenow; + + return; + } - // Read multi-byte numeric values (fields are identical in a NATPortMapReply) - AddrReply->err = (mDNSu16) ( (mDNSu16)pkt[2] << 8 | pkt[3]); + // The minimum reasonable NAT-PMP packet length is vers (1) + opcode (1) + err (2) + upseconds (4) = 8 bytes + // If it's not at least this long, bail before we byte-swap the upseconds field & overrun our buffer. + // The retry timer will ensure we converge to correctness. + if (len < 8) + { + LogMsg("NAT-PMP message too short (%d bytes) 0x%X 0x%X", len, AddrReply->opcode, AddrReply->err); + return; + } + + // Read multi-byte upseconds value (field is identical in a NATPortMapReply) AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]); nat_elapsed = AddrReply->upseconds - m->LastNATupseconds; @@ -3328,7 +3642,7 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac // giving an apparent local time difference of 7 seconds // The two-second safety margin coves this possible calculation discrepancy if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8) - { LogMsg("NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m); } + { LogMsg("NAT-PMP epoch time check failed: assuming NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m, 0); } m->LastNATupseconds = AddrReply->upseconds; m->LastNATReplyLocalTime = m->timenow; @@ -3343,7 +3657,7 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err); mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, ""); #endif - if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal AddrResponse message too short (%d bytes)", len); return; } + if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT-PMP AddrResponse message too short (%d bytes)", len); return; } natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr); } else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse) @@ -3356,7 +3670,7 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac #endif if (!PortMapReply->err) { - if (len < sizeof(NATPortMapReply)) { LogMsg("NAT Traversal PortMapReply message too short (%d bytes)", len); return; } + if (len < sizeof(NATPortMapReply)) { LogMsg("NAT-PMP PortMapReply message too short (%d bytes)", len); return; } PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]); } @@ -3367,14 +3681,157 @@ mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interfac for (ptr = m->NATTraversals; ptr; ptr=ptr->next) if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport)) - natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease); + natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease, NATTProtocolNATPMP); } - else { LogMsg("Received NAT Traversal response with version unknown opcode 0x%X", AddrReply->opcode); return; } + else { LogMsg("Received NAT-PMP response with unknown opcode 0x%X", AddrReply->opcode); return; } // Don't need an SSDP socket if we get a NAT-PMP packet if (m->SSDPSocket) { debugf("uDNS_ReceiveNATPMPPacket destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } } +mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) +{ + NATTraversalInfo *ptr; + PCPMapReply *reply = (PCPMapReply*)pkt; + mDNSu32 client_delta, server_delta; + mDNSBool checkEpochValidity = m->LastNATupseconds != 0; + mDNSu8 strippedOpCode; + mDNSv4Addr mappedAddress = zerov4Addr; + mDNSu8 protocol = 0; + mDNSIPPort intport = zeroIPPort; + mDNSIPPort extport = zeroIPPort; + + // Minimum PCP packet is 24 bytes + if (len < 24) + { + LogMsg("uDNS_ReceivePCPPacket: message too short (%d bytes)", len); + return; + } + + strippedOpCode = reply->opCode & 0x7f; + + if ((reply->opCode & 0x80) == 0x00 || (strippedOpCode != PCPOp_Announce && strippedOpCode != PCPOp_Map)) + { + LogMsg("uDNS_ReceivePCPPacket: unhandled opCode %u", reply->opCode); + return; + } + + // Read multi-byte values + reply->lifetime = (mDNSs32)((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[ 6] << 8 | pkt[ 7]); + reply->epoch = (mDNSs32)((mDNSs32)pkt[8] << 24 | (mDNSs32)pkt[9] << 16 | (mDNSs32)pkt[10] << 8 | pkt[11]); + + client_delta = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond; + server_delta = reply->epoch - m->LastNATupseconds; + debugf("uDNS_ReceivePCPPacket: %X %X upseconds %u client_delta %d server_delta %d", reply->opCode, reply->result, reply->epoch, client_delta, server_delta); + + // If seconds since the epoch is 0, use 1 so we'll check epoch validity next time + m->LastNATupseconds = reply->epoch ? reply->epoch : 1; + m->LastNATReplyLocalTime = m->timenow; + +#ifdef _LEGACY_NAT_TRAVERSAL_ + LNT_ClearState(m); +#endif // _LEGACY_NAT_TRAVERSAL_ + + // Don't need an SSDP socket if we get a PCP packet + if (m->SSDPSocket) { debugf("uDNS_ReceivePCPPacket: destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } + + if (checkEpochValidity && (client_delta + 2 < server_delta - server_delta / 16 || server_delta + 2 < client_delta - client_delta / 16)) + { + // If this is an ANNOUNCE packet, wait a random interval up to 5 seconds + // otherwise, refresh immediately + mDNSu32 waitTicks = strippedOpCode ? 0 : mDNSRandom(PCP_WAITSECS_AFTER_EPOCH_INVALID * mDNSPlatformOneSecond); + LogMsg("uDNS_ReceivePCPPacket: Epoch invalid, %#a likely rebooted, waiting %u ticks", &m->Router, waitTicks); + RecreateNATMappings(m, waitTicks); + // we can ignore the rest of this packet, as new requests are about to go out + return; + } + + if (strippedOpCode == PCPOp_Announce) + return; + + // We globally keep track of the most recent error code for mappings. + // This seems bad to do with PCP, but best not change it now. + m->LastNATMapResultCode = reply->result; + + if (!reply->result) + { + if (len < sizeof(PCPMapReply)) + { + LogMsg("uDNS_ReceivePCPPacket: mapping response too short (%d bytes)", len); + return; + } + + // Check the nonce + if (reply->nonce[0] != m->PCPNonce[0] || reply->nonce[1] != m->PCPNonce[1] || reply->nonce[2] != m->PCPNonce[2]) + { + LogMsg("uDNS_ReceivePCPPacket: invalid nonce, ignoring. received { %x %x %x } expected { %x %x %x }", + reply->nonce[0], reply->nonce[1], reply->nonce[2], + m->PCPNonce[0], m->PCPNonce[1], m->PCPNonce[2]); + return; + } + + // Get the values + protocol = reply->protocol; + intport = reply->intPort; + extport = reply->extPort; + + // Get the external address, which should be mapped, since we only support IPv4 + if (!mDNSAddrIPv4FromMappedIPv6(&reply->extAddress, &mappedAddress)) + { + LogMsg("uDNS_ReceivePCPPacket: unexpected external address: %.16a", &reply->extAddress); + reply->result = NATErr_NetFail; + // fall through to report the error + } + else if (mDNSIPv4AddressIsZero(mappedAddress)) + { + // If this is the deletion case, we will have sent the zero IPv4-mapped address + // in our request, and the server should reflect it in the response, so we + // should not log about receiving a zero address. And in this case, we no + // longer have a NATTraversal to report errors back to, so it's ok to set the + // result here. + // In other cases, a zero address is an error, and we will have a NATTraversal + // to report back to, so set an error and fall through to report it. + // CheckNATMappings will log the error. + reply->result = NATErr_NetFail; + } + } + else + { + LogInfo("uDNS_ReceivePCPPacket: error received from server. opcode %X result %X lifetime %X epoch %X", + reply->opCode, reply->result, reply->lifetime, reply->epoch); + + // If the packet is long enough, get the protocol & intport for matching to report + // the error + if (len >= sizeof(PCPMapReply)) + { + protocol = reply->protocol; + intport = reply->intPort; + } + } + + for (ptr = m->NATTraversals; ptr; ptr=ptr->next) + { + mDNSu8 ptrProtocol = ((ptr->Protocol & NATOp_MapTCP) == NATOp_MapTCP ? PCPProto_TCP : PCPProto_UDP); + if ((protocol == ptrProtocol && mDNSSameIPPort(ptr->IntPort, intport)) || + (!ptr->Protocol && protocol == PCPProto_TCP && mDNSSameIPPort(DiscardPort, intport))) + { + natTraversalHandlePortMapReplyWithAddress(m, ptr, InterfaceID, reply->result ? NATErr_NetFail : NATErr_None, mappedAddress, extport, reply->lifetime, NATTProtocolPCP); + } + } +} + +mDNSexport void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len) +{ + if (len == 0) + LogMsg("uDNS_ReceiveNATPacket: zero length packet"); + else if (pkt[0] == PCP_VERS) + uDNS_ReceivePCPPacket(m, InterfaceID, pkt, len); + else if (pkt[0] == NATMAP_VERS) + uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, len); + else + LogMsg("uDNS_ReceiveNATPacket: packet with version %u (expected %u or %u)", pkt[0], PCP_VERS, NATMAP_VERS); +} + // Shorten DNS-SD queries to avoid NAT bugs // Add check to avoid crashing NAT gateways that have buggy DNS relay code // @@ -3622,7 +4079,19 @@ mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) if (PrivateQuery(q) && !q->tcp) { LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - if (!q->nta) { LogMsg("sendLLQRefresh:ERROR!! q->nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; } + if (!q->nta) + { + // Note: If a question is in LLQ_Established state, we never free the zone data for the + // question (PrivateQuery). If we free, we reset the state to something other than LLQ_Established. + // This function is called only if the query is in LLQ_Established state and hence nta should + // never be NULL. In spite of that, we have seen q->nta being NULL in the field. Just refetch the + // zone data in that case. + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q); + return; + // ThisQInterval is not adjusted when we return from here which means that we will get called back + // again immediately. As q->servAddr and q->servPort are still valid and the nta->Host is initialized + // without any additional discovery for PrivateQuery, things work. + } q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL); } else @@ -3928,8 +4397,7 @@ mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) mDNSu8 *limit; DomainAuthInfo *AuthInfo; - if (m->mDNS_busy != m->mDNS_reentrancy+1) - LogMsg("SendRecordDeRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy); + mDNS_CheckLock(m); if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) { @@ -3993,7 +4461,7 @@ mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr) case regState_NATMap: // A record could be in NoTarget to start with if the corresponding SRV record could not find a target. // It is also possible to reenter the NoTarget state when we move to a network with a NAT that has - // no NAT-PMP/UPnP support. In that case before we entered NoTarget, we already deregistered with + // no {PCP, NAT-PMP, UPnP/IGD} support. In that case before we entered NoTarget, we already deregistered with // the server. case regState_NoTarget: case regState_Unregistered: @@ -4160,6 +4628,47 @@ unreg_error: #pragma mark - Periodic Execution Routines #endif +mDNSlocal void handle_unanswered_query(mDNS *const m) +{ + DNSQuestion *q = m->CurrentQuestion; + + if (q->unansweredQueries >= MAX_DNSSEC_UNANSWERED_QUERIES && DNSSECOptionalQuestion(q)) + { + // If we are not receiving any responses for DNSSEC question, it could be due to + // a broken middlebox or a DNS server that does not understand the EDNS0/DOK option that + // silently drops the packets. Also as per RFC 5625 there are certain buggy DNS Proxies + // that are known to drop these pkts. To handle this, we turn off sending the EDNS0/DOK + // option if we have not received any responses indicating that the server or + // the middlebox is DNSSEC aware. If we receive at least one response to a DNSSEC + // question, we don't turn off validation. Also, we wait for MAX_DNSSEC_RETRANSMISSIONS + // before turning off validation to accomodate packet loss. + // + // Note: req_DO affects only DNSSEC_VALIDATION_SECURE_OPTIONAL questions; + // DNSSEC_VALIDATION_SECURE questions ignores req_DO. + + if (q->qDNSServer && !q->qDNSServer->DNSSECAware && q->qDNSServer->req_DO) + { + q->qDNSServer->retransDO++; + if (q->qDNSServer->retransDO == MAX_DNSSEC_RETRANSMISSIONS) + { + LogInfo("handle_unanswered_query: setting req_DO false for %#a", &q->qDNSServer->addr); + q->qDNSServer->req_DO = mDNSfalse; + } + } + + if (!q->qDNSServer->req_DO) + { + q->ValidationState = DNSSECValNotRequired; + q->ValidationRequired = DNSSEC_VALIDATION_NONE; + + if (q->ProxyQuestion) + q->ProxyDNSSECOK = mDNSfalse; + LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a", + q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr); + } + } +} + // The question to be checked is not passed in as an explicit parameter; // instead it is implicit that the question to be checked is m->CurrentQuestion. mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) @@ -4184,6 +4693,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) } } + handle_unanswered_query(m); // We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll if (!(q->LongLived && q->state != LLQ_Poll)) { @@ -4194,7 +4704,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)", q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c); - PenalizeDNSServer(m, q); + PenalizeDNSServer(m, q, zeroID); q->noServerResponse = 1; } // There are two cases here. @@ -4240,7 +4750,12 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) - end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); + { + if (q->ProxyQuestion) + end = DNSProxySetAttributes(q, &m->omsg.h, &m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); + else + end = putDNSSECOption(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData); + } private = PrivateQuery(q); } else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL) // Make sure at least three seconds has elapsed since last test query @@ -4266,9 +4781,14 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d", q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries); - if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (!q->LocalSocket) + { + q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); + if (q->LocalSocket) + mDNSPlatformSetDelegatePID(q->LocalSocket, &q->qDNSServer->addr, q); + } if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time - else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBrackgroundTrafficClass); + else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass); } } @@ -4310,13 +4830,9 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) // (When we have a group of identical questions, only the active representative of the group gets // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire -- // but we want *all* of the questions to get answer callbacks.) - CacheRecord *rr; const mDNSu32 slot = HashSlot(&q->qname); CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname); - if (cg) - for (rr = cg->members; rr; rr=rr->next) - if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr); if (!q->qDNSServer) { @@ -4358,16 +4874,30 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c); } + if (cg) + { + for (rr = cg->members; rr; rr=rr->next) + { + if (SameNameRecordAnswersQuestion(&rr->resrec, q)) + { + LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); + mDNS_PurgeCacheResourceRecord(m, rr); + } + } + } // For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers // don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try // every fifteen minutes in that case MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (DomainEnumQuery(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer); q->unansweredQueries = 0; + if (!mDNSOpaque16IsZero(q->responseFlags)) + m->rec.r.responseFlags = q->responseFlags; // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list. // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we // momentarily defer generating answer callbacks until mDNS_Execute time. CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow), mDNStrue, mDNSNULL); ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow)); + m->rec.r.responseFlags = zeroID; m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it } @@ -4381,9 +4911,9 @@ mDNSexport void CheckNATMappings(mDNS *m) mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4); m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF; - if (HaveRoutable) m->ExternalAddress = m->AdvertisedV4.ip.v4; + if (HaveRoutable) m->ExtAddress = m->AdvertisedV4.ip.v4; - if (m->NATTraversals && rfc1918) // Do we need to open NAT-PMP socket to receive multicast announcements from router? + if (m->NATTraversals && rfc1918) // Do we need to open a socket to receive multicast announcements from router? { if (m->NATMcastRecvskt == mDNSNULL) // If we are behind a NAT and the socket hasn't been opened yet, open it { @@ -4394,7 +4924,7 @@ mDNSexport void CheckNATMappings(mDNS *m) { if (needLog) { - LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for NAT-PMP announcements"); + LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for PCP & NAT-PMP announcements"); needLog = mDNSfalse; } } @@ -4408,29 +4938,7 @@ mDNSexport void CheckNATMappings(mDNS *m) if (m->SSDPSocket) { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; } } - if (!m->NATTraversals) - m->retryGetAddr = m->timenow + 0x78000000; - else - { - if (m->timenow - m->retryGetAddr >= 0) - { - err = uDNS_SendNATMsg(m, mDNSNULL); // Will also do UPnP discovery for us, if necessary - if (!err) - { - if (m->retryIntervalGetAddr < NATMAP_INIT_RETRY) m->retryIntervalGetAddr = NATMAP_INIT_RETRY; - else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) m->retryIntervalGetAddr *= 2; - else m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL; - } - LogInfo("CheckNATMappings retryGetAddr sent address request err %d interval %d", err, m->retryIntervalGetAddr); - - // Always update m->retryGetAddr, even if we fail to send the packet. Otherwise in cases where we can't send the packet - // (like when we have no active interfaces) we'll spin in an infinite loop repeatedly failing to send the packet - m->retryGetAddr = m->timenow + m->retryIntervalGetAddr; - } - // Even when we didn't send the GetAddr packet, still need to make sure NextScheduledNATOp is set correctly - if (m->NextScheduledNATOp - m->retryGetAddr > 0) - m->NextScheduledNATOp = m->retryGetAddr; - } + uDNS_RequestAddress(m); if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use"); m->CurrentNATTraversal = m->NATTraversals; @@ -4438,6 +4946,7 @@ mDNSexport void CheckNATMappings(mDNS *m) while (m->CurrentNATTraversal) { NATTraversalInfo *cur = m->CurrentNATTraversal; + mDNSv4Addr EffectiveAddress = HaveRoutable ? m->AdvertisedV4.ip.v4 : cur->NewAddress; m->CurrentNATTraversal = m->CurrentNATTraversal->next; if (HaveRoutable) // If not RFC 1918 address, our own address and port are effectively our external address and port @@ -4445,9 +4954,9 @@ mDNSexport void CheckNATMappings(mDNS *m) cur->ExpiryTime = 0; cur->NewResult = mStatus_NoError; } - else if (cur->Protocol) // Check if it's time to send port mapping packets + else // Check if it's time to send port mapping packet(s) { - if (m->timenow - cur->retryPortMap >= 0) // Time to do something with this mapping + if (m->timenow - cur->retryPortMap >= 0) // Time to send a mapping request for this packet { if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0) // Mapping has expired { @@ -4455,8 +4964,7 @@ mDNSexport void CheckNATMappings(mDNS *m) cur->retryInterval = NATMAP_INIT_RETRY; } - //LogMsg("uDNS_SendNATMsg"); - err = uDNS_SendNATMsg(m, cur); + err = uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry NATSetNextRenewalTime(m, cur); @@ -4470,24 +4978,35 @@ mDNSexport void CheckNATMappings(mDNS *m) } if (m->NextScheduledNATOp - cur->retryPortMap > 0) + { m->NextScheduledNATOp = cur->retryPortMap; + } } // Notify the client if necessary. We invoke the callback if: - // (1) we have an ExternalAddress, or we've tried and failed a couple of times to discover it - // and (2) the client doesn't want a mapping, or the client won't need a mapping, or the client has a successful mapping, or we've tried and failed a couple of times - // and (3) we have new data to give the client that's changed since the last callback + // (1) We have an effective address, + // or we've tried and failed a couple of times to discover it + // AND + // (2) the client requested the address only, + // or the client won't need a mapping because we have a routable address, + // or the client has an expiry time and therefore a successful mapping, + // or we've tried and failed a couple of times (see "Time line" below) + // AND + // (3) we have new data to give the client that's changed since the last callback + // // Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send // At this point we've sent three requests without an answer, we've just sent our fourth request, - // retryIntervalGetAddr is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds), + // retryInterval is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds), // so we return an error result to the caller. - if (!mDNSIPv4AddressIsZero(m->ExternalAddress) || m->retryIntervalGetAddr > NATMAP_INIT_RETRY * 8) + if (!mDNSIPv4AddressIsZero(EffectiveAddress) || cur->retryInterval > NATMAP_INIT_RETRY * 8) { - const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&m->ExternalAddress) ? mStatus_DoubleNAT : mStatus_NoError; + const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&EffectiveAddress) ? mStatus_DoubleNAT : mStatus_NoError; const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort : - !mDNSIPv4AddressIsZero(m->ExternalAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort; + !mDNSIPv4AddressIsZero(EffectiveAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort; + if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8) - if (!mDNSSameIPv4Address(cur->ExternalAddress, m->ExternalAddress) || + { + if (!mDNSSameIPv4Address(cur->ExternalAddress, EffectiveAddress) || !mDNSSameIPPort (cur->ExternalPort, ExternalPort) || cur->Result != EffectiveResult) { @@ -4496,13 +5015,13 @@ mDNSexport void CheckNATMappings(mDNS *m) { if (!EffectiveResult) LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", - cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); + cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); else LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d", - cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); + cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult); } - cur->ExternalAddress = m->ExternalAddress; + cur->ExternalAddress = EffectiveAddress; cur->ExternalPort = ExternalPort; cur->Lifetime = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ? (cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0; @@ -4513,6 +5032,7 @@ mDNSexport void CheckNATMappings(mDNS *m) mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again // MUST NOT touch cur after invoking the callback } + } } } } @@ -4776,8 +5296,8 @@ mDNSexport void udns_validatelists(void *const v) DomainAuthInfo *info; for (info = m->AuthInfoList; info; info = info->next) - if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (const char*)~0) - LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel); + if (info->next == (DomainAuthInfo *)~0) + LogMemCorruption("m->AuthInfoList: %p is garbage", info); HostnameInfo *hi; for (hi = m->Hostnames; hi; hi = hi->next) @@ -4794,89 +5314,331 @@ mDNSexport void udns_validatelists(void *const v) // This should probably move to the UDS daemon -- the concept of legacy clients and automatic registration / automatic browsing // is really a UDS API issue, not something intrinsic to uDNS -mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action) +mDNSlocal void uDNS_DeleteWABQueries(mDNS *const m, SearchListElem *ptr, int delete) +{ + const char *name1 = mDNSNULL; + const char *name2 = mDNSNULL; + ARListElem **arList = &ptr->AuthRecs; + domainname namestorage1, namestorage2; + mStatus err; + + // "delete" parameter indicates the type of query. + switch (delete) + { + case UDNS_WAB_BROWSE_QUERY: + mDNS_StopGetDomains(m, &ptr->BrowseQ); + mDNS_StopGetDomains(m, &ptr->DefBrowseQ); + name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse]; + name2 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]; + break; + case UDNS_WAB_LBROWSE_QUERY: + mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); + name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic]; + break; + case UDNS_WAB_REG_QUERY: + mDNS_StopGetDomains(m, &ptr->RegisterQ); + mDNS_StopGetDomains(m, &ptr->DefRegisterQ); + name1 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration]; + name2 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault]; + break; + default: + LogMsg("uDNS_DeleteWABQueries: ERROR!! returning from default"); + return; + } + // When we get the results to the domain enumeration queries, we add a LocalOnly + // entry. For example, if we issue a domain enumeration query for b._dns-sd._udp.xxxx.com, + // and when we get a response, we add a LocalOnly entry b._dns-sd._udp.local whose RDATA + // points to what we got in the response. Locate the appropriate LocalOnly entries and delete + // them. + if (name1) + { + MakeDomainNameFromDNSNameString(&namestorage1, name1); + AppendDNSNameString(&namestorage1, "local"); + } + if (name2) + { + MakeDomainNameFromDNSNameString(&namestorage2, name2); + AppendDNSNameString(&namestorage2, "local"); + } + while (*arList) + { + ARListElem *dereg = *arList; + if ((name1 && SameDomainName(&dereg->ar.namestorage, &namestorage1)) || + (name2 && SameDomainName(&dereg->ar.namestorage, &namestorage2))) + { + LogInfo("uDNS_DeleteWABQueries: Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); + *arList = dereg->next; + err = mDNS_Deregister(m, &dereg->ar); + if (err) LogMsg("uDNS_DeleteWABQueries:: ERROR!! mDNS_Deregister returned %d", err); + // Memory will be freed in the FreeARElemCallback + } + else + { + LogInfo("uDNS_DeleteWABQueries: Skipping PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); + arList = &(*arList)->next; + } + } +} + +mDNSexport void uDNS_SetupWABQueries(mDNS *const m) { SearchListElem **p = &SearchList, *ptr; mStatus err; + int action = 0; // step 1: mark each element for removal - for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag |= SLE_DELETE; + for (ptr = SearchList; ptr; ptr = ptr->next) + ptr->flag |= SLE_DELETE; // Make sure we have the search domains from the platform layer so that if we start the WAB - // queries below, we have the latest information + // queries below, we have the latest information. mDNS_Lock(m); - mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL); + if (!mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL, mDNSfalse)) + { + // If the configuration did not change, clear the flag so that we don't free the searchlist. + // We still have to start the domain enumeration queries as we may not have started them + // before. + for (ptr = SearchList; ptr; ptr = ptr->next) + ptr->flag &= ~SLE_DELETE; + LogInfo("uDNS_SetupWABQueries: No config change"); + } mDNS_Unlock(m); - if (action & UDNS_START_WAB_QUERY) - m->StartWABQueries = mDNStrue; + if (m->WABBrowseQueriesCount) + action |= UDNS_WAB_BROWSE_QUERY; + if (m->WABLBrowseQueriesCount) + action |= UDNS_WAB_LBROWSE_QUERY; + if (m->WABRegQueriesCount) + action |= UDNS_WAB_REG_QUERY; + // delete elems marked for removal, do queries for elems marked add while (*p) { ptr = *p; - LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d, AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c); - if (ptr->flag & SLE_DELETE) + LogInfo("uDNS_SetupWABQueries:action 0x%x: Flags 0x%x, AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c); + // If SLE_DELETE is set, stop all the queries, deregister all the records and free the memory. + // Otherwise, check to see what the "action" requires. If a particular action bit is not set and + // we have started the corresponding queries as indicated by the "flags", stop those queries and + // deregister the records corresponding to them. + if ((ptr->flag & SLE_DELETE) || + (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED)) || + (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED)) || + (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED))) { - ARListElem *arList = ptr->AuthRecs; - ptr->AuthRecs = mDNSNULL; - *p = ptr->next; + if (ptr->flag & SLE_DELETE) + { + ARListElem *arList = ptr->AuthRecs; + ptr->AuthRecs = mDNSNULL; + *p = ptr->next; + + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries + // We suppressed the domain enumeration for scoped search domains below. When we enable that + // enable this. + if ((ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: DELETE Browse for domain %##s", ptr->domain.c); + mDNS_StopGetDomains(m, &ptr->BrowseQ); + mDNS_StopGetDomains(m, &ptr->DefBrowseQ); + } + if ((ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: DELETE Legacy Browse for domain %##s", ptr->domain.c); + mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); + } + if ((ptr->flag & SLE_WAB_REG_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: DELETE Registration for domain %##s", ptr->domain.c); + mDNS_StopGetDomains(m, &ptr->RegisterQ); + mDNS_StopGetDomains(m, &ptr->DefRegisterQ); + } + + mDNSPlatformMemFree(ptr); + + // deregister records generated from answers to the query + while (arList) + { + ARListElem *dereg = arList; + arList = arList->next; + LogInfo("uDNS_SetupWABQueries: DELETE Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); + err = mDNS_Deregister(m, &dereg->ar); + if (err) LogMsg("uDNS_SetupWABQueries:: ERROR!! mDNS_Deregister returned %d", err); + // Memory will be freed in the FreeARElemCallback + } + continue; + } // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries // We suppressed the domain enumeration for scoped search domains below. When we enable that // enable this. - if ((ptr->flag & SLE_WAB_QUERY_STARTED) && + if (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) && !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) { - mDNS_StopGetDomains(m, &ptr->BrowseQ); - mDNS_StopGetDomains(m, &ptr->RegisterQ); - mDNS_StopGetDomains(m, &ptr->DefBrowseQ); - mDNS_StopGetDomains(m, &ptr->DefRegisterQ); - mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ); + LogInfo("uDNS_SetupWABQueries: Deleting Browse for domain %##s", ptr->domain.c); + ptr->flag &= ~SLE_WAB_BROWSE_QUERY_STARTED; + uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_BROWSE_QUERY); } - mDNSPlatformMemFree(ptr); + if (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + LogInfo("uDNS_SetupWABQueries: Deleting Legacy Browse for domain %##s", ptr->domain.c); + ptr->flag &= ~SLE_WAB_LBROWSE_QUERY_STARTED; + uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_LBROWSE_QUERY); + } - // deregister records generated from answers to the query - while (arList) + if (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED) && + !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) { - ARListElem *dereg = arList; - arList = arList->next; - debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c); - err = mDNS_Deregister(m, &dereg->ar); - if (err) LogMsg("uDNS_SetupSearchDomains:: ERROR!! mDNS_Deregister returned %d", err); - // Memory will be freed in the FreeARElemCallback + LogInfo("uDNS_SetupWABQueries: Deleting Registration for domain %##s", ptr->domain.c); + ptr->flag &= ~SLE_WAB_REG_QUERY_STARTED; + uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_REG_QUERY); } - continue; + + // Fall through to handle the ADDs } - if ((action & UDNS_START_WAB_QUERY) && !(ptr->flag & SLE_WAB_QUERY_STARTED)) + if ((action & UDNS_WAB_BROWSE_QUERY) && !(ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED)) { // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. // Also, suppress the domain enumeration for scoped search domains for now until there is a need. if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) { - mStatus err1, err2, err3, err4, err5; + mStatus err1, err2; err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err1) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeBrowse)\n", ptr->domain.c, err1); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Browse for domain %##s", ptr->domain.c); + } err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - err3 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); - if (err1 || err2 || err3 || err4 || err5) - LogMsg("uDNS_SetupSearchDomains: GetDomains for domain %##s returned error(s):\n" - "%d (mDNS_DomainTypeBrowse)\n" - "%d (mDNS_DomainTypeBrowseDefault)\n" - "%d (mDNS_DomainTypeRegistration)\n" - "%d (mDNS_DomainTypeRegistrationDefault)" + if (err2) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeBrowseDefault)\n", ptr->domain.c, err2); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Default Browse for domain %##s", ptr->domain.c); + } + // For simplicity, we mark a single bit for denoting that both the browse queries have started. + // It is not clear as to why one would fail to start and the other would succeed in starting up. + // If that happens, we will try to stop both the queries and one of them won't be in the list and + // it is not a hard error. + if (!err1 || !err2) + { + ptr->flag |= SLE_WAB_BROWSE_QUERY_STARTED; + } + } + } + if ((action & UDNS_WAB_LBROWSE_QUERY) && !(ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED)) + { + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. + // Also, suppress the domain enumeration for scoped search domains for now until there is a need. + if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + mStatus err1; + err1 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err1) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" "%d (mDNS_DomainTypeBrowseAutomatic)\n", - ptr->domain.c, err1, err2, err3, err4, err5); - ptr->flag |= SLE_WAB_QUERY_STARTED; + ptr->domain.c, err1); + } + else + { + ptr->flag |= SLE_WAB_LBROWSE_QUERY_STARTED; + LogInfo("uDNS_SetupWABQueries: Starting Legacy Browse for domain %##s", ptr->domain.c); + } + } + } + if ((action & UDNS_WAB_REG_QUERY) && !(ptr->flag & SLE_WAB_REG_QUERY_STARTED)) + { + // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries. + // Also, suppress the domain enumeration for scoped search domains for now until there is a need. + if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any)) + { + mStatus err1, err2; + err1 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err1) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeRegistration)\n", ptr->domain.c, err1); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Registration for domain %##s", ptr->domain.c); + } + err2 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr); + if (err2) + { + LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n" + "%d (mDNS_DomainTypeRegistrationDefault)", ptr->domain.c, err2); + } + else + { + LogInfo("uDNS_SetupWABQueries: Starting Default Registration for domain %##s", ptr->domain.c); + } + if (!err1 || !err2) + { + ptr->flag |= SLE_WAB_REG_QUERY_STARTED; + } } } p = &ptr->next; } - return mStatus_NoError; +} + +// mDNS_StartWABQueries is called once per API invocation where normally +// one of the bits is set. +mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType) +{ + if (queryType & UDNS_WAB_BROWSE_QUERY) + { + m->WABBrowseQueriesCount++; + LogInfo("uDNS_StartWABQueries: Browse query count %d", m->WABBrowseQueriesCount); + } + if (queryType & UDNS_WAB_LBROWSE_QUERY) + { + m->WABLBrowseQueriesCount++; + LogInfo("uDNS_StartWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount); + } + if (queryType & UDNS_WAB_REG_QUERY) + { + m->WABRegQueriesCount++; + LogInfo("uDNS_StartWABQueries: Reg query count %d", m->WABRegQueriesCount); + } + uDNS_SetupWABQueries(m); +} + +// mDNS_StopWABQueries is called once per API invocation where normally +// one of the bits is set. +mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType) +{ + if (queryType & UDNS_WAB_BROWSE_QUERY) + { + m->WABBrowseQueriesCount--; + LogInfo("uDNS_StopWABQueries: Browse query count %d", m->WABBrowseQueriesCount); + } + if (queryType & UDNS_WAB_LBROWSE_QUERY) + { + m->WABLBrowseQueriesCount--; + LogInfo("uDNS_StopWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount); + } + if (queryType & UDNS_WAB_REG_QUERY) + { + m->WABRegQueriesCount--; + LogInfo("uDNS_StopWABQueries: Reg query count %d", m->WABRegQueriesCount); + } + uDNS_SetupWABQueries(m); } mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal) @@ -4890,7 +5652,7 @@ mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID // Skip the domains that we already looked at before. Guard against "p" // being NULL. When search domains change we may not set the SearchListIndex // of the question to zero immediately e.g., domain enumeration query calls - // uDNS_SetupSearchDomain which reads in the new search domain but does not + // uDNS_SetupWABQueries which reads in the new search domain but does not // restart the questions immediately. Questions are restarted as part of // network change and hence temporarily SearchListIndex may be out of range. @@ -4957,12 +5719,29 @@ mDNSlocal void FlushAddressCacheRecords(mDNS *const m) // Retry questions which has seach domains appended mDNSexport void RetrySearchDomainQuestions(mDNS *const m) { + DNSQuestion *q; + mDNSBool found = mDNSfalse; + + // Check to see if there are any questions which needs search domains to be applied. + // If there is none, search domains can't possibly affect them. + for (q = m->Questions; q; q = q->next) + { + if (q->AppendSearchDomains) + { + found = mDNStrue; + break; + } + } + if (!found) + { + LogInfo("RetrySearchDomainQuestions: Questions with AppendSearchDomain not found"); + return; + } + LogInfo("RetrySearchDomainQuestions: Question with AppendSearchDomain found %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); // Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries // does this. When we restart the question, we first want to try the new search domains rather // than use the entries that is already in the cache. When we appended search domains, we might // have created cache entries which is no longer valid as there are new search domains now - - LogInfo("RetrySearchDomainQuestions: Calling mDNSCoreRestartAddressQueries"); mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL); } @@ -4984,3 +5763,233 @@ struct CompileTimeAssertionChecks_uDNS char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1]; char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; }; + +#else // !UNICAST_DISABLED + +mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) +{ + (void) m; + (void) rr; + + return mDNSNULL; +} + +mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name) +{ + (void) m; + (void) name; + + return mDNSNULL; +} + +mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) +{ + (void) m; + (void) q; + + return mDNSNULL; +} + +mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) +{ + (void) m; + (void) q; +} + +mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) +{ + (void) tcp; +} + +mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) +{ + (void) m; + (void) q; +} + +mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext) +{ + (void) m; + (void) name; + (void) target; + (void) callback; + (void) ZoneDataContext; + + return mDNSNULL; +} + +mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData) +{ + (void) m; + (void) err; + (void) zoneData; +} + +mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, + const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion) +{ + (void) m; + (void) msg; + (void) end; + (void) srcaddr; + (void) srcport; + (void) matchQuestion; + + return uDNS_LLQ_Not; +} + +mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags) +{ + (void) m; + (void) q; + (void) responseFlags; +} + +mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) +{ + (void) domain; + (void) InterfaceID; +} + +mDNSexport void RetrySearchDomainQuestions(mDNS *const m) +{ + (void) m; +} + +mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel) +{ + (void) m; + (void) info; + (void) domain; + (void) keyname; + (void) b64keydata; + (void) hostname; + (void) port; + (void) autoTunnel; + + return mStatus_UnsupportedErr; +} + +mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal) +{ + (void) m; + (void) InterfaceID; + (void) searchIndex; + (void) ignoreDotLocal; + + return mDNSNULL; +} + +mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name) +{ + (void) m; + (void) name; + + return mDNSNULL; +} + +mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal) +{ + (void) m; + (void) traversal; + + return mStatus_UnsupportedErr; +} + +mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr, + const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, + mDNSBool reqAAAA, mDNSBool reqDO) +{ + (void) m; + (void) d; + (void) interface; + (void) serviceID; + (void) addr; + (void) port; + (void) scoped; + (void) timeout; + (void) cellIntf; + (void) resGroupID; + (void) reqA; + (void) reqAAAA; + (void) reqDO; + + return mDNSNULL; +} + +mDNSexport void uDNS_SetupWABQueries(mDNS *const m) +{ + (void) m; +} + +mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType) +{ + (void) m; + (void) queryType; +} + +mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType) +{ + (void) m; + (void) queryType; +} + +mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) +{ + (void) m; + (void) fqdn; + (void) StatusCallback; + (void) StatusContext; +} +mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router) +{ + (void) m; + (void) v4addr; + (void) v6addr; + (void) router; +} + +mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) +{ + (void) m; + (void) fqdn; +} + +mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks) +{ + (void) m; + (void) waitTicks; +} + +mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q) +{ + (void)q; + + return mDNSfalse; +} + +#endif // !UNICAST_DISABLED diff --git a/mDNSCore/uDNS.h b/mDNSCore/uDNS.h index 02bdd1c..eca8b70 100755 --- a/mDNSCore/uDNS.h +++ b/mDNSCore/uDNS.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,8 +32,9 @@ extern "C" { //#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond) #define LLQ_POLL_INTERVAL (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc. #define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond) // require server responses within one minute of request -#define MAX_UCAST_UNANSWERED_QUERIES 2 // the number of unanswered queries from any one uDNS server before trying another server -#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server +#define MAX_DNSSEC_UNANSWERED_QUERIES 1 // number of unanswered queries from any one uDNS server before turning off DNSSEC Validation +#define MAX_UCAST_UNANSWERED_QUERIES 2 // number of unanswered queries from any one uDNS server before trying another server +#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server // On some interfaces, we want to delay the first retransmission to a minimum of 2 seconds // rather than the default (1 second). @@ -45,6 +46,10 @@ extern "C" { #define QuestionIntervalStep2 (QuestionIntervalStep*QuestionIntervalStep) #define QuestionIntervalStep3 (QuestionIntervalStep*QuestionIntervalStep*QuestionIntervalStep) #define InitialQuestionInterval ((mDNSPlatformOneSecond + QuestionIntervalStep-1) / QuestionIntervalStep) +#define MaxQuestionInterval (3600 * mDNSPlatformOneSecond) + +// just move to MaxQuestionInterval once over this threshold +#define QuestionIntervalThreshold (QuestionIntervalStep3 * mDNSPlatformOneSecond) // For Unicast record registrations, we initialize the interval to 1 second. When we send any query for // the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep @@ -66,6 +71,11 @@ extern "C" { // which accomodates two DNS servers and two queries per DNS server. #define DEFAULT_UDNSSEC_TIMEOUT 10 // in seconds +// If we are sending queries with EDNS0/DO option and we have no indications that the server +// is DNSSEC aware and we have already reached MAX_DNSSEC_RETRANSMISSIONS, we disable +// validation (for optional case only) for any questions that uses this server +#define MAX_DNSSEC_RETRANSMISSIONS 3 + // Entry points into unicast-specific routines extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo); @@ -104,14 +114,16 @@ extern void CheckNATMappings(mDNS *m); extern mStatus uDNS_SetupDNSConfig(mDNS *const m); -// uDNS_SetupSearchDomains by default adds search domains. It also can be called with one or -// more values for "action" which does the following: -// -// -UDNS_START_WAB_QUERY - start Wide Area Bonjour (domain enumeration) queries +// uDNS_SetupWABQueries reads search domains from the platform layer and starts the Wide Area Bonjour +// (WAB) domain enumeration queries if necessary. -#define UDNS_START_WAB_QUERY 0x00000001 +#define UDNS_WAB_BROWSE_QUERY 0x00000001 // Browse queries (b, db) +#define UDNS_WAB_LBROWSE_QUERY 0x00000002 // Browse queries (lb) +#define UDNS_WAB_REG_QUERY 0x00000004 // Registration queries (r and dr) -extern mStatus uDNS_SetupSearchDomains(mDNS *const m, int action); +extern void uDNS_SetupWABQueries(mDNS *const m); +extern void uDNS_StartWABQueries(mDNS *const m, int queryType); +extern void uDNS_StopWABQueries(mDNS *const m, int queryType); extern domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal); typedef enum @@ -128,9 +140,9 @@ extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const extern void DisposeTCPConn(struct tcpInfo_t *tcp); // NAT traversal -extern void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received NAT-PMP packet +extern void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len); // Called for each received PCP or NAT-PMP packet extern void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr); -extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease); +extern void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease, NATTProtocol protocol); #ifdef __cplusplus } diff --git a/mDNSMacOSX/BonjourEvents.c b/mDNSMacOSX/BonjourEvents.c index f705a09..b930818 100644 --- a/mDNSMacOSX/BonjourEvents.c +++ b/mDNSMacOSX/BonjourEvents.c @@ -17,7 +17,7 @@ #include #include -#include +#include "dns_sd.h" #include #include #include @@ -855,8 +855,11 @@ NetBrowserInfo* NetBrowserInfoCreate(CFStringRef serviceType, CFStringRef domain if (domain) { CFIndex domainSize = CFStringGetMaximumSizeForEncoding(CFStringGetLength(domain), kCFStringEncodingUTF8); - cDomain = calloc(serviceSize, 1); - success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8); + if (domainSize) + { + cDomain = calloc(domainSize, 1); + success = success && CFStringGetCString(domain, cDomain, domainSize, kCFStringEncodingUTF8); + } } if (!success) diff --git a/mDNSMacOSX/CUPolicy.c b/mDNSMacOSX/CUPolicy.c new file mode 100644 index 0000000..434e65c --- /dev/null +++ b/mDNSMacOSX/CUPolicy.c @@ -0,0 +1,91 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2013 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mDNSMacOSX.h" +#include + +#if TARGET_OS_IPHONE + +mDNSexport void CUPInit(mDNS *const m) +{ + + m->p->handle = cellular_usage_policy_create_client(); + if (!m->p->handle) + { + LogMsg("CUPInit: cellular_usage_policy_create_client failed"); + } +} + +mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) +{ + // Currently the policy applies only for DNS requests sent over cellular interface + if (m->p->handle && q->qDNSServer && q->qDNSServer->cellIntf) + { + mDNSBool allowed; + if (q->pid) + { + allowed = (mDNSBool) cellular_usage_policy_is_data_allowed_for_pid(m->p->handle, q->pid); + if (!allowed) + { + xpc_object_t pidx = xpc_uint64_create(q->pid); + if (pidx) + { + network_config_cellular_blocked_notify(pidx, NULL, NULL); + LogInfo("mDNSPlaformAllowPID: Notified PID(%d) for %##s (%s)", q->pid, q->qname.c, DNSTypeName(q->qtype)); + xpc_release(pidx); + } + } + } + else + { + allowed = (mDNSBool) cellular_usage_policy_is_data_allowed_for_uuid(m->p->handle, q->uuid); + if (!allowed) + { + xpc_object_t uuidx = xpc_uuid_create(q->uuid); + if (uuidx) + { + network_config_cellular_blocked_notify(NULL, uuidx, NULL); + LogInfo("mDNSPlaformAllowPID: Notified UUID for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + xpc_release(uuidx); + } + } + } + return allowed; + } + else + { + return mDNStrue; + } +} + +#else // TARGET_OS_IPHONE + +mDNSexport void CUPInit(mDNS *const m) +{ + (void)m; //unused +} + +mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) +{ + (void)m; //unused + (void)q; //unused + //LogMsg("mDNSPlatformAllowPID: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNStrue; +} + +#endif // TARGET_OS_IPHONE + diff --git a/mDNSMacOSX/CryptoSupport.c b/mDNSMacOSX/CryptoSupport.c index 4223ca2..408b3a2 100644 --- a/mDNSMacOSX/CryptoSupport.c +++ b/mDNSMacOSX/CryptoSupport.c @@ -27,18 +27,23 @@ #include "CryptoAlg.h" #include "CryptoSupport.h" #include "dnssec.h" +#include "DNSSECSupport.h" #if TARGET_OS_IPHONE #include "SecRSAKey.h" // For RSA_SHA1 etc. verification +#else +#include +#endif + +#if !TARGET_OS_IPHONE +mDNSlocal SecKeyRef SecKeyCreateRSAPublicKey_OSX(unsigned char *asn1, int length); #endif typedef struct { -#if DISPATCH_API_VERSION >= 20111008 dispatch_data_t encData; dispatch_data_t encMap; dispatch_data_t encNULL; -#endif }encContext; mDNSlocal mStatus enc_create(AlgContext *ctx) @@ -56,7 +61,6 @@ mDNSlocal mStatus enc_create(AlgContext *ctx) LogMsg("enc_create: Unsupported algorithm %d", ctx->alg); return mStatus_BadParamErr; } -#if DISPATCH_API_VERSION >= 20111008 ptr->encData = NULL; ptr->encMap = NULL; // The encoded data is not NULL terminated. So, we concatenate a null byte later when we encode and map @@ -67,7 +71,6 @@ mDNSlocal mStatus enc_create(AlgContext *ctx) mDNSPlatformMemFree(ptr); return mStatus_NoMemoryErr; } -#endif ctx->context = ptr; return mStatus_NoError; } @@ -75,23 +78,20 @@ mDNSlocal mStatus enc_create(AlgContext *ctx) mDNSlocal mStatus enc_destroy(AlgContext *ctx) { encContext *ptr = (encContext *)ctx->context; -#if DISPATCH_API_VERSION >= 20111008 if (ptr->encData) dispatch_release(ptr->encData); if (ptr->encMap) dispatch_release(ptr->encMap); if (ptr->encNULL) dispatch_release(ptr->encNULL); -#endif mDNSPlatformMemFree(ptr); return mStatus_NoError; } -mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len) +mDNSlocal mStatus enc_add(AlgContext *ctx, const void *data, mDNSu32 len) { switch (ctx->alg) { case ENC_BASE32: case ENC_BASE64: { -#if DISPATCH_API_VERSION >= 20111008 encContext *ptr = (encContext *)ctx->context; dispatch_data_t src_data = dispatch_data_create(data, len, dispatch_get_global_queue(0, 0), ^{}); if (!src_data) @@ -100,7 +100,7 @@ mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len) return mStatus_BadParamErr; } dispatch_data_t dest_data = dispatch_data_create_with_transform(src_data, DISPATCH_DATA_FORMAT_TYPE_NONE, - (ctx->alg == ENC_BASE32 ? DISPATCH_DATA_FORMAT_TYPE_BASE32 : DISPATCH_DATA_FORMAT_TYPE_BASE64)); + (ctx->alg == ENC_BASE32 ? DISPATCH_DATA_FORMAT_TYPE_BASE32HEX : DISPATCH_DATA_FORMAT_TYPE_BASE64)); dispatch_release(src_data); if (!dest_data) { @@ -108,10 +108,7 @@ mDNSlocal mStatus enc_add(AlgContext *ctx, void *data, mDNSu32 len) return mStatus_BadParamErr; } ptr->encData = dest_data; -#else - (void)data; - (void)len; -#endif + return mStatus_NoError; } default: @@ -129,7 +126,6 @@ mDNSlocal mDNSu8* enc_encode(AlgContext *ctx) case ENC_BASE32: case ENC_BASE64: { -#if DISPATCH_API_VERSION >= 20111008 encContext *ptr = (encContext *)ctx->context; size_t size; dispatch_data_t dest_data = ptr->encData; @@ -150,7 +146,7 @@ mDNSlocal mDNSu8* enc_encode(AlgContext *ctx) dispatch_release(dest_data); ptr->encData = data; ptr->encMap = map; -#endif + return (mDNSu8 *)result; } default: @@ -202,7 +198,7 @@ mDNSlocal mDNSu32 sha_len(AlgContext *ctx) } } -mDNSlocal mStatus sha_add(AlgContext *ctx, void *data, mDNSu32 len) +mDNSlocal mStatus sha_add(AlgContext *ctx, const void *data, mDNSu32 len) { switch (ctx->alg) { @@ -251,6 +247,34 @@ mDNSlocal mStatus sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu return mStatus_NoAuth; } +mDNSlocal mStatus sha_final(AlgContext *ctx, void *digestOut, mDNSu32 dlen) +{ + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + mDNSu32 digestLen; + + switch (ctx->alg) + { + case SHA1_DIGEST_TYPE: + digestLen = CC_SHA1_DIGEST_LENGTH; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case SHA256_DIGEST_TYPE: + digestLen = CC_SHA256_DIGEST_LENGTH; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + default: + LogMsg("sha_final: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + if (dlen != digestLen) + { + LogMsg("sha_final(Alg %d): digest len mismatch len %u, expected %u", ctx->alg, (unsigned int)dlen, (unsigned int)digestLen); + return mStatus_BadParamErr; + } + memcpy(digestOut, digest, digestLen); + return mStatus_NoError; +} + mDNSlocal mStatus rsa_sha_create(AlgContext *ctx) { mDNSu8 *ptr; @@ -303,7 +327,7 @@ mDNSlocal mDNSu32 rsa_sha_len(AlgContext *ctx) } } -mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, void *data, mDNSu32 len) +mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, const void *data, mDNSu32 len) { switch (ctx->alg) { @@ -324,7 +348,6 @@ mDNSlocal mStatus rsa_sha_add(AlgContext *ctx, void *data, mDNSu32 len) return mStatus_NoError; } -#if TARGET_OS_IPHONE mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) { static const int max_key_bytes = 4096 / 8; // max DNSSEC supported modulus is 4096 bits @@ -427,29 +450,38 @@ mDNSlocal SecKeyRef rfc3110_import(const mDNSu8 *data, const mDNSu32 len) for (i = 1; i <= exp_length; i++) asn1[index++] = data[i]; +#if TARGET_OS_IPHONE // index contains bytes written, use it for length - return SecKeyCreateRSAPublicKey(NULL, asn1, index, kSecKeyEncodingPkcs1); + return (SecKeyCreateRSAPublicKey(NULL, asn1, index, kSecKeyEncodingPkcs1)); +#else + return (SecKeyCreateRSAPublicKey_OSX(asn1, index)); +#endif } +#if TARGET_OS_IPHONE mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) { SecKeyRef keyref; OSStatus result; mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; int digestlen; + int cryptoAlg; switch (ctx->alg) { case CRYPTO_RSA_NSEC3_SHA1: case CRYPTO_RSA_SHA1: + cryptoAlg = kSecPaddingPKCS1SHA1; digestlen = CC_SHA1_DIGEST_LENGTH; CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); break; case CRYPTO_RSA_SHA256: + cryptoAlg = kSecPaddingPKCS1SHA256; digestlen = CC_SHA256_DIGEST_LENGTH; CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); break; case CRYPTO_RSA_SHA512: + cryptoAlg = kSecPaddingPKCS1SHA512; digestlen = CC_SHA512_DIGEST_LENGTH; CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context); break; @@ -459,34 +491,204 @@ mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, m } keyref = rfc3110_import(key, keylen); - if (!key) + if (!keyref) { LogMsg("rsa_sha_verify: Error decoding rfc3110 key data"); return mStatus_NoMemoryErr; } - // TBD: Use the right algorithm when the support becomes available - result = SecKeyRawVerify(keyref, kSecPaddingPKCS1SHA1, digest, digestlen, signature, siglen); - LogMsg("rsa_sha_verify: result: %s (%ld)", result == noErr ? "PASS" : "FAIL", result); - if (result == noErr) - return mStatus_NoError; - else + result = SecKeyRawVerify(keyref, cryptoAlg, digest, digestlen, signature, siglen); + CFRelease(keyref); + if (result != noErr) + { + LogMsg("rsa_sha_verify: Failed for alg %d", ctx->alg); return mStatus_BadParamErr; + } + else + { + LogInfo("rsa_sha_verify: Passed for alg %d", ctx->alg); + return mStatus_NoError; + } } -#else +#else // TARGET_OS_IPHONE + +mDNSlocal SecKeyRef SecKeyCreateRSAPublicKey_OSX(unsigned char *asn1, int length) +{ + SecKeyRef result = NULL; + + SecExternalFormat extFormat = kSecFormatBSAFE; + SecExternalItemType itemType = kSecItemTypePublicKey; + CFArrayRef outArray = NULL; + + CFDataRef keyData = CFDataCreate(NULL, asn1, length); + if (!keyData) + return NULL; + + OSStatus err = SecItemImport(keyData, NULL, &extFormat, &itemType, 0, NULL, NULL, &outArray); + + CFRelease(keyData); + if (noErr != err || outArray == NULL) + { + if (outArray) + CFRelease(outArray); + return NULL; + } + + result = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0); + if (result == NULL) + { + CFRelease(outArray); + return NULL; + } + + CFRetain(result); + CFRelease(outArray); + return result; +} + +mDNSlocal Boolean VerifyData(SecKeyRef key, CFStringRef digestStr, mDNSu8 *digest, int dlen, int digestlenAttr, mDNSu8 *sig, int siglen, CFStringRef digest_type) +{ + CFErrorRef error; + Boolean ret; + + CFDataRef signature = CFDataCreate(NULL, sig, siglen); + if (!signature) + return false; + + SecTransformRef verifyXForm = SecVerifyTransformCreate(key, signature, &error); + CFRelease(signature); + if (verifyXForm == NULL) + { + return false; + } + + // tell the transform what type of data it is geting + if (!SecTransformSetAttribute(verifyXForm, kSecInputIsAttributeName, digest_type, &error)) + { + LogMsg("VerifyData: SecTransformSetAttribute digest_type"); + goto err; + } + + if (!SecTransformSetAttribute(verifyXForm, kSecDigestTypeAttribute, digestStr, &error)) + { + LogMsg("VerifyData: SecTransformSetAttribute digestStr"); + goto err; + } + + CFNumberRef digestLengthRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &digestlenAttr); + if (digestLengthRef == NULL) + { + LogMsg("VerifyData: CFNumberCreate failed"); + goto err; + } + + ret = SecTransformSetAttribute(verifyXForm, kSecDigestLengthAttribute, digestLengthRef, &error); + CFRelease(digestLengthRef); + if (!ret) + { + LogMsg("VerifyData: SecTransformSetAttribute digestLengthRef"); + goto err; + } + + CFDataRef dataToSign = CFDataCreate(NULL, digest, dlen); + if (dataToSign == NULL) + { + LogMsg("VerifyData: CFDataCreate failed"); + goto err; + } + + ret = SecTransformSetAttribute(verifyXForm, kSecTransformInputAttributeName, dataToSign, &error); + CFRelease(dataToSign); + if (!ret) + { + LogMsg("VerifyData: SecTransformSetAttribute TransformAttributeName"); + goto err; + } + + CFBooleanRef boolRef = SecTransformExecute(verifyXForm, &error); + CFRelease(verifyXForm); + + if (error != NULL) + { + CFStringRef errStr = CFErrorCopyDescription(error); + char errorbuf[128]; + errorbuf[0] = 0; + if (errStr != NULL) + { + if (!CFStringGetCString(errStr, errorbuf, sizeof(errorbuf), kCFStringEncodingUTF8)) + { + LogMsg("VerifyData: CFStringGetCString failed"); + } + } + LogMsg("VerifyData: SecTransformExecute failed with %s", errorbuf); + return false; + } + return CFEqual(boolRef, kCFBooleanTrue); +err: + CFRelease(verifyXForm); + return false; +} + mDNSlocal mStatus rsa_sha_verify(AlgContext *ctx, mDNSu8 *key, mDNSu32 keylen, mDNSu8 *signature, mDNSu32 siglen) { - (void)ctx; - (void)key; - (void)keylen; - (void)signature; - (void)siglen; - return mStatus_NoError; + SecKeyRef keyref; + mDNSu8 digest[CC_SHA512_DIGEST_LENGTH]; + int digestlen; + int digestlenAttr; + CFStringRef digestStr; + mDNSBool ret; + + switch (ctx->alg) + { + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + digestStr = kSecDigestSHA1; + digestlen = CC_SHA1_DIGEST_LENGTH; + digestlenAttr = 0; + CC_SHA1_Final(digest, (CC_SHA1_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA256: + digestStr = kSecDigestSHA2; + digestlen = CC_SHA256_DIGEST_LENGTH; + digestlenAttr = 256; + CC_SHA256_Final(digest, (CC_SHA256_CTX *)ctx->context); + break; + case CRYPTO_RSA_SHA512: + digestStr = kSecDigestSHA2; + digestlen = CC_SHA512_DIGEST_LENGTH; + digestlenAttr = 512; + CC_SHA512_Final(digest, (CC_SHA512_CTX *)ctx->context); + break; + default: + LogMsg("rsa_sha_verify: Unsupported algorithm %d", ctx->alg); + return mStatus_BadParamErr; + } + + keyref = rfc3110_import(key, keylen); + if (!keyref) + { + LogMsg("rsa_sha_verify: Error decoding rfc3110 key data"); + return mStatus_NoMemoryErr; + } + ret = VerifyData(keyref, digestStr, digest, digestlen, digestlenAttr, signature, siglen, kSecInputIsDigest); + CFRelease(keyref); + if (!ret) + { + LogMsg("rsa_sha_verify: Failed for alg %d", ctx->alg); + return mStatus_BadParamErr; + } + else + { + LogInfo("rsa_sha_verify: Passed for alg %d", ctx->alg); + return mStatus_NoError; + } } -#endif +#endif // TARGET_OS_IPHONE + +AlgFuncs sha_funcs = {sha_create, sha_destroy, sha_len, sha_add, sha_verify, mDNSNULL, sha_final}; +AlgFuncs rsa_sha_funcs = {rsa_sha_create, rsa_sha_destroy, rsa_sha_len, rsa_sha_add, rsa_sha_verify, mDNSNULL, mDNSNULL}; +AlgFuncs enc_funcs = {enc_create, enc_destroy, mDNSNULL, enc_add, mDNSNULL, enc_encode, mDNSNULL}; -AlgFuncs sha_funcs = {sha_create, sha_destroy, sha_len, sha_add, sha_verify, mDNSNULL}; -AlgFuncs rsa_sha_funcs = {rsa_sha_create, rsa_sha_destroy, rsa_sha_len, rsa_sha_add, rsa_sha_verify, mDNSNULL}; -AlgFuncs enc_funcs = {enc_create, enc_destroy, mDNSNULL, enc_add, mDNSNULL, enc_encode}; +#ifndef DNSSEC_DISABLED mDNSexport mStatus DNSSECCryptoInit(mDNS *const m) { @@ -517,8 +719,20 @@ mDNSexport mStatus DNSSECCryptoInit(mDNS *const m) if (result != mStatus_NoError) return result; - m->TrustAnchors = mDNSNULL; - m->notifyToken = 0; + result = DNSSECPlatformInit(m); + + return result; +} + +#else // !DNSSEC_DISABLED + +mDNSexport mStatus DNSSECCryptoInit(mDNS *const m) +{ + (void) m; return mStatus_NoError; } + +#endif // !DNSSEC_DISABLED + + diff --git a/mDNSMacOSX/DNSProxySupport.c b/mDNSMacOSX/DNSProxySupport.c new file mode 100644 index 0000000..042bbc8 --- /dev/null +++ b/mDNSMacOSX/DNSProxySupport.c @@ -0,0 +1,543 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2011 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mDNSEmbeddedAPI.h" +#include "mDNSMacOSX.h" + +#include +#include +#include + +#define ValidSocket(s) ((s) >= 0) + +// Global to store the 4 DNS Proxy Listeners (UDPv4/6, TCPv4/6) +static int dp_listener[4]; + +#define NUM_PROXY_TCP_CONNS 100 + +typedef struct +{ + TCPSocket sock; + DNSMessage *reply; + mDNSu16 replyLen; + mDNSu32 nread; +} ProxyTCPInfo_t; + +// returns -1 for failures including the other end closing the socket +// returns 0 if successful in reading data, but still not read the data fully +// returns 1 if successful in reading all the data +mDNSlocal int ProxyTCPRead(ProxyTCPInfo_t *tcpInfo) +{ + long n; + mDNSBool closed; + + if (tcpInfo->nread < 2) // First read the two-byte length preceeding the DNS message + { + mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replyLen; + n = mDNSPlatformReadTCP(&tcpInfo->sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed); + if (n < 0 || closed) + { + LogMsg("ProxyTCPRead: attempt to read message length failed"); + return -1; + } + + tcpInfo->nread += n; + if (tcpInfo->nread < 2) + { + LogMsg("ProxyTCPRead: nread %d, n %d", tcpInfo->nread, n); + return 0; + } + + tcpInfo->replyLen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]); + if (tcpInfo->replyLen < sizeof(DNSMessageHeader)) + { + LogMsg("ProxyTCPRead: Message length too short (%d bytes)", tcpInfo->replyLen); + return -1; + } + + tcpInfo->reply = mallocL("ProxyTCPInfo", tcpInfo->replyLen); + if (!tcpInfo->reply) + { + LogMsg("ProxyTCPRead: Memory failure"); + return -1; + } + } + + n = mDNSPlatformReadTCP(&tcpInfo->sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replyLen - (tcpInfo->nread - 2), &closed); + + if (n < 0 || closed) + { + LogMsg("ProxyTCPRead: read failure n %d, closed %d", n, closed); + return -1; + } + tcpInfo->nread += n; + if ((tcpInfo->nread - 2) != tcpInfo->replyLen) + return 0; + else + return 1; +} + +mDNSlocal void ProxyTCPSocketCallBack(int s1, short filter, void *context) +{ + int ret; + struct sockaddr_storage from; + struct sockaddr_storage to; + mDNSAddr senderAddr, destAddr; + mDNSIPPort senderPort; + ProxyTCPInfo_t *ti = (ProxyTCPInfo_t *)context; + TCPSocket *sock = &ti->sock; + KQSocketSet *kq = &sock->ss; + + (void) filter; + + ret = ProxyTCPRead(ti); + if (ret == -1) + { + mDNSPlatformDisposeProxyContext(ti); + return; + } + else if (!ret) + { + debugf("ProxyTCPReceive: Not yet read completely Actual length %d, Read length %d", ti->replyLen, ti->nread); + return; + } + // We read all the data and hence not interested in read events anymore + KQueueSet(s1, EV_DELETE, EVFILT_READ, sock->kqEntry); + + mDNSPlatformMemZero(&to, sizeof(to)); + mDNSPlatformMemZero(&from, sizeof(from)); + socklen_t len = sizeof(to); + ret = getsockname(s1, (struct sockaddr*) &to, &len); + if (ret < 0) + { + LogMsg("ProxyTCPReceive: getsockname(fd=%d) errno %d", s1, errno); + mDNSPlatformDisposeProxyContext(ti); + return; + } + ret = getpeername(s1, (struct sockaddr*) &from, &len); + if (ret < 0) + { + LogMsg("ProxyTCPReceive: getpeername(fd=%d) errno %d", s1, errno); + mDNSPlatformDisposeProxyContext(ti); + return; + } + + if (from.ss_family == AF_INET) + { + struct sockaddr_in *s = (struct sockaddr_in*)&from; + + senderAddr.type = mDNSAddrType_IPv4; + senderAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; + senderPort.NotAnInteger = s->sin_port; + + s = (struct sockaddr_in *)&to; + destAddr.type = mDNSAddrType_IPv4; + destAddr.ip.v4.NotAnInteger = s->sin_addr.s_addr; + + LogInfo("ProxyTCPReceive received IPv4 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL); + } + else if (from.ss_family == AF_INET6) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&from; + senderAddr.type = mDNSAddrType_IPv6; + senderAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + senderPort.NotAnInteger = sin6->sin6_port; + + sin6 = (struct sockaddr_in6 *)&to; + destAddr.type = mDNSAddrType_IPv6; + destAddr.ip.v6 = *(mDNSv6Addr*)&sin6->sin6_addr; + + LogInfo("ProxyTCPReceive received IPv6 packet(len %d) from %#-15a to %#-15a on skt %d %s", ti->replyLen, &senderAddr, &destAddr, s1, NULL); + } + else + { + LogMsg("ProxyTCPReceive from is unknown address family %d", from.ss_family); + mDNSPlatformDisposeProxyContext(ti); + return; + } + + // We pass sock for the TCPSocket and the "ti" for context as that's what we want to free at the end. + // In the UDP case, there is just a single socket and nothing to free. Hence, the context (last argument) + // would be NULL. + kq->m->p->TCPProxyCallback(kq->m, sock, ti->reply, (mDNSu8 *)ti->reply + ti->replyLen, &senderAddr, senderPort, &destAddr, + UnicastDNSPort, 0, ti); +} + +mDNSlocal void ProxyTCPAccept(int s1, short filter, void *context) +{ + int newfd; + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + const int on = 1; + KQSocketSet *listenSet = (KQSocketSet *)context; + + (void) filter; + + while ((newfd = accept(s1, (struct sockaddr *)&ss, &sslen)) != -1) + { + int err; + int *s; + KQueueEntry *k; + KQSocketSet *kq; + + // Even though we just need a single KQueueEntry, for simplicity we re-use + // the KQSocketSet + ProxyTCPInfo_t *ti = mallocL("ProxyTCPContext", sizeof(ProxyTCPInfo_t)); + if (!ti) + { + LogMsg("ProxyTCPAccept: cannot allocate TCPSocket"); + close(newfd); + return; + } + mDNSPlatformMemZero(ti, sizeof(ProxyTCPInfo_t)); + TCPSocket *sock = &ti->sock; + + kq = &sock->ss; + kq->sktv4 = -1; + kq->sktv6 = -1; + kq->m = listenSet->m; + + fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + if (ss.ss_family == AF_INET) + { + s = &kq->sktv4; + k = &kq->kqsv4; + // Receive interface identifiers + err = setsockopt(newfd, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err) + { + LogMsg("ProxyTCPAccept: IP_RECVIF %d errno %d (%s)", newfd, errno, strerror(errno)); + mDNSPlatformDisposeProxyContext(ti); + return; + } + } + else + { + s = &kq->sktv6; + k = &kq->kqsv6; + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(newfd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (err) + { + LogMsg("ProxyTCPAccept: IP_RECVPKTINFO %d errno %d (%s)", newfd, errno, strerror(errno)); + mDNSPlatformDisposeProxyContext(ti); + return; + } + } + *s = newfd; + // mDNSPlatformReadTCP/WriteTCP (unlike the UDP counterpart) does not provide the destination address + // from which we can infer the destination address family. Hence we need to remember that here. + // Instead of remembering the address family, we remember the right fd. + sock->fd = newfd; + sock->kqEntry = k; + + k->KQcallback = ProxyTCPSocketCallBack; + k->KQcontext = ti; + k->KQtask = "TCP Proxy packet reception"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + } +} + +mDNSlocal mStatus SetupUDPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass) +{ + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; + const int on = 1; + mDNSIPPort port; + mStatus err = mStatus_NoError; + + cp->m = m; + port = cp->port; + cp->closeFlag = mDNSNULL; + + // set default traffic class + // setTrafficClass(skt, mDNSfalse); + (void) useBackgroundTrafficClass; + + if (sa_family == AF_INET) + { + err = setsockopt(skt, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IP_RECVDSTADDR %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + + // We want to receive interface identifiers + err = setsockopt(skt, IPPROTO_IP, IP_RECVIF, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IP_RECVIF %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + } + else if (sa_family == AF_INET6) + { + // We want to receive destination addresses and receive interface identifiers + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IPV6_RECVPKTINFO %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + + // We want to receive packet hop count value so we can check it + err = setsockopt(skt, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); + if (err < 0) + { + LogMsg("SetupUDPProxySocket: IPV6_RECVHOPLIMIT %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + } + else + { + LogMsg("SetupUDPProxySocket: wrong family %d", sa_family); + return -1; + } + + if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) < 0) + { + LogMsg("SetupUDPProxySocket: fnctl failed %d", errno); + return -1; + } + + *s = skt; + //k->KQcallback = ProxyUDPSocketCallBack; + k->KQcallback = myKQSocketCallBack; + k->KQcontext = cp; + k->KQtask = "UDP Proxy packet reception"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + + return(err); +} + +mDNSlocal mStatus SetupTCPProxySocket(mDNS *const m, int skt, KQSocketSet *cp, u_short sa_family, mDNSBool useBackgroundTrafficClass) +{ + int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; + KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; + mDNSIPPort port; + mStatus err; + + cp->m = m; + port = cp->port; + // XXX may not be used by the TCP codepath + cp->closeFlag = mDNSNULL; + + // for TCP sockets, the traffic class is set once and not changed + // setTrafficClass(skt, useBackgroundTrafficClass); + (void) useBackgroundTrafficClass; + + // All the socket setup has already been done + err = listen(skt, NUM_PROXY_TCP_CONNS); + if (err) + { + LogMsg("SetupTCPProxySocket: listen %d errno %d (%s)", skt, errno, strerror(errno)); + return err; + } + fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking + + *s = skt; + k->KQcallback = ProxyTCPAccept; + k->KQcontext = cp; + k->KQtask = "TCP Accept"; +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + k->readSource = mDNSNULL; + k->writeSource = mDNSNULL; + k->fdClosed = mDNSfalse; +#endif + KQueueSet(*s, EV_ADD, EVFILT_READ, k); + return mStatus_NoError; +} + +mDNSlocal void BindDPSocket(int fd, int sa_family) +{ + int err; + const int on = 1; + + if (sa_family == AF_INET) + { + struct sockaddr_in addr; + + err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) + LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for V4 %d errno %d (%s)", fd, errno, strerror(errno)); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(53); + + err = bind(fd, (struct sockaddr*) &addr, sizeof(addr)); + if (err) + { + LogMsg("BindDPSocket: bind %d errno %d (%s)", fd, errno, strerror(errno)); + return; + } + } + else + { + struct sockaddr_in6 addr6; + + // We want to receive only IPv6 packets. Without this option we get IPv4 packets too, + // with mapped addresses of the form 0:0:0:0:0:FFFF:xxxx:xxxx, where xxxx:xxxx is the IPv4 address + err = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (err < 0) + { + LogMsg("DPFBindSocket: setsockopt IPV6_V6ONLY %d errno %d (%s)", fd, errno, strerror(errno)); + return; + } + err = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); + if (err < 0) + LogMsg("BindDPSocket: setsockopt SO_REUSEPORT failed for V6 %d errno %d (%s)", fd, errno, strerror(errno)); + + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(53); + + err = bind(fd, (struct sockaddr*) &addr6, sizeof(addr6)); + if (err) + { + LogMsg("BindDPSocket: bind6 %d errno %d (%s)", fd, errno, strerror(errno)); + return; + } + } +} + +// Setup DNS Proxy Skts in main kevent loop and set the skt options +mDNSlocal void SetupDNSProxySkts(mDNS *const m, int fd[4]) +{ + int i; + mStatus err; + KQSocketSet *udpSS; + KQSocketSet *tcpSS; + + udpSS = &m->p->UDPProxy.ss; + tcpSS = &m->p->TCPProxy.ss; + udpSS->port = UnicastDNSPort; + tcpSS->port = UnicastDNSPort; + + LogMsg("SetupDNSProxySkts: %d, %d, %d, %d", fd[0], fd[1], fd[2], fd[3]); + + // myKQSocketCallBack checks for proxy and calls the m->p->ProxyCallback instead of mDNSCoreReceive + udpSS->proxy = mDNStrue; + err = SetupUDPProxySocket(m, fd[0], udpSS, AF_INET, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! UDPv4 Socket"); + + err = SetupUDPProxySocket(m, fd[1], udpSS, AF_INET6, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! UDPv6 Socket"); + + err = SetupTCPProxySocket(m, fd[2], tcpSS, AF_INET, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! TCPv4 Socket"); + + err = SetupTCPProxySocket(m, fd[3], tcpSS, AF_INET6, mDNSfalse); + if (err) + LogMsg("SetupDNSProxySkts: ERROR!! TCPv6 Socket"); + + for (i = 0; i < 4; i++) + dp_listener[i] = fd[i]; +} + +// Create and bind the DNS Proxy Skts for use +mDNSexport void mDNSPlatformInitDNSProxySkts(mDNS *const m, ProxyCallback UDPCallback, ProxyCallback TCPCallback) +{ + int dpskt[4]; + + dpskt[0] = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + dpskt[1] = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + dpskt[2] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + dpskt[3] = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); + + // Close all DNS Proxy skts in case any of them are invalid + if (!ValidSocket(dpskt[0]) || !ValidSocket(dpskt[1]) || + !ValidSocket(dpskt[2]) || !ValidSocket(dpskt[3])) + { + if (ValidSocket(dpskt[0])) + close(dpskt[0]); + if (ValidSocket(dpskt[1])) + close(dpskt[1]); + if (ValidSocket(dpskt[2])) + close(dpskt[2]); + if (ValidSocket(dpskt[3])) + close(dpskt[3]); + } + + BindDPSocket(dpskt[0], AF_INET); + BindDPSocket(dpskt[1], AF_INET6); + BindDPSocket(dpskt[2], AF_INET); + BindDPSocket(dpskt[3], AF_INET6); + + LogInfo("mDNSPlatformInitDNSProxySkts: Opened Listener Sockets for DNS Proxy : %d, %d, %d, %d", + dpskt[0], dpskt[1], dpskt[2], dpskt[3]); + + m->p->UDPProxyCallback = UDPCallback; + m->p->TCPProxyCallback = TCPCallback; + + SetupDNSProxySkts(m, dpskt); +} + +mDNSexport void mDNSPlatformCloseDNSProxySkts(mDNS *const m) +{ + (void) m; + int i; + for (i = 0; i < 4; i++) + close(dp_listener[i]); + LogInfo("mDNSPlatformCloseDNSProxySkts: Closing DNS Proxy Listener Sockets"); +} + +mDNSexport void mDNSPlatformDisposeProxyContext(void *context) +{ + ProxyTCPInfo_t *ti; + TCPSocket *sock; + KQSocketSet *kq; + + if (!context) + return; + + ti = (ProxyTCPInfo_t *)context; + sock = &ti->sock; + + kq = &sock->ss; + if (kq->sktv4 != -1) + { + shutdown(kq->sktv4, 2); + mDNSPlatformCloseFD(&kq->kqsv4, kq->sktv4); + } + if (kq->sktv6 != -1) + { + shutdown(kq->sktv6, 2); + mDNSPlatformCloseFD(&kq->kqsv6, kq->sktv6); + } + if (kq->closeFlag) + *kq->closeFlag = 1; + + if (ti->reply) + freeL("ProxyTCPInfoLen", ti->reply); + freeL("ProxyTCPContext", ti); +} + diff --git a/mDNSMacOSX/DNSSECSupport.c b/mDNSMacOSX/DNSSECSupport.c new file mode 100644 index 0000000..e1255c4 --- /dev/null +++ b/mDNSMacOSX/DNSSECSupport.c @@ -0,0 +1,645 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// *************************************************************************** +// DNSSECSupport.c: Platform specific support for DNSSEC like fetching root +// trust anchor and dnssec probes etc. +// *************************************************************************** + +#include "mDNSEmbeddedAPI.h" +#include "DNSCommon.h" // For mDNS_Lock, mDNS_Random +#include "dnssec.h" +#include "DNSSECSupport.h" + +#include // For Hash algorithms SHA1 etc. + +// Following are needed for fetching the root trust anchor dynamically +#include +#include +#include +#include +#include + +// 30 days +#define ROOT_TA_UPDATE_INTERVAL (30 * 24 * 3600) // seconds + +// After 100 days, the test anchors are not valid. Just an arbitrary number +// to configure validUntil. +#define TEST_TA_EXPIRE_INTERVAL (100 * 24 * 4600) + +// When we can't fetch the root TA due to network errors etc., we start off a timer +// to fire at 60 seconds and then keep doubling it till we fetch it +#define InitialTAFetchInterval 60 + +#if !TARGET_OS_IPHONE +DNSQuestion DNSSECProbeQuestion; +#endif + +mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval); + +mDNSlocal void LinkTrustAnchor(mDNS *const m, TrustAnchor *ta) +{ + int length = 0; + int i; + mDNSu8 *p; + TrustAnchor **t = &m->TrustAnchors; + char buffer[256]; + + while (*t) + t = &((*t)->next); + *t = ta; + + buffer[0] = 0; + p = ta->rds.digest; + for (i = 0; i < ta->digestLen; i++) + { + length += mDNS_snprintf(buffer+length, sizeof(buffer)-1-length, "%x", p[i]); + } + LogInfo("LinkTrustAnchor: Zone %##s, keytag %d, alg %d, digestType %d, digestLen %d, digest %s", ta->zone.c, ta->rds.keyTag, + ta->rds.alg, ta->rds.digestType, ta->digestLen, buffer); +} + +mDNSlocal void DelTrustAnchor(mDNS *const m, const domainname *zone) +{ + TrustAnchor **ta = &m->TrustAnchors; + TrustAnchor *tmp; + + while (*ta && !SameDomainName(&(*ta)->zone, zone)) + ta = &(*ta)->next; + + // First time, we won't find the TrustAnchor in the list as it has + // not been added. + if (!(*ta)) + return; + + tmp = *ta; + *ta = (*ta)->next; // Cut this record from the list + tmp->next = mDNSNULL; + if (tmp->rds.digest) + mDNSPlatformMemFree(tmp->rds.digest); + mDNSPlatformMemFree(tmp); +} + +mDNSlocal void AddTrustAnchor(mDNS *const m, const domainname *zone, mDNSu16 keytag, mDNSu8 alg, mDNSu8 digestType, int diglen, + mDNSu8 *digest) +{ + TrustAnchor *ta, *tmp; + mDNSu32 t = (mDNSu32) time(NULL); + + // Check for duplicates + tmp = m->TrustAnchors; + while (tmp) + { + if (SameDomainName(zone, &tmp->zone) && tmp->rds.keyTag == keytag && tmp->rds.alg == alg && tmp->rds.digestType == digestType && + !memcmp(tmp->rds.digest, digest, diglen)) + { + LogMsg("AddTrustAnchors: Found a duplicate"); + return; + } + tmp = tmp->next; + } + + ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor)); + if (!ta) + { + LogMsg("AddTrustAnchor: malloc failure ta"); + return; + } + ta->rds.keyTag = keytag; + ta->rds.alg = alg; + ta->rds.digestType = digestType; + ta->rds.digest = digest; + ta->digestLen = diglen; + ta->validFrom = t; + ta->validUntil = t + TEST_TA_EXPIRE_INTERVAL; + AssignDomainName(&ta->zone, zone); + ta->next = mDNSNULL; + + LinkTrustAnchor(m, ta); +} + +#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0' ) : \ + ((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) : \ + ((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1) + +mDNSlocal mDNSu8 *ConvertDigest(char *digest, int digestType, int *diglen) +{ + int i, j; + mDNSu8 *dig; + + switch (digestType) + { + case SHA1_DIGEST_TYPE: + *diglen = CC_SHA1_DIGEST_LENGTH; + break; + case SHA256_DIGEST_TYPE: + *diglen = CC_SHA256_DIGEST_LENGTH; + break; + default: + LogMsg("ConvertDigest: digest type %d not supported", digestType); + return mDNSNULL; + } + dig = mDNSPlatformMemAllocate(*diglen); + if (!dig) + { + LogMsg("ConvertDigest: malloc failure"); + return mDNSNULL; + } + + for (j=0,i=0; i<*diglen*2; i+=2) + { + int l, h; + l = HexVal(digest[i]); + h = HexVal(digest[i+1]); + if (l<0 || h<0) { LogMsg("ConvertDigest: Cannot convert digest"); return NULL;} + dig[j++] = (mDNSu8)((l << 4) | h); + } + return dig; +} + +// All the children are in a linked list +// +// has two children: and +// has four children +// +// Returns false if failed to parse the element i.e., malformed xml document. +// Validity of the actual values itself is done outside the function. +mDNSlocal mDNSBool ParseElementChildren(xmlDocPtr tadoc, xmlNode *node, TrustAnchor *ta) +{ + xmlNode *cur_node; + xmlChar *val1, *val2, *val; + char *invalid = NULL; + + val = val1 = val2 = NULL; + + for (cur_node = node; cur_node; cur_node = cur_node->next) + { + invalid = NULL; + val1 = val2 = NULL; + + val = xmlNodeListGetString(tadoc, cur_node->xmlChildrenNode, 1); + if (!val) + { + LogInfo("ParseElementChildren: NULL value for %s", cur_node->name); + continue; + } + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Zone")) + { + // MaeDomainNameFromDNSNameString does not work for "." + if (!xmlStrcmp(val, (const xmlChar *)".")) + { + ta->zone.c[0] = 0; + } + else if (!MakeDomainNameFromDNSNameString(&ta->zone, (char *)val)) + { + LogMsg("ParseElementChildren: Cannot parse Zone %s", val); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %##s", cur_node->name, ta->zone.c); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyTag")) + { + ta->rds.keyTag = strtol((const char *)val, &invalid, 10); + if (*invalid != '\0') + { + LogMsg("ParseElementChildren: KeyTag invalid character %d", *invalid); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.keyTag); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Algorithm")) + { + ta->rds.alg = strtol((const char *)val, &invalid, 10); + if (*invalid != '\0') + { + LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.alg); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"DigestType")) + { + ta->rds.digestType = strtol((const char *)val, &invalid, 10); + if (*invalid != '\0') + { + LogMsg("ParseElementChildren: Algorithm invalid character %c", *invalid); + goto error; + } + else + { + LogInfo("ParseElementChildren: Element %s, value %d", cur_node->name, ta->rds.digestType); + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"Digest")) + { + int diglen; + mDNSu8 *dig = ConvertDigest((char *)val, ta->rds.digestType, &diglen); + if (dig) + { + LogInfo("ParseElementChildren: Element %s, digest %s", cur_node->name, val); + ta->digestLen = diglen; + ta->rds.digest = dig; + } + else + { + LogMsg("ParseElementChildren: Element %s, NULL digest", cur_node->name); + goto error; + } + } + else if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest")) + { + struct tm tm; + val1 = xmlGetProp(cur_node, (const xmlChar *)"validFrom"); + if (val1) + { + char *s = strptime((const char *)val1, "%Y-%m-%dT%H:%M:%S", &tm); + if (!s) + { + LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val1); + goto error; + } + else + { + ta->validFrom = (mDNSu32)timegm(&tm); + } + } + val2 = xmlGetProp(cur_node, (const xmlChar *)"validUntil"); + if (val2) + { + char *s = strptime((const char *)val2, "%Y-%m-%dT%H:%M:%S", &tm); + if (!s) + { + LogMsg("ParseElementChildren: Parsing ValidFrom failed %s", val2); + goto error; + } + else + { + ta->validUntil = (mDNSu32)timegm(&tm); + } + } + else + { + // If there is no validUntil, set it to the next probing interval + mDNSu32 t = (mDNSu32) time(NULL); + ta->validUntil = t + ROOT_TA_UPDATE_INTERVAL; + } + LogInfo("ParseElementChildren: ValidFrom time %u, validUntil %u", (unsigned)ta->validFrom, (unsigned)ta->validUntil); + } + if (val1) + xmlFree(val1); + if (val2) + xmlFree(val2); + if (val) + xmlFree(val); + } + return mDNStrue; +error: + if (val1) + xmlFree(val1); + if (val2) + xmlFree(val2); + if (val) + xmlFree(val); + return mDNSfalse; +} + +mDNSlocal mDNSBool ValidateTrustAnchor(TrustAnchor *ta) +{ + time_t currTime = time(NULL); + + // Currently only support trust anchor for root. + if (!SameDomainName(&ta->zone, (const domainname *)"\000")) + { + LogInfo("ParseElementChildren: Zone %##s not root", ta->zone.c); + return mDNSfalse; + } + + switch (ta->rds.digestType) + { + case SHA1_DIGEST_TYPE: + if (ta->digestLen != CC_SHA1_DIGEST_LENGTH) + { + LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA1", ta->digestLen); + return mDNSfalse; + } + break; + case SHA256_DIGEST_TYPE: + if (ta->digestLen != CC_SHA256_DIGEST_LENGTH) + { + LogMsg("ValidateTrustAnchor: Invalid digest len %d for SHA256", ta->digestLen); + return mDNSfalse; + } + break; + default: + LogMsg("ValidateTrustAnchor: digest type %d not supported", ta->rds.digestType); + return mDNSfalse; + } + if (!ta->rds.digest) + { + LogMsg("ValidateTrustAnchor: digest NULL for %d", ta->rds.digestType); + return mDNSfalse; + } + switch (ta->rds.alg) + { + case CRYPTO_RSA_SHA512: + case CRYPTO_RSA_SHA256: + case CRYPTO_RSA_NSEC3_SHA1: + case CRYPTO_RSA_SHA1: + break; + default: + LogMsg("ValidateTrustAnchor: Algorithm %d not supported", ta->rds.alg); + return mDNSfalse; + } + + if (DNS_SERIAL_LT(currTime, ta->validFrom)) + { + LogMsg("ValidateTrustAnchor: Invalid ValidFrom time %u, currtime %u", (unsigned)ta->validFrom, (unsigned)currTime); + return mDNSfalse; + } + if (DNS_SERIAL_LT(ta->validUntil, currTime)) + { + LogMsg("ValidateTrustAnchor: Invalid ValidUntil time %u, currtime %u", (unsigned)ta->validUntil, (unsigned)currTime); + return mDNSfalse; + } + return mDNStrue; +} + +mDNSlocal mDNSBool ParseElement(xmlDocPtr tadoc, xmlNode * a_node, TrustAnchor *ta) +{ + xmlNode *cur_node = NULL; + + for (cur_node = a_node; cur_node; cur_node = cur_node->next) + { + if (cur_node->type == XML_ELEMENT_NODE) + { + // There could be multiple KeyDigests per TrustAnchor. We keep parsing till we + // reach the last one or we encounter an error in parsing the document. + if (!xmlStrcmp(cur_node->name, (const xmlChar *)"KeyDigest")) + { + if (ta->rds.digest) + mDNSPlatformMemFree(ta->rds.digest); + ta->rds.digestType = 0; + ta->digestLen = 0; + } + if (!ParseElementChildren(tadoc, cur_node->children, ta)) + return mDNSfalse; + if (!ParseElement(tadoc, cur_node->children, ta)) + return mDNSfalse; + } + } + return mDNStrue; +} + +mDNSlocal void TAComplete(mDNS *const m, void *context) +{ + TrustAnchor *ta = (TrustAnchor *)context; + + DelTrustAnchor(m, &ta->zone); + LinkTrustAnchor(m, ta); +} + +mDNSlocal void FetchRootTA(mDNS *const m) +{ + CFStringRef urlString = CFSTR("https://data.iana.org/root-anchors/root-anchors.xml"); + CFDataRef xmlData; + CFStringRef fileRef = NULL; + const char *xmlFileName = NULL; + char buf[512]; + CFURLRef url = NULL; + static unsigned int RootTAFetchInterval = InitialTAFetchInterval; + + (void) m; + + TrustAnchor *ta = (TrustAnchor *)mDNSPlatformMemAllocate(sizeof(TrustAnchor)); + if (!ta) + { + LogMsg("FetchRootTA: TrustAnchor alloc failed"); + return; + } + memset(ta, 0, sizeof(TrustAnchor)); + + url = CFURLCreateWithString(NULL, urlString, NULL); + if (!url) + { + LogMsg("FetchRootTA: CFURLCreateWithString error"); + mDNSPlatformMemFree(ta); + return; + } + + // If we can't fetch the XML file e.g., network problems, trigger a timer. All other failures + // should hardly happen in practice for which schedule the normal interval to refetch the TA. + if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, url, &xmlData, NULL, NULL, NULL)) + { + LogInfo("FetchRootTA: CFURLCreateDataAndPropertiesFromResource error"); + CFRelease(url); + mDNSPlatformMemFree(ta); + RegisterNotification(m, RootTAFetchInterval); + RootTAFetchInterval *= 2 + 1; + return; + } + + // get the name of the last component from the url, libxml will use it if + // it has to report an error + fileRef = CFURLCopyLastPathComponent(url); + if (fileRef) + { + xmlFileName = CFStringGetCStringPtr(fileRef, kCFStringEncodingUTF8); + if (!xmlFileName) + { + if (!CFStringGetCString(fileRef, buf, sizeof(buf), kCFStringEncodingUTF8) ) + strlcpy(buf, "nofile.xml", sizeof(buf)); + xmlFileName = (const char *)buf; + } + } + + // Parse the XML and get the CFXMLTree. + xmlDocPtr tadoc = xmlReadMemory((const char*)CFDataGetBytePtr(xmlData), + (int)CFDataGetLength(xmlData), xmlFileName, NULL, 0); + + CFRelease(fileRef); + CFRelease(url); + CFRelease(xmlData); + + if (!tadoc) + { + LogMsg("FetchRootTA: xmlReadMemory failed"); + goto done; + } + + xmlNodePtr root = xmlDocGetRootElement(tadoc); + if (!root) + { + LogMsg("FetchRootTA: Cannot get root element"); + goto done; + } + + if (ParseElement(tadoc, root, ta) && ValidateTrustAnchor(ta)) + { + // Do the actual addition of TA on the main queue. + mDNSPlatformDispatchAsync(m, ta, TAComplete); + } + else + { + if (ta->rds.digest) + mDNSPlatformMemFree(ta->rds.digest); + mDNSPlatformMemFree(ta); + } +done: + if (tadoc) + xmlFreeDoc(tadoc); + RegisterNotification(m, ROOT_TA_UPDATE_INTERVAL); + RootTAFetchInterval = InitialTAFetchInterval; + return; +} + + +#if APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE +mDNSlocal void DNSSECProbeCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + if (!AddRecord) + return; + + mDNS_Lock(m); + if ((m->timenow - question->StopTime) >= 0) + { + mDNS_Unlock(m); + LogDNSSEC("DNSSECProbeCallback: Question %##s (%s) timed out", question->qname.c, DNSTypeName(question->qtype)); + mDNS_StopQuery(m, question); + return; + } + mDNS_Unlock(m); + + // Wait till we get the DNSSEC results. If we get a negative response e.g., no DNS servers, the + // question will be restarted by the core and we should have the DNSSEC results eventually. + if (AddRecord != QC_dnssec) + { + LogDNSSEC("DNSSECProbeCallback: Question %##s (%s)", question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer)); + return; + } + + LogDNSSEC("DNSSECProbeCallback: Question %##s (%s), DNSSEC status %s", question->qname.c, DNSTypeName(question->qtype), + DNSSECStatusName(question->ValidationStatus)); + + mDNS_StopQuery(m, question); +} + +// Send a DNSSEC probe just for the sake of collecting DNSSEC statistics. +mDNSexport void DNSSECProbe(mDNS *const m) +{ + mDNSu32 rand; + + if (DNSSECProbeQuestion.ThisQInterval != -1) + return; + + rand = mDNSRandom(0x3FFFFFFF) % 100; + // Probe 5% of the time + if (rand > 5) + return; + + mDNS_DropLockBeforeCallback(); + InitializeQuestion(m, &DNSSECProbeQuestion, mDNSInterface_Any, (const domainname *)"\003com", kDNSType_DS, DNSSECProbeCallback, mDNSNULL); + DNSSECProbeQuestion.ValidatingResponse = 0; + DNSSECProbeQuestion.ValidationRequired = DNSSEC_VALIDATION_SECURE; + + BumpDNSSECStats(m, kStatsActionIncrement, kStatsTypeProbe, 1); + mDNS_StartQuery(m, &DNSSECProbeQuestion); + mDNS_ReclaimLockAfterCallback(); +} +#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_IPHONE + +// For now we fetch the root trust anchor and update the local copy +mDNSexport void UpdateTrustAnchors(mDNS *const m) +{ + // Register for a notification to fire immediately which in turn will update + // the trust anchor + if (RegisterNotification(m, 1)) + { + LogMsg("UpdateTrustAnchors: ERROR!! failed to register for notification"); + } +} + +mDNSlocal int RegisterNotification(mDNS *const m, unsigned int interval) +{ + int len = strlen("com.apple.system.notify.service.timer:+") + 21; // 21 bytes to accomodate the interval + char buffer[len]; + unsigned int blen; + int status; + + // Starting "interval" second from now (+ below indicates relative) register for a notification + blen = mDNS_snprintf(buffer, sizeof(buffer), "com.apple.system.notify.service.timer:+%us", interval); + if (blen >= sizeof(buffer)) + { + LogMsg("RegisterNotification: Buffer too small blen %d, buffer size %d", blen, sizeof(buffer)); + return -1; + } + LogInfo("RegisterNotification: buffer %s", buffer); + if (m->notifyToken) + { + notify_cancel(m->notifyToken); + m->notifyToken = 0; + } + status = notify_register_dispatch(buffer, &m->notifyToken, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^(int t) { (void) t; FetchRootTA(m); }); + + if (status != NOTIFY_STATUS_OK) + { + LogMsg("RegisterNotification: notify_register_dispatch failed"); + return -1; + } + return 0; +} + +mDNSexport mStatus DNSSECPlatformInit(mDNS *const m) +{ + int diglen; + + m->TrustAnchors = mDNSNULL; + m->notifyToken = 0; + + // Add a couple of trust anchors for testing purposes. + mDNSlocal const domainname *testZone = (const domainname*)"\007example"; + + char *digest = "F122E47B5B7D2B6A5CC0A21EADA11D96BB9CC927"; + mDNSu8 *dig = ConvertDigest(digest, 1, &diglen); + AddTrustAnchor(m, testZone, 23044, 5, 1, diglen, dig); + + char *digest1 = "D795AE5E1AFB200C6139474199B70EAD3F3484553FD97BE5A43704B8A4791F21"; + dig = ConvertDigest(digest1, 2, &diglen); + AddTrustAnchor(m, testZone, 23044, 5, 2, diglen, dig); + + // Add the TA for root zone manually here. We will dynamically fetch the root TA and + // update it shortly. If that fails e.g., disconnected from the network, we still + // have something to work with. + char *digest2 = "49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"; + dig = ConvertDigest(digest2, 2, &diglen); + AddTrustAnchor(m, (const domainname *)"\000", 19036, 8, 2, diglen, dig); + +#if !TARGET_OS_IPHONE + DNSSECProbeQuestion.ThisQInterval = -1; +#endif + return mStatus_NoError; +} diff --git a/mDNSMacOSX/DNSSECSupport.h b/mDNSMacOSX/DNSSECSupport.h new file mode 100644 index 0000000..2310fc2 --- /dev/null +++ b/mDNSMacOSX/DNSSECSupport.h @@ -0,0 +1,24 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DNSSEC_SUPPORT_H +#define __DNSSEC_SUPPORT_H + +extern mStatus DNSSECPlatformInit(mDNS *const m); +extern void UpdateTrustAnchors(mDNS *const m); + +#endif // __DNSSEC_SUPPORT_H diff --git a/mDNSMacOSX/DNSServiceDiscovery.c b/mDNSMacOSX/DNSServiceDiscovery.c index e629732..717efca 100644 --- a/mDNSMacOSX/DNSServiceDiscovery.c +++ b/mDNSMacOSX/DNSServiceDiscovery.c @@ -23,7 +23,7 @@ #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 "DNSServiceDiscovery.h" +#include "../mDNSMacOSX/DNSServiceDiscovery.h" #include "DNSServiceDiscoveryDefines.h" #include "DNSServiceDiscoveryReplyServer.h" diff --git a/mDNSMacOSX/LaunchDaemonInfo.helper.plist b/mDNSMacOSX/LaunchDaemonInfo.helper.plist index 9a686d8..a21868c 100644 --- a/mDNSMacOSX/LaunchDaemonInfo.helper.plist +++ b/mDNSMacOSX/LaunchDaemonInfo.helper.plist @@ -19,5 +19,7 @@ BeginTransactionAtShutdown + POSIXSpawnType + Interactive diff --git a/mDNSMacOSX/LaunchDaemonInfo.plist b/mDNSMacOSX/LaunchDaemonInfo.plist index 8966dc0..ba99d15 100644 --- a/mDNSMacOSX/LaunchDaemonInfo.plist +++ b/mDNSMacOSX/LaunchDaemonInfo.plist @@ -15,12 +15,13 @@ ProgramArguments /usr/sbin/mDNSResponder - -launchd MachServices com.apple.mDNSResponder + com.apple.mDNSResponder.dnsproxy + Sockets diff --git a/mDNSMacOSX/LegacyNATTraversal.c b/mDNSMacOSX/LegacyNATTraversal.c index 8e03d65..e7cd65f 100644 --- a/mDNSMacOSX/LegacyNATTraversal.c +++ b/mDNSMacOSX/LegacyNATTraversal.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -364,7 +364,7 @@ mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo) // Make sure to compute extport *before* we zero tcpInfo->retries extport = mDNSOpaque16fromIntVal(RequestedPortNum(natInfo)); tcpInfo->retries = 0; - natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE); + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, mStatus_NoError, extport, NATMAP_DEFAULT_LEASE, NATTProtocolUPNPIGD); } else if (http_result == HTTPCode_500) { @@ -382,7 +382,7 @@ mDNSlocal void handleLNTPortMappingResponse(tcpLNTInfo *tcpInfo) { LogMsg("handleLNTPortMappingResponse too many conflict retries %d %d", mDNSVal16(natInfo->IntPort), mDNSVal16(natInfo->RequestedPort)); mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", "noop", "Conflict - too many retries", "Retries: %d", tcpInfo->retries); - natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); + natTraversalHandlePortMapReply(m, natInfo, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD); } return; } @@ -485,8 +485,8 @@ exit: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.DeviceDescription", "success", "success", ""); break; case LNTExternalAddrOp: mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.AddressRequest", - mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", - mDNSIPv4AddressIsZero(m->ExternalAddress) ? "failure" : "success", ""); + mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success", + mDNSIPv4AddressIsZero(m->ExtAddress) ? "failure" : "success", ""); break; case LNTPortMapOp: if (tcpInfo->parentNATInfo) mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.legacy.PortMapRequest", (tcpInfo->parentNATInfo->Result) ? "failure" : "success", @@ -658,7 +658,7 @@ mDNSlocal mStatus SendPortMapRequest(mDNS *m, NATTraversalInfo *n) } else { - natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0); + natTraversalHandlePortMapReply(m, n, m->UPnPInterfaceID, NATErr_Res, zeroIPPort, 0, NATTProtocolUPNPIGD); return mStatus_NoError; } } @@ -911,7 +911,7 @@ mDNSexport void LNT_SendDiscoveryMsg(mDNS *m) // Create message bufLen = mDNS_snprintf((char*)buf, sizeof(m->omsg), msg, m->SSDPWANPPPConnection ? "PPP" : "IP"); - debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress); + debugf("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExtAddress); if (!mDNSIPv4AddressIsZero(m->Router.ip.v4)) { diff --git a/mDNSMacOSX/Private/dns_services.c b/mDNSMacOSX/Private/dns_services.c new file mode 100644 index 0000000..d0e9e6c --- /dev/null +++ b/mDNSMacOSX/Private/dns_services.c @@ -0,0 +1,212 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * PRIVATE DNSX CLIENT LIBRARY --FOR Apple Platforms ONLY OSX/iOS-- + * Resides in /usr/lib/libdns_services.dylib + */ + +#include "dns_services.h" +#include "dns_xpc.h" +#include +#include +#include +#include +#include + +//************************************************************************************************************* +// Globals + +#define connection_t xpc_connection_t + +struct _DNSXConnRef_t +{ + connection_t conn_ref; // xpc_connection between client and daemon + dispatch_queue_t lib_q; // internal queue created in library itself + void *AppCallBack; // Callback function ptr for Client + dispatch_queue_t client_q; // Queue specified by client for scheduling its Callback +}; + +//************************************************************************************************************* +// Utility Functions + +static bool LogDebugEnabled() +{ + return false; +} + +static void LogDebug(const char *prefix, xpc_object_t o) +{ + if (!LogDebugEnabled()) + return; + + char *desc = xpc_copy_description(o); + syslog(LOG_INFO, "%s: %s", prefix, desc); + free(desc); +} + +//************************************************************************************************************** + +void DNSXRefDeAlloc(DNSXConnRef connRef) +{ + if (!connRef) + { + syslog(LOG_WARNING, "dns_services: DNSXRefDeAlloc called with NULL DNSXConnRef"); + return; + } + + // Schedule this work on the internal library queue + dispatch_sync(connRef->lib_q, ^{ + + xpc_release(connRef->conn_ref); + connRef->AppCallBack = NULL; + dispatch_release(connRef->client_q); + + }); + + dispatch_release(connRef->lib_q); + free(connRef); + + syslog(LOG_INFO, "dns_services: DNSXRefDeAlloc successfully DeAllocated connRef"); + +} + +// Sends the Msg(Dictionary) to the Server +static DNSXErrorType SendMsgToServer(DNSXConnRef *connRef, xpc_object_t msg, bool old_conn) +{ + DNSXErrorType errx = kDNSX_NoError; + + LogDebug("dns_services: SendMsgToServer", msg); + + xpc_connection_set_event_handler((*connRef)->conn_ref, ^(xpc_object_t recv_msg) + { + xpc_type_t type = xpc_get_type(recv_msg); + + if (type == XPC_TYPE_DICTIONARY) + { + LogDebug("dns_services: SendMsgToServer SUCCESS CALLBACK FROM SERVER", recv_msg); + syslog(LOG_INFO, "dns_services: Successfully Sent Msg to the Daemon"); + uint64_t daemon_status = xpc_dictionary_get_uint64(recv_msg, kDNSDaemonReply); + + // Schedule the AppCallBacks on the Client Specified Queue + switch (daemon_status) + { + case kDNSDaemonEngaged: + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_Engaged); + }); + break; + case kDNSMsgReceived: + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_NoError); + }); + break; + default: + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_UnknownErr); + }); + break; + } + + } + else + { + LogDebug("dns_services: SendMsgToServer UNEXPECTED CALLBACK FROM SERVER", recv_msg); + syslog(LOG_WARNING, "dns_services: Connection failed since NO privileges to access service OR Daemon NOT Running"); + dispatch_async((*connRef)->client_q, ^{ + ((DNSXEnableProxyReply)(*connRef)->AppCallBack)((*connRef), kDNSX_DaemonNotRunning); + }); + } + }); + + // To prevent Over-Resume of a connection + if (!old_conn) + xpc_connection_resume((*connRef)->conn_ref); + xpc_connection_send_message((*connRef)->conn_ref, msg); + if (!errx) + syslog(LOG_INFO, "dns_services: SendMSgToServer sent Msg Dict successfully to Daemon"); + return errx; +} + +// Creates a new DNSX Connection Reference(DNSXConnRef). +// If DNSXConnRef exists, you may want to use that depending on the use case +static DNSXErrorType InitConnection(DNSXConnRef *connRef, const char *servname, dispatch_queue_t clientq, void *AppCallBack) +{ + if (!connRef) + { + syslog(LOG_WARNING, "dns_services: InitConnection() called with NULL DNSXConnRef"); + return kDNSX_BadParam; + } + + *connRef = malloc(sizeof(struct _DNSXConnRef_t)); + if (!(*connRef)) + { + syslog(LOG_WARNING, "dns_services: InitConnection() No memory to allocate"); + return kDNSX_NoMem; + } + + // Initialize the DNSXConnRef + dispatch_retain(clientq); + (*connRef)->client_q = clientq; + (*connRef)->AppCallBack = AppCallBack; + (*connRef)->lib_q = dispatch_queue_create("com.apple.mDNSResponder.libdns_services.q", NULL); + (*connRef)->conn_ref = xpc_connection_create_mach_service(servname, (*connRef)->lib_q, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + + syslog(LOG_INFO, "dns_services: InitConnection() successfully create a new DNSXConnRef"); + return kDNSX_NoError; +} + +DNSXErrorType DNSXEnableProxy(DNSXConnRef *connRef, DNSProxyParameters proxyparam, IfIndex inIfindexArr[MaxInputIf], + IfIndex outIfindex, dispatch_queue_t clientq, DNSXEnableProxyReply callBack) +{ + + DNSXErrorType errx = kDNSX_NoError; + bool old_conn = false; + + // Sanity Checks + if (!connRef || !callBack || !clientq) + { + syslog(LOG_WARNING, "dns_services: DNSXEnableProxy called with NULL DNSXConnRef OR Callback OR ClientQ parameter"); + return kDNSX_BadParam; + } + + // If no connRef, get it from InitConnection() + if (!*connRef) + { + errx = InitConnection(connRef, kDNSProxyService, clientq, callBack); + if (errx) // On error InitConnection() leaves *connRef set to NULL + { + syslog(LOG_WARNING, "dns_services: Since InitConnection() returned %d error returning w/o sending msg", errx); + return errx; + } + } + else // Client already has a valid connRef + { + old_conn = true; + } + + // Create Dictionary To Send + xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0); + if (!dict) + { + syslog(LOG_WARNING, "dns_services: DNSXEnableProxy could not create the Msg Dict To Send!"); + DNSXRefDeAlloc(*connRef); + return kDNSX_DictError; + } + + xpc_dictionary_set_uint64(dict, kDNSProxyParameters, proxyparam); + + xpc_dictionary_set_uint64(dict, kDNSInIfindex0, inIfindexArr[0]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex1, inIfindexArr[1]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex2, inIfindexArr[2]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex3, inIfindexArr[3]); + xpc_dictionary_set_uint64(dict, kDNSInIfindex4, inIfindexArr[4]); + + xpc_dictionary_set_uint64(dict, kDNSOutIfindex, outIfindex); + + errx = SendMsgToServer(connRef, dict, old_conn); + xpc_release(dict); + + return errx; +} + diff --git a/mDNSMacOSX/Private/dns_services.h b/mDNSMacOSX/Private/dns_services.h new file mode 100644 index 0000000..7b74e10 --- /dev/null +++ b/mDNSMacOSX/Private/dns_services.h @@ -0,0 +1,124 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * + * @header Interface to DNSX SPI + * + * @discussion Describes the functions and data structures + * that make up the DNSX SPI + */ + +#ifndef _DNS_SERVICES_H +#define _DNS_SERVICES_H + +#include + +// DNSXConnRef: Opaque internal data type +typedef struct _DNSXConnRef_t *DNSXConnRef; + +typedef enum +{ + kDNSX_NoError = 0, + kDNSX_UnknownErr = -65537, /* 0xFFFE FFFF */ + kDNSX_NoMem = -65539, + kDNSX_BadParam = -65540, + kDNSX_DaemonNotRunning = -65563, /* Background daemon not running */ + kDNSX_DictError = -65565, /* Dictionary Error */ + kDNSX_Engaged = -65566, /* DNS Proxy is in use by another client */ + kDNSX_Timeout = -65568 +} DNSXErrorType; + +// A max of 5 input interfaces can be processed at one time +#define MaxInputIf 5 +#define IfIndex uint64_t +#define kDNSIfindexAny 0 + +// Enable DNS Proxy with an appropriate parameter defined below +typedef enum +{ + kDNSProxyEnable = 1 + // Other values reserved for future use +} DNSProxyParameters; + +/********************************************************************************************* +* +* Enable DNS Proxy Functionality +* +*********************************************************************************************/ + +/* DNSXEnableProxy : Turns ON the DNS Proxy (Details below) + * + * DNSXEnableProxyReply() parameters: + * + * connRef: The DNSXConnRef initialized by DNSXEnableProxy(). + * + * errCode: Will be kDNSX_NoError on success, otherwise will indicate the + * failure that occurred. Other parameters are undefined if + * errCode is nonzero. + * + */ + +typedef void (*DNSXEnableProxyReply) +( + DNSXConnRef connRef, + DNSXErrorType errCode +); + +/* DNSXEnableProxy + * + * Enables the DNS Proxy functionality which will remain ON until the client terminates explictly (or exits/crashes). + * Client can turn it OFF by passing the returned DNSXConnRef to DNSXRefDeAlloc() + * + * DNSXEnableProxy() Parameters: + * + * connRef: A pointer to DNSXConnRef that is initialized to NULL when called for the first + * time. If the call succeeds it will be initialized to a non-NULL value. + * Client terminates the DNS Proxy by passing this DNSXConnRef to DNSXRefDeAlloc(). + * + * proxyparam: Enable DNS Proxy functionality with parameters that are described in + * DNSProxyParameters above. + * + * inIfindexArr[MaxInputIf]: List of input interfaces from which the DNS queries will be accepted and + * forwarded to the output interface specified below. The daemon processes + * MaxInputIf entries in the list. For eg. if one has less than MaxInputIfs + * values, just initialize the other values to be 0. Note: This field needs to + * be initialized by the client. + * + * outIfindex: Output interface on which the query will be forwarded. + * Passing kDNSIfindexAny causes DNS Queries to be sent on the primary interface. + * + * clientq: Queue the client wants to schedule the callBack on (Note: Must not be NULL) + * + * callBack: CallBack function for the client that indicates success or failure. + * Note: callback may be invoked more than once, For eg. if enabling DNS Proxy + * first succeeds and the daemon possibly crashes sometime later. + * + * return value: Returns kDNSX_NoError when no error otherwise returns an error code indicating + * the error that occurred. Note: A return value of kDNSX_NoError does not mean + * that DNS Proxy was successfully enabled. The callBack may asynchronously + * return an error (such as kDNSX_DaemonNotRunning/ kDNSX_Engaged) + * + */ + +DNSXErrorType DNSXEnableProxy +( + DNSXConnRef *connRef, + DNSProxyParameters proxyparam, + IfIndex inIfindexArr[MaxInputIf], + IfIndex outIfindex, + dispatch_queue_t clientq, + DNSXEnableProxyReply callBack +); + +/* DNSXRefDeAlloc() + * + * Terminate a connection with the daemon and free memory associated with the DNSXConnRef. + * Used to Disable DNS Proxy on that connection. + * + * connRef: A DNSXConnRef initialized by any of the DNSX*() calls. + * + */ +void DNSXRefDeAlloc(DNSXConnRef connRef); + +#endif /* _DNS_SERVICES_H */ diff --git a/mDNSMacOSX/Private/dns_xpc.h b/mDNSMacOSX/Private/dns_xpc.h new file mode 100644 index 0000000..10ae01f --- /dev/null +++ b/mDNSMacOSX/Private/dns_xpc.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * Defines the common interface between mDNSResponder and the Private ClientLibrary(libdnsprivate.dylib) + * Uses XPC as the IPC Mechanism + * + */ + +#ifndef DNS_XPC_H +#define DNS_XPC_H + +#define kDNSProxyService "com.apple.mDNSResponder.dnsproxy" + +#define kDNSProxyParameters "DNSProxyParameters" + +#define kDNSInIfindex0 "InputArrayInterfaceIndex[0]" +#define kDNSInIfindex1 "InputArrayInterfaceIndex[1]" +#define kDNSInIfindex2 "InputArrayInterfaceIndex[2]" +#define kDNSInIfindex3 "InputArrayInterfaceIndex[3]" +#define kDNSInIfindex4 "InputArrayInterfaceIndex[4]" + +#define kDNSOutIfindex "OutputInterfaceIndex" + +#define kDNSDaemonReply "DaemonReplyStatusToClient" + +typedef enum +{ + kDNSMsgReceived = 0, + kDNSDaemonEngaged +} DaemonReplyStatusCodes; + +#endif // DNS_XPC_H diff --git a/mDNSMacOSX/Private/xpc_services.c b/mDNSMacOSX/Private/xpc_services.c new file mode 100644 index 0000000..7a0e29f --- /dev/null +++ b/mDNSMacOSX/Private/xpc_services.c @@ -0,0 +1,255 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * xpc_services.c + * mDNSResponder + * + * XPC as an IPC mechanism to communicate with Clients. Open only to Apple OSX/iOS clients + */ + +#include "xpc_services.h" +#include "dns_xpc.h" + +#ifndef UNICAST_DISABLED + +#include "dnsproxy.h" // DNSProxyInit/ProxyUDPCallback/ProxyTCPCallback +#include "mDNSMacOSX.h" // KQueueLock/KQueueUnlock +#include +#include // xpc_connection_copy_entitlement_value + +// *************************************************************************** +// Globals +extern mDNS mDNSStorage; +static int dps_client_pid; // To track current active client using DNS Proxy Service +static dispatch_queue_t dps_queue = NULL; +// *************************************************************************** + +// prints current XPC Server State +mDNSexport void xpcserver_info(mDNS *const m) +{ + + LogMsg("----- Active XPC Clients -----"); + if (dps_client_pid) + LogMsg("DNSProxy->Client[%d]: InputIntfs[%d, %d, %d, %d, %d] Output[%d]", dps_client_pid, m->dp_ipintf[0], + m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4], m->dp_opintf); +} + + +mDNSlocal void ActivateDNSProxy(mDNSu32 IpIfArr[MaxIp], mDNSu32 OpIf, mDNSBool proxy_off) +{ + + LogInfo("ActivateDNSProxy: InterfaceIndex List by Client: Input[%d, %d, %d, %d, %d] Output[%d] ", IpIfArr[0], IpIfArr[1], + IpIfArr[2], IpIfArr[3], IpIfArr[4], OpIf); + + KQueueLock(&mDNSStorage); + DNSProxyInit(&mDNSStorage, IpIfArr, OpIf); + if (proxy_off) // Open skts only if proxy was OFF else we may end up opening extra skts + mDNSPlatformInitDNSProxySkts(&mDNSStorage, ProxyUDPCallback, ProxyTCPCallback); + KQueueUnlock(&mDNSStorage, "DNSProxy Activated"); +} + +mDNSlocal void handle_dps_terminate() +{ + + LogInfo("handle_dps_terminate: Client PID[%d] terminated connection or crashed. Proceed to terminate DNSProxy", dps_client_pid); + // Clear the Client's PID, so that we can now accept new DPS requests + dps_client_pid = 0; + + KQueueLock(&mDNSStorage); + mDNSPlatformCloseDNSProxySkts(&mDNSStorage); + // TBD: Close TCP Sockets + DNSProxyTerminate(&mDNSStorage); + KQueueUnlock(&mDNSStorage, "DNSProxy Deactivated"); +} + +mDNSlocal void handle_dps_request(xpc_object_t req) +{ + int dps_tmp_client; + mDNSBool proxy_off = mDNSfalse; + xpc_connection_t remote_conn = xpc_dictionary_get_remote_connection(req); + dps_tmp_client = (int) xpc_connection_get_pid(remote_conn); + + LogInfo("handle_dps_request: Handler for DNS Proxy Requests"); + + if (dps_client_pid <= 0) + { + LogInfo("handle_dps_request: DNSProxy is not engaged (New Client)"); + // No Active Client, save new Client's PID (also indicates DNS Proxy was OFF) + dps_client_pid = dps_tmp_client; + proxy_off = mDNStrue; + } + else + { + // We already have an active DNS Proxy Client and until that client does not terminate the connection + // or crashes, a new client cannot change/override the current DNS Proxy settings. + if (dps_client_pid != dps_tmp_client) + { + LogMsg("handle_dps_request: A Client is already using DNS Proxy and your request cannot be handled at this time"); + // Return Engaged Status to the client + xpc_object_t reply = xpc_dictionary_create(NULL, NULL, 0); + if (reply) + { + xpc_dictionary_set_uint64(reply, kDNSDaemonReply, kDNSDaemonEngaged); + xpc_connection_send_message(remote_conn, reply); + xpc_release(reply); + } + else + { + LogMsg("handle_dps_request: Reply Dictionary could not be created"); + return; + } + // We do not really need to terminate the connection with the client + // as it may try again later which is fine + return; + } + } + + // Return Success Status to the client + xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0); + if (response) + { + xpc_dictionary_set_uint64(response, kDNSDaemonReply, kDNSMsgReceived); + xpc_connection_send_message(remote_conn, response); + xpc_release(response); + } + else + { + LogMsg("handle_dps_request: Response Dictionary could not be created"); + return; + } + + // Proceed to get DNS Proxy Settings from the Client + if (xpc_dictionary_get_uint64(req, kDNSProxyParameters)) + { + mDNSu32 inIf[MaxIp], outIf; + + inIf[0] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex0); + inIf[1] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex1); + inIf[2] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex2); + inIf[3] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex3); + inIf[4] = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSInIfindex4); + outIf = (mDNSu32)xpc_dictionary_get_uint64(req, kDNSOutIfindex); + + ActivateDNSProxy(inIf, outIf, proxy_off); + } + +} + +// Verify Client's Entitlement +mDNSlocal mDNSBool IsEntitled(xpc_connection_t conn, const char *password) +{ + mDNSBool entitled = mDNSfalse; + xpc_object_t ent = xpc_connection_copy_entitlement_value(conn, password); + + if (ent) + { + if (xpc_get_type(ent) == XPC_TYPE_BOOL && xpc_bool_get_value(ent)) + { + entitled = mDNStrue; + } + xpc_release(ent); + } + else + { + LogMsg("IsEntitled: Client Entitlement is NULL"); + } + + return entitled; +} + +mDNSlocal void accept_dps_client(xpc_connection_t conn) +{ + uid_t euid; + euid = xpc_connection_get_euid(conn); + + if (euid != 0 || !IsEntitled(conn, kDNSProxyService)) + { + LogMsg("accept_dps_client: DNSProxyService Client Pid[%d] is missing Entitlement or is not root!", (int) xpc_connection_get_pid(conn)); + xpc_connection_cancel(conn); + return; + } + + xpc_retain(conn); + xpc_connection_set_target_queue(conn, dps_queue); + xpc_connection_set_event_handler(conn, ^(xpc_object_t req_msg) + { + xpc_type_t type = xpc_get_type(req_msg); + + if (type == XPC_TYPE_DICTIONARY) + { + handle_dps_request(req_msg); + } + // We hit the case below only if Client Terminated DPS Connection OR Crashed + else + { + LogInfo("accept_dps_client: DPS Client %p teared down the connection or Crashed", (void *) conn); + // Only the Client that has activated DPS should be able to terminate it + if (((int)xpc_connection_get_pid(conn)) == dps_client_pid) + handle_dps_terminate(); + xpc_release(conn); + } + }); + xpc_connection_resume(conn); + +} + +mDNSlocal void init_dnsproxy_service(void) +{ + + xpc_connection_t dps_listener = xpc_connection_create_mach_service(kDNSProxyService, NULL, XPC_CONNECTION_MACH_SERVICE_LISTENER); + if (!dps_listener || xpc_get_type(dps_listener) != XPC_TYPE_CONNECTION) + { + LogMsg("init_dnsproxy_service: Error Creating XPC Listener for DNSProxyService !!"); + return; + } + + dps_queue = dispatch_queue_create("com.apple.mDNSResponder.dnsproxyservice_queue", NULL); + + xpc_connection_set_event_handler(dps_listener, ^(xpc_object_t eventmsg) + { + xpc_type_t type = xpc_get_type(eventmsg); + + if (type == XPC_TYPE_CONNECTION) + { + LogInfo("init_dnsproxy_service: New DNSProxyService Client %p", eventmsg); + accept_dps_client(eventmsg); + } + // Ideally, we would never hit the cases below + else if (type == XPC_TYPE_ERROR) + { + LogMsg("init_dnsproxy_service: XPCError: %s", xpc_dictionary_get_string(eventmsg, XPC_ERROR_KEY_DESCRIPTION)); + return; + } + else + { + LogMsg("init_dnsproxy_service: Unknown EventMsg type"); + return; + } + }); + xpc_connection_resume(dps_listener); + +} + +mDNSexport void xpc_server_init() +{ + // Add XPC Services here + init_dnsproxy_service(); +} + +#else // !UNICAST_DISABLED + +mDNSexport void xpc_server_init() +{ + return; +} + +mDNSexport void xpcserver_info(mDNS *const m) +{ + (void) m; + + return; +} + +#endif // !UNICAST_DISABLED + diff --git a/mDNSMacOSX/Private/xpc_services.h b/mDNSMacOSX/Private/xpc_services.h new file mode 100644 index 0000000..50081be --- /dev/null +++ b/mDNSMacOSX/Private/xpc_services.h @@ -0,0 +1,21 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * + * + * File: xpc_services.h + * + * Contains: Interfaces necessary to talk to xpc_services.c + * + */ + +#ifndef XPC_SERVICES_H +#define XPC_SERVICES_H + +#include "mDNSEmbeddedAPI.h" + +extern void xpc_server_init(void); +extern void xpcserver_info(mDNS *const m); + +#endif // XPC_SERVICES_H diff --git a/mDNSMacOSX/README.privsep b/mDNSMacOSX/README.privsep index 30de198..130e327 100644 --- a/mDNSMacOSX/README.privsep +++ b/mDNSMacOSX/README.privsep @@ -9,22 +9,16 @@ needed and handles requests from mDNSResponder. 10 seconds of idle time. * The com.apple.mDNSResponder LaunchD job specifies the account under - which to run, so that mDNSResponder starts as _mdnsresponder. When - run as root--- e.g. from the command line with `sudo'--- - mDNSResponder drops privileges itself. + which to run, so that mDNSResponder starts as _mdnsresponder. * A subdirectory named "mdns" and owned by _mdnsresponder has been created in /var/run. The PID file and uDNS server socket has been moved to that subdirectory. * There are currently six remote procedure calls handled by - mDNSResponderHelper: mDNSDynamicStoreSetConfig, - mDNSPreferencesSetName, mDNSKeychainGetSecrets, + mDNSResponderHelper: mDNSPreferencesSetName, mDNSKeychainGetSecrets, mDNSConfigureServer, and mDNSAutoTunnelSetKeys -* mDNSDynamicStoreSetConfig allows mDNSResponder to set the - MulticastDNS, PrivateDNS, or DynamicDNS configurations. - * mDNSPreferencesSetName allows mDNSResponder to set the computer name or local host name, and displays a notification if there was a conflict. diff --git a/mDNSMacOSX/VPNService.c b/mDNSMacOSX/VPNService.c new file mode 100644 index 0000000..8c1bf1d --- /dev/null +++ b/mDNSMacOSX/VPNService.c @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4 -*- + * + * Copyright (c) 2013 Apple Computer, Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mDNSMacOSX.h" +#include + +mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q) +{ + (void) m; + int sid; + + if (q->pid) + { + sid = VPNAppLayerGetMatchingServiceIdentifier(q->pid, NULL); + } + else + { + sid = VPNAppLayerGetMatchingServiceIdentifier(0, q->uuid); + } + LogInfo("mDNSPlatformGetServiceID: returning %d for %##s (%s)", sid, q->qname.c, DNSTypeName(q->qtype)); + return sid; +} diff --git a/mDNSMacOSX/com.apple.networking.mDNSResponder b/mDNSMacOSX/com.apple.networking.mDNSResponder new file mode 100644 index 0000000..d07c99d --- /dev/null +++ b/mDNSMacOSX/com.apple.networking.mDNSResponder @@ -0,0 +1 @@ +? [= LoggerID com.apple.networking.mDNSResponder] file /Library/Logs/CrashReporter/com.apple.networking.mDNSResponder.log rotate file_max=1M compress diff --git a/mDNSMacOSX/daemon.c b/mDNSMacOSX/daemon.c index 151053f..3b3ed39 100644 --- a/mDNSMacOSX/daemon.c +++ b/mDNSMacOSX/daemon.c @@ -16,12 +16,8 @@ * */ -// We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h -#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4 - #include #include -#include #include #include #include @@ -29,18 +25,18 @@ #include #include #include +#include // for launch_socket_service_check_in() #include #include #include #include #include #include - -#if TARGET_OS_EMBEDDED -#include - -#define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0) -#endif +#include +#include +#include +#include +#include // for bootstrap_check_in() #include "DNSServiceDiscoveryRequestServer.h" #include "DNSServiceDiscoveryReply.h" @@ -50,10 +46,21 @@ #include "mDNSMacOSX.h" // Defines the specific types needed to run mDNS on this platform #include "uds_daemon.h" // Interface to the server side implementation of dns_sd.h +#include "xpc_services.h" // Interface to XPC services -#include +#include "../mDNSMacOSX/DNSServiceDiscovery.h" #include "helper.h" +static aslclient log_client = NULL; +static aslmsg log_msg = NULL; + +// Used on Embedded Side for Reading mDNSResponder Managed Preferences Profile +#if TARGET_OS_EMBEDDED +#define kmDNSEnableLoggingStr CFSTR("EnableLogging") +#define kmDNSResponderPrefIDStr "com.apple.mDNSResponder.plist" +#define kmDNSResponderPrefID CFSTR(kmDNSResponderPrefIDStr) +#endif + //************************************************************************************************************* #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - Globals @@ -68,7 +75,6 @@ static mDNS_PlatformSupport PlatformStorage; #define RR_CACHE_SIZE ((16*1024) / sizeof(CacheRecord)) static CacheEntity rrcachestorage[RR_CACHE_SIZE]; -static const char kmDNSBootstrapName[] = "com.apple.mDNSResponderRestart"; static mach_port_t m_port = MACH_PORT_NULL; #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM @@ -78,8 +84,6 @@ static mach_port_t client_death_port = MACH_PORT_NULL; static mach_port_t signal_port = MACH_PORT_NULL; #endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM -static mach_port_t server_priv_port = MACH_PORT_NULL; - static dnssd_sock_t *launchd_fds = mDNSNULL; static mDNSu32 launchd_fds_count = 0; @@ -91,8 +95,6 @@ static mDNSu32 launchd_fds_count = 0; // even extra-slow clients a fair chance before we cut them off. #define MDNS_MM_TIMEOUT 250 -static int restarting_via_mach_init = 0; // Used on Jaguar/Panther when daemon is started via mach_init mechanism -static int started_via_launchdaemon = 0; // Indicates we're running on Tiger or later, where daemon is managed by launchd static mDNSBool advertise = mDNS_Init_AdvertiseLocalAddresses; // By default, advertise addresses (& other records) via multicast extern mDNSBool StrictUnicastOrdering; @@ -710,7 +712,7 @@ mDNSlocal mStatus AddDomainToBrowser(DNSServiceBrowser *browser, const domainnam AssignDomainName(&question->domain, d); question->next = browser->qlist; LogOperation("%5d: DNSServiceBrowse(%##s%##s) START", browser->ClientMachPort, browser->type.c, d->c); - err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, FoundInstance, browser); + err = mDNS_StartBrowse(&mDNSStorage, &question->q, &browser->type, d, mDNSNULL, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, FoundInstance, browser); if (!err) browser->qlist = question; else @@ -768,7 +770,7 @@ mDNSexport kern_return_t provide_DNSServiceBrowserCreate_rpc(mach_port_t unuseds // Check other parameters domainname t, d; t.c[0] = 0; - mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + mDNSs32 NumSubTypes = ChopSubTypes(regtype, mDNSNULL); // Note: Modifies regtype string to remove trailing subtypes if (NumSubTypes < 0 || NumSubTypes > 1) { errormsg = "Bad Service SubType"; goto badparam; } if (NumSubTypes == 1 && !AppendDNSNameString(&t, regtype + strlen(regtype) + 1)) { errormsg = "Bad Service SubType"; goto badparam; } @@ -1052,7 +1054,7 @@ mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname { LogMsg("Requested addition of domain %##s already in list", domain->c); return mStatus_AlreadyRegistered; } } - SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype); + SubTypes = AllocateSubTypes(x->NumSubTypes, x->regtype, mDNSNULL); if (x->NumSubTypes && !SubTypes) return mStatus_NoMemoryErr; si = mallocL("ServiceInstance", sizeof(*si) - sizeof(RDataBody) + x->rdsize); @@ -1063,6 +1065,7 @@ mDNSlocal mStatus AddServiceInstance(DNSServiceRegistration *x, const domainname si->autoname = x->autoname; si->name = x->autoname ? mDNSStorage.nicelabel : x->name; si->domain = *domain; + si->srs.AnonData = mDNSNULL; err = mDNS_RegisterService(&mDNSStorage, &si->srs, &si->name, &x->type, domain, NULL, x->port, x->txtinfo, x->txt_len, SubTypes, x->NumSubTypes, mDNSInterface_Any, RegCallback, si, 0); @@ -1129,7 +1132,7 @@ mDNSexport kern_return_t provide_DNSServiceRegistrationCreate_rpc(mach_port_t un // Check for sub-types after the service type size_t reglen = strlen(regtype) + 1; if (reglen > MAX_ESCAPED_DOMAIN_NAME) { errormsg = "reglen too long"; goto badparam; } - mDNSs32 NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes + mDNSs32 NumSubTypes = ChopSubTypes(regtype, mDNSNULL); // Note: Modifies regtype string to remove trailing subtypes if (NumSubTypes < 0) { errormsg = "Bad Service SubType"; goto badparam; } // Check other parameters @@ -1560,6 +1563,30 @@ fail: #pragma mark - Startup, shutdown, and supporting code #endif +mDNSlocal void ExitCallback(int sig) +{ + (void)sig; // Unused + LogMsg("%s stopping", mDNSResponderVersionString); + + debugf("ExitCallback: Aborting MIG clients"); + while (DNSServiceDomainEnumerationList) + AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList); + while (DNSServiceBrowserList) + AbortClient(DNSServiceBrowserList->ClientMachPort, DNSServiceBrowserList); + while (DNSServiceResolverList) + AbortClient(DNSServiceResolverList->ClientMachPort, DNSServiceResolverList); + while (DNSServiceRegistrationList) + AbortClient(DNSServiceRegistrationList->ClientMachPort, DNSServiceRegistrationList); + + if (udsserver_exit() < 0) + LogMsg("ExitCallback: udsserver_exit failed"); + + debugf("ExitCallback: mDNS_StartExit"); + mDNS_StartExit(&mDNSStorage); +} + +#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + mDNSlocal void DNSserverCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) { mig_reply_error_t *request = msg; @@ -1653,76 +1680,6 @@ done: KQueueUnlock(&mDNSStorage, "Mach client event"); } -mDNSlocal kern_return_t registerBootstrapService() -{ - kern_return_t status; - mach_port_t service_rcv_port; - - LogMsg("Registering Bootstrap Service"); - - /* - * See if our service name is already registered and if we have privilege to check in. - */ - status = bootstrap_check_in(bootstrap_port, (char*)kmDNSBootstrapName, &service_rcv_port); - if (status == KERN_SUCCESS) - { - /* - * If so, we must be a followup instance of an already defined server. In that case, - * the bootstrap port we inherited from our parent is the server's privilege port, so set - * that in case we have to unregister later (which requires the privilege port). - */ - server_priv_port = bootstrap_port; - restarting_via_mach_init = TRUE; - } - else - { - LogMsg("registerBootstrapService: ERROR!! Registering Bootstrap Service failed %d", status); - return status; - } - - /* - * We have no intention of responding to requests on the service port. We are not otherwise - * a Mach port-based service. We are just using this mechanism for relaunch facilities. - * So, we can dispose of all the rights we have for the service port. We don't destroy the - * send right for the server's privileged bootstrap port - in case we have to unregister later. - */ - mach_port_destroy(mach_task_self(), service_rcv_port); - return status; -} - -mDNSlocal kern_return_t destroyBootstrapService() -{ - debugf("Destroying Bootstrap Service"); - return bootstrap_register(server_priv_port, (char*)kmDNSBootstrapName, MACH_PORT_NULL); -} - -mDNSlocal void ExitCallback(int sig) -{ - (void)sig; // Unused - LogMsg("%s stopping", mDNSResponderVersionString); - - debugf("ExitCallback"); - if (!mDNS_DebugMode && !started_via_launchdaemon) - destroyBootstrapService(); - - debugf("ExitCallback: Aborting MIG clients"); - while (DNSServiceDomainEnumerationList) - AbortClient(DNSServiceDomainEnumerationList->ClientMachPort, DNSServiceDomainEnumerationList); - while (DNSServiceBrowserList) - AbortClient(DNSServiceBrowserList->ClientMachPort, DNSServiceBrowserList); - while (DNSServiceResolverList) - AbortClient(DNSServiceResolverList->ClientMachPort, DNSServiceResolverList); - while (DNSServiceRegistrationList) - AbortClient(DNSServiceRegistrationList->ClientMachPort, DNSServiceRegistrationList); - - if (udsserver_exit() < 0) LogMsg("ExitCallback: udsserver_exit failed"); - - debugf("ExitCallback: mDNS_StartExit"); - mDNS_StartExit(&mDNSStorage); -} - -#ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - // Send a mach_msg to ourselves (since that is signal safe) telling us to cleanup and exit mDNSlocal void HandleSIG(int sig) { @@ -1751,43 +1708,19 @@ mDNSlocal void HandleSIG(int sig) mDNSlocal void INFOCallback(void) { mDNSs32 utc = mDNSPlatformUTC(); - DNSServiceDomainEnumeration *e; - DNSServiceBrowser *b; - DNSServiceResolver *l; - DNSServiceRegistration *r; NetworkInterfaceInfoOSX *i; DNSServer *s; McastResolver *mr; + // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when SIGINFO is received. + // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file + // present in /etc/asl/com.apple.networking.mDNSResponder. + asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder"); + LogMsg("---- BEGIN STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); udsserver_info(&mDNSStorage); - - LogMsgNoIdent("--------- Mach Clients ---------"); - if (!DNSServiceDomainEnumerationList && !DNSServiceBrowserList && !DNSServiceResolverList && !DNSServiceRegistrationList) - LogMsgNoIdent(""); - else - { - for (e = DNSServiceDomainEnumerationList; e; e=e->next) - LogMsgNoIdent("%5d: Mach DomainEnumeration %##s", e->ClientMachPort, e->dom.qname.c); - - for (b = DNSServiceBrowserList; b; b=b->next) - { - DNSServiceBrowserQuestion *qptr; - for (qptr = b->qlist; qptr; qptr = qptr->next) - LogMsgNoIdent("%5d: Mach ServiceBrowse %##s", b->ClientMachPort, qptr->q.qname.c); - } - - for (l = DNSServiceResolverList; l; l=l->next) - LogMsgNoIdent("%5d: Mach ServiceResolve %##s", l->ClientMachPort, l->i.name.c); - - for (r = DNSServiceRegistrationList; r; r=r->next) - { - ServiceInstance *si; - for (si = r->regs; si; si = si->next) - LogMsgNoIdent("%5d: Mach ServiceInstance %##s %u", si->ClientMachPort, si->srs.RR_SRV.resrec.name->c, mDNSVal16(si->srs.RR_SRV.resrec.rdata->u.srv.port)); - } - } + xpcserver_info(&mDNSStorage); LogMsgNoIdent("----- KQSocketEventSources -----"); if (!gEventSources) LogMsgNoIdent(""); @@ -1795,10 +1728,7 @@ mDNSlocal void INFOCallback(void) { KQSocketEventSource *k; for (k = gEventSources; k; k=k->next) - { - LogMsgNoIdent("%3d %s", k->fd, k->kqs.KQtask); - usleep((mDNSStorage.KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); - } + LogMsgNoIdent("%3d %s %s", k->fd, k->kqs.KQtask, k->fd == mDNSStorage.uds_listener_skt ? "Listener for incoming UDS clients" : " "); } LogMsgNoIdent("------ Network Interfaces ------"); @@ -1824,9 +1754,9 @@ mDNSlocal void INFOCallback(void) i->ifinfo.IPv4Available ? "v4" : " ", i->ifinfo.IPv4Available ? (mDNSv4Addr*)&i->ifa_v4addr : &zerov4Addr, i->ifinfo.IPv6Available ? "v6" : " ", - i->ifinfo.Advertise ? "⊙" : " ", - i->ifinfo.McastTxRx ? "⇆" : " ", - !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "☼" : "☀", + i->ifinfo.Advertise ? "A" : " ", + i->ifinfo.McastTxRx ? "M" : " ", + !(i->ifinfo.InterfaceActive && i->ifinfo.NetWake) ? " " : !sps[0] ? "p" : "P", &i->ifinfo.ip); if (sps[0]) LogMsgNoIdent(" %13d %#s", SPSMetric(sps[0]->resrec.rdata->u.name.c), sps[0]->resrec.rdata->u.name.c); @@ -1836,23 +1766,31 @@ mDNSlocal void INFOCallback(void) } } - LogMsgNoIdent("--------- DNS Servers ----------"); + LogMsgNoIdent("--------- DNS Servers(%d) ----------", NumUnicastDNSServers); if (!mDNSStorage.DNSServers) LogMsgNoIdent(""); else { for (s = mDNSStorage.DNSServers; s; s = s->next) { NetworkInterfaceInfoOSX *ifx = IfindexToInterfaceInfoOSX(&mDNSStorage, s->interface); - LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s", + LogMsgNoIdent("DNS Server %##s %s%s%#a:%d %d %s %d %d %s %s %s %s %s", s->domain.c, ifx ? ifx->ifinfo.ifname : "", ifx ? " " : "", &s->addr, mDNSVal16(s->port), - s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, s->scoped ? "Scoped" : "", + s->penaltyTime ? s->penaltyTime - mDNS_TimeNow(&mDNSStorage) : 0, DNSScopeToString(s->scoped), s->timeout, s->resGroupID, s->teststate == DNSServer_Untested ? "(Untested)" : s->teststate == DNSServer_Passed ? "" : s->teststate == DNSServer_Failed ? "(Failed)" : - s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)"); + s->teststate == DNSServer_Disabled ? "(Disabled)" : "(Unknown state)", + s->req_A ? "v4" : "!v4", + s->req_AAAA ? "v6" : "!v6", + s->cellIntf ? "cell" : "!cell", + s->DNSSECAware ? "DNSSECAware" : "!DNSSECAware"); } } + mDNSs32 now = mDNS_TimeNow(&mDNSStorage); + LogMsgNoIdent("v4answers %d", mDNSStorage.p->v4answers); + LogMsgNoIdent("v6answers %d", mDNSStorage.p->v6answers); + LogMsgNoIdent("Last DNS Trigger: %d ms ago", (now - mDNSStorage.p->DNSTrigger)); LogMsgNoIdent("--------- Mcast Resolvers ----------"); if (!mDNSStorage.McastResolvers) LogMsgNoIdent(""); @@ -1862,12 +1800,155 @@ mDNSlocal void INFOCallback(void) LogMsgNoIdent("Mcast Resolver %##s timeout %u", mr->domain.c, mr->timeout); } - mDNSs32 now = mDNS_TimeNow(&mDNSStorage); LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now); - LogMsg("---- END STATE LOG ---- %s %s %d", mDNSResponderVersionString, OSXVers ? "OSXVers" : "iOSVers", OSXVers ? OSXVers : iOSVers); + + // If logging is disabled, only then clear the key we set at the top of this func + if (!mDNS_LoggingEnabled) + asl_unset(log_msg, "LoggerID"); } +mDNSlocal void DebugSetFilter() +{ + if (!log_client) + return; + + // When USR1 is turned on, we log only the LOG_WARNING and LOG_NOTICE messages by default. + // The user has to manually do "syslog -c mDNSResponder -i" to get the LOG_INFO messages + // also to be logged. Most of the times, we need the INFO level messages for debugging. + // Hence, we set the filter to INFO level when USR1 logging is turned on to avoid + // having the user to do this extra step manually. + + if (mDNS_LoggingEnabled) + { + asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO)); + asl_set(log_msg, "LoggerID", "com.apple.networking.mDNSResponder"); + // Create LoggerID(Key)->com.apple.networking.mDNSResponder(Value) pair when USR1 Logging is Enabled. + // This key-value pair is used as a condition by syslogd to Log to com.apple.networking.mDNSResponder.log file + // present in /etc/asl/com.apple.networking.mDNSResponder. + } + else + { + asl_set_filter(log_client, ASL_FILTER_MASK_UPTO(ASL_LEVEL_ERR)); + asl_unset(log_msg, "LoggerID"); + // Clear the key-value pair when USR1 Logging is Disabled, as we do not want to log to + // com.apple.networking.mDNSResponder.log file in this case. + } +} + +mDNSexport void mDNSPlatformLogToFile(int log_level, const char *buffer) +{ + int asl_level = ASL_LEVEL_ERR; + + if (!log_client) + { + syslog(log_level, "%s", buffer); + return; + } + switch (log_level) + { + case LOG_ERR: + asl_level = ASL_LEVEL_ERR; + break; + case LOG_WARNING: + asl_level = ASL_LEVEL_WARNING; + break; + case LOG_NOTICE: + asl_level = ASL_LEVEL_NOTICE; + break; + case LOG_INFO: + asl_level = ASL_LEVEL_INFO; + break; + case LOG_DEBUG: + asl_level = ASL_LEVEL_DEBUG; + break; + default: + break; + } + asl_log(log_client, log_msg, asl_level, "%s", buffer); +} + +// Writes the state out to the dynamic store and also affects the ASL filter level +mDNSexport void UpdateDebugState() +{ + mDNSu32 one = 1; + mDNSu32 zero = 0; + + CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) + { + LogMsg("UpdateDebugState: Could not create dict"); + return; + } + + CFNumberRef numOne = CFNumberCreate(NULL, kCFNumberSInt32Type, &one); + if (!numOne) + { + LogMsg("UpdateDebugState: Could not create CFNumber one"); + return; + } + CFNumberRef numZero = CFNumberCreate(NULL, kCFNumberSInt32Type, &zero); + if (!numZero) + { + LogMsg("UpdateDebugState: Could not create CFNumber zero"); + CFRelease(numOne); + return; + } + + if (mDNS_LoggingEnabled) + CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("VerboseLogging"), numZero); + + if (mDNS_PacketLoggingEnabled) + CFDictionarySetValue(dict, CFSTR("PacketLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("PacketLogging"), numZero); + + if (mDNS_McastLoggingEnabled) + CFDictionarySetValue(dict, CFSTR("McastLogging"), numOne); + else + CFDictionarySetValue(dict, CFSTR("McastLogging"), numZero); + + if (mDNS_McastTracingEnabled) + CFDictionarySetValue(dict, CFSTR("McastTracing"), numOne); + else + CFDictionarySetValue(dict, CFSTR("McastTracing"), numZero); + + CFRelease(numOne); + CFRelease(numZero); + mDNSDynamicStoreSetConfig(kmDNSDebugState, mDNSNULL, dict); + CFRelease(dict); + // If we turned off USR1 logging, we need to reset the filter + DebugSetFilter(); +} + +#if TARGET_OS_EMBEDDED +mDNSlocal void Prefschanged() +{ + mDNSBool mDNSProf_installed; + LogMsg("Prefschanged: mDNSResponder Managed Preferences have changed"); + mDNSProf_installed = GetmDNSManagedPref(kmDNSEnableLoggingStr); + dispatch_async(dispatch_get_main_queue(), + ^{ + if (mDNSProf_installed) + { + mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 1; + } + else + { + LogMsg("Prefschanged: mDNSDebugProfile is uninstalled -> Turning OFF USR1/USR2 Logging with SIGINFO o/p"); + INFOCallback(); + mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = 0; + } + UpdateDebugState(); + // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log (has to be LogInfo) + LogInfo("Prefschanged: mDNSDebugProfile is installed -> Turned ON USR1/USR2 Logging"); + }); + return; +} +#endif //TARGET_OS_EMBEDDED + #ifndef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) @@ -1882,6 +1963,10 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void KQueueLock(m); switch(msg_header->msgh_id) { + case SIGURG: + m->mDNSOppCaching = m->mDNSOppCaching ? mDNSfalse : mDNStrue; + LogMsg("SIGURG: Opportunistic Caching %s", m->mDNSOppCaching ? "Enabled" : "Disabled"); + // FALL THROUGH to purge the cache so that we re-do the caching based on the new setting case SIGHUP: { mDNSu32 slot; CacheGroup *cg; @@ -1902,13 +1987,26 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; + UpdateDebugState(); + // If Logging Enabled: Start Logging to com.apple.networking.mDNSResponder.log + LogInfo("USR1 Logging Enabled: Start Logging to mDNSResponder Log file"); break; case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse; + LogInfo("SIGUSR2: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); break; - case SIGIO: - m->mDNSHandlePeerEvents = m->mDNSHandlePeerEvents ? mDNSfalse : mDNStrue; - LogMsg("SIGIO: Handle AWDL Peer Events %s", m->mDNSHandlePeerEvents ? "Enabled" : "Disabled"); + case SIGPROF: mDNS_McastLoggingEnabled = mDNS_McastLoggingEnabled ? mDNSfalse : mDNStrue; + LogMsg("SIGPROF: Multicast Logging %s", mDNS_McastLoggingEnabled ? "Enabled" : "Disabled"); + LogMcastStateInfo(m, mDNSfalse, mDNStrue, mDNStrue); + mDNS_McastTracingEnabled = (mDNS_PacketLoggingEnabled && mDNS_McastLoggingEnabled) ? mDNStrue : mDNSfalse; + LogMsg("SIGPROF: Multicast Tracing is %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + break; + case SIGTSTP: mDNS_LoggingEnabled = mDNS_PacketLoggingEnabled = mDNS_McastLoggingEnabled = mDNS_McastTracingEnabled = mDNSfalse; + LogMsg("All mDNSResponder Debug Logging/Tracing Disabled (USR1/USR2/PROF)"); + UpdateDebugState(); break; default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break; @@ -1916,34 +2014,15 @@ mDNSlocal void SignalCallback(CFMachPortRef port, void *msg, CFIndex size, void KQueueUnlock(m, "Unix Signal"); } -// On 10.2 the MachServerName is DNSServiceDiscoveryServer -// On 10.3 and later, the MachServerName is com.apple.mDNSResponder +// MachServerName is com.apple.mDNSResponder (Supported only till 10.9.x) mDNSlocal kern_return_t mDNSDaemonInitialize(void) { mStatus err; CFMachPortRef s_port; CFRunLoopSourceRef s_rls; CFRunLoopSourceRef d_rls; - - // If launchd already created our Mach port for us, then use that, else we create a new one of our own - if (m_port != MACH_PORT_NULL) - s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); - else - { - s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - m_port = CFMachPortGetPort(s_port); - kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port); - - if (status) - { - if (status == 1103) - LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running"); - else - LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status)); - return(status); - } - } - + + s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); CFMachPortRef d_port = CFMachPortCreate(NULL, ClientDeathCallback, NULL, NULL); err = mDNS_Init(&mDNSStorage, &PlatformStorage, @@ -1954,7 +2033,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) if (err) { LogMsg("Daemon start: mDNS_Init failed %d", err); return(err); } client_death_port = CFMachPortGetPort(d_port); - + s_rls = CFMachPortCreateRunLoopSource(NULL, s_port, 0); CFRunLoopAddSource(PlatformStorage.CFRunLoop, s_rls, kCFRunLoopDefaultMode); CFRelease(s_rls); @@ -2008,9 +2087,11 @@ mDNSlocal void SignalDispatch(dispatch_source_t source) case SIGUSR1: mDNS_LoggingEnabled = mDNS_LoggingEnabled ? 0 : 1; LogMsg("SIGUSR1: Logging %s", mDNS_LoggingEnabled ? "Enabled" : "Disabled"); WatchDogReportingThreshold = mDNS_LoggingEnabled ? 50 : 250; + UpdateDebugState(); break; case SIGUSR2: mDNS_PacketLoggingEnabled = mDNS_PacketLoggingEnabled ? 0 : 1; LogMsg("SIGUSR2: Packet Logging %s", mDNS_PacketLoggingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); break; default: LogMsg("SignalCallback: Unknown signal %d", sig); break; } @@ -2039,29 +2120,9 @@ mDNSlocal void mDNSSetupSignal(dispatch_queue_t queue, int sig) mDNSlocal kern_return_t mDNSDaemonInitialize(void) { mStatus err; - CFMachPortRef s_port; dispatch_source_t mach_source; dispatch_queue_t queue = dispatch_get_main_queue(); - // If launchd already created our Mach port for us, then use that, else we create a new one of our own - if (m_port != MACH_PORT_NULL) - s_port = CFMachPortCreateWithPort(NULL, m_port, DNSserverCallback, NULL, NULL); - else - { - s_port = CFMachPortCreate(NULL, DNSserverCallback, NULL, NULL); - m_port = CFMachPortGetPort(s_port); - kern_return_t status = bootstrap_register(bootstrap_port, "com.apple.mDNSResponder", m_port); - - if (status) - { - if (status == 1103) - LogMsg("bootstrap_register() failed: A copy of the daemon is apparently already running"); - else - LogMsg("bootstrap_register() failed: %d %X %s", status, status, mach_error_string(status)); - return(status); - } - } - err = mDNS_Init(&mDNSStorage, &PlatformStorage, rrcachestorage, RR_CACHE_SIZE, advertise, @@ -2083,7 +2144,7 @@ mDNSlocal kern_return_t mDNSDaemonInitialize(void) mDNSSetupSignal(queue, SIGINFO); mDNSSetupSignal(queue, SIGUSR1); mDNSSetupSignal(queue, SIGUSR2); - mDNSSetupSignal(queue, SIGIO); + mDNSSetupSignal(queue, SIGURG); // Create a custom handler for doing the housekeeping work. This is either triggered // by the timer or an event source @@ -2427,6 +2488,16 @@ mDNSlocal mDNSBool AllowSleepNow(mDNS *const m, mDNSs32 now) m->p->SleepCookie, ready ? "ready for sleep" : "giving up", now, m->SleepLimit - now); m->SleepLimit = 0; // Don't clear m->SleepLimit until after we've logged it above + m->TimeSlept = mDNSPlatformUTC(); + + // accumulate total time awake for this statistics gathering interval + if (m->StatStartTime) + { + m->ActiveStatTime += (m->TimeSlept - m->StatStartTime); + + // indicate this value is invalid until reinitialzed on wakeup + m->StatStartTime = 0; + } #if !TARGET_OS_EMBEDDED && defined(kIOPMAcknowledgmentOptionSystemCapabilityRequirements) if (m->p->IOPMConnection) IOPMConnectionAcknowledgeEventWithOptions(m->p->IOPMConnection, m->p->SleepCookie, opts); @@ -2668,21 +2739,16 @@ mDNSlocal void * KQueueLoop(void *m_param) mDNSlocal void LaunchdCheckin(void) { - launch_data_t msg = launch_data_new_string(LAUNCH_KEY_CHECKIN); - launch_data_t resp = launch_msg(msg); - launch_data_free(msg); - if (!resp) { LogMsg("launch_msg returned NULL"); return; } - - if (launch_data_get_type(resp) == LAUNCH_DATA_ERRNO) - { - int err = launch_data_get_errno(resp); - // When running on Tiger with "ServiceIPC = false", we get "err == EACCES" to tell us there's no launchdata to fetch - if (err != EACCES) LogMsg("launch_msg returned %d", err); - else LogInfo("Launchd provided no launchdata; will open Mach port and Unix Domain Socket explicitly...", err); + // Ask launchd for our socket + launch_data_t resp_sd = launch_socket_service_check_in(); + if (!resp_sd) + { + LogMsg("launch_socket_service_check_in returned NULL"); + return; } else { - launch_data_t skts = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_SOCKETS); + launch_data_t skts = launch_data_dict_lookup(resp_sd, LAUNCH_JOBKEY_SOCKETS); if (!skts) LogMsg("launch_data_dict_lookup LAUNCH_JOBKEY_SOCKETS returned NULL"); else { @@ -2719,58 +2785,29 @@ mDNSlocal void LaunchdCheckin(void) } } } - - launch_data_t ports = launch_data_dict_lookup(resp, "MachServices"); - if (!ports) LogMsg("launch_data_dict_lookup MachServices returned NULL"); - else - { - launch_data_t p = launch_data_dict_lookup(ports, "com.apple.mDNSResponder"); - if (!p) LogInfo("launch_data_dict_lookup(ports, \"com.apple.mDNSResponder\") returned NULL"); - else - { - m_port = launch_data_get_fd(p); - LogInfo("Launchd Mach Port: %d", m_port); - if (m_port == ~0U) m_port = MACH_PORT_NULL; - } - } } - launch_data_free(resp); + launch_data_free(resp_sd); } -mDNSlocal void DropPrivileges(void) +static mach_port_t RegisterMachService(const char *service_name) { - static const char login[] = "_mdnsresponder"; - struct passwd *pwd = getpwnam(login); - if (NULL == pwd) - LogMsg("Could not find account name \"%s\". Running as root.", login); - else - { - uid_t uid = pwd->pw_uid; - gid_t gid = pwd->pw_gid; - - LogMsg("Started as root. Switching to userid \"%s\".", login); - - if (unlink(MDNS_UDS_SERVERPATH) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, errno, strerror(errno)); - else - { - static char path[] = "/var/run/mdns/mDNSResponder"; - char *p = strrchr(path, '/'); - *p = '\0'; - if (mkdir(path, 0755) < 0 && errno != EEXIST) LogMsg("DropPrivileges: Could not create directory \"%s\": (%d) %s", path, errno, strerror(errno)); - else if (chown(path, uid, gid) < 0) LogMsg("DropPrivileges: Could not chown directory \"%s\": (%d) %s", path, errno, strerror(errno)); - else - { - *p = '/'; - if (unlink(path) < 0 && errno != ENOENT) LogMsg("DropPrivileges: Could not unlink \"%s\": (%d) %s", path, errno, strerror(errno)); - else if (symlink(path, MDNS_UDS_SERVERPATH) < 0) LogMsg("DropPrivileges: Could not symlink \"%s\" -> \"%s\": (%d) %s", MDNS_UDS_SERVERPATH, path, errno, strerror(errno)); - else LogInfo("DropPrivileges: Created subdirectory and symlink"); - } - } - - if (0 != initgroups(login, gid)) LogMsg("initgroups(\"%s\", %lu) failed. Continuing.", login, (unsigned long)gid); - if (0 != setgid(gid)) LogMsg("setgid(%lu) failed. Continuing with group %lu privileges.", (unsigned long)getegid()); - if (0 != setuid(uid)) LogMsg("setuid(%lu) failed. Continuing as root after all.", (unsigned long)uid); - } + mach_port_t port = MACH_PORT_NULL; + kern_return_t kr; + + if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) + { + LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr)); + return MACH_PORT_NULL; + } + + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) + { + LogMsg("RegisterMachService: %d %X %s", kr, kr, mach_error_string(kr)); + mach_port_deallocate(mach_task_self(), port); + return MACH_PORT_NULL; + } + + return port; } extern int sandbox_init(const char *profile, uint64_t flags, char **errorbuf) __attribute__((weak_import)); @@ -2795,13 +2832,15 @@ mDNSexport int main(int argc, char **argv) LogMsg("block wastage %d", 16*1024 - sizeof(CacheEntity) * RR_CACHE_SIZE); #endif - if (0 == geteuid()) DropPrivileges(); + if (0 == geteuid()) + { + LogMsg("mDNSResponder cannot be run as root !! Exiting.."); + return -1; + } for (i=1; i + + + + com.apple.mDNSResponder.dnsproxy + + + diff --git a/mDNSMacOSX/helper-error.h b/mDNSMacOSX/helper-error.h index f90d5ce..2e463b0 100644 --- a/mDNSMacOSX/helper-error.h +++ b/mDNSMacOSX/helper-error.h @@ -19,8 +19,6 @@ ERROR(kmDNSHelperCommunicationFailed, "Mach communication failed") ERROR(kmDNSHelperNotAuthorized, "Not authorized") ERROR(kmDNSHelperCreationFailed, "Object creation failed") ERROR(kmDNSHelperInvalidPList, "Invalid property list") -ERROR(kmDNSHelperDynamicStoreFailed, "Could not create dynamic store session") -ERROR(kmDNSHelperDynamicStoreSetFailed, "Could not set dynamic store configuration") ERROR(kmDNSHelperInvalidNameKey, "Invalid name key") ERROR(kmDNSHelperInvalidConfigKey, "Invalid configuration key") ERROR(kmDNSHelperTypeError, "Object was not of expected type") diff --git a/mDNSMacOSX/helper-main.c b/mDNSMacOSX/helper-main.c index d3610a4..52779e8 100644 --- a/mDNSMacOSX/helper-main.c +++ b/mDNSMacOSX/helper-main.c @@ -17,9 +17,6 @@ #define _FORTIFY_SOURCE 2 -// We set VERSION_MIN_REQUIRED to 10.4 to avoid "bootstrap_register is deprecated" warnings from bootstrap.h -#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4 - #include #include #include @@ -45,10 +42,7 @@ #include #if TARGET_OS_EMBEDDED -#include #define NO_SECURITYFRAMEWORK 1 - -#define bootstrap_register(A,B,C) bootstrap_register2((A),(B),(C),0) #endif #ifndef LAUNCH_JOBKEY_MACHSERVICES @@ -188,62 +182,25 @@ static int initialize_timer() return err; } -static mach_port_t checkin(char *service_name) -{ - kern_return_t kr = KERN_SUCCESS; - mach_port_t port = MACH_PORT_NULL; - launch_data_t msg = NULL, reply = NULL, datum = NULL; - - if (NULL == (msg = launch_data_new_string(LAUNCH_KEY_CHECKIN))) - { helplog(ASL_LEVEL_ERR, "Could not create checkin message for launchd."); goto fin; } - if (NULL == (reply = launch_msg(msg))) - { helplog(ASL_LEVEL_ERR, "Could not message launchd."); goto fin; } - if (LAUNCH_DATA_ERRNO == launch_data_get_type(reply)) - { - if (launch_data_get_errno(reply) == EACCES) { launch_data_free(msg); launch_data_free(reply); return(MACH_PORT_NULL); } - helplog(ASL_LEVEL_ERR, "Launchd checkin failed: %s.", strerror(launch_data_get_errno(reply))); goto fin; - } - if (NULL == (datum = launch_data_dict_lookup(reply, LAUNCH_JOBKEY_MACHSERVICES)) || LAUNCH_DATA_DICTIONARY != launch_data_get_type(datum)) - { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s dictionary.", LAUNCH_JOBKEY_MACHSERVICES); goto fin; } - if (NULL == (datum = launch_data_dict_lookup(datum, service_name)) || LAUNCH_DATA_MACHPORT != launch_data_get_type(datum)) - { helplog(ASL_LEVEL_ERR, "Launchd reply does not contain %s Mach port.", service_name); goto fin; } - if (MACH_PORT_NULL == (port = launch_data_get_machport(datum))) - { helplog(ASL_LEVEL_ERR, "Launchd gave me a null Mach port."); goto fin; } - if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) - { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto fin; } - -fin: - if (NULL != msg) launch_data_free(msg); - if (NULL != reply) launch_data_free(reply); - if (MACH_PORT_NULL == port) exit(EXIT_FAILURE); - return port; -} - static mach_port_t register_service(const char *service_name) { mach_port_t port = MACH_PORT_NULL; kern_return_t kr; - if (KERN_SUCCESS == (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) + if (KERN_SUCCESS != (kr = bootstrap_check_in(bootstrap_port, (char *)service_name, &port))) { - if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) - helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); - else - return port; + helplog(ASL_LEVEL_ERR, "bootstrap_check_in: %d %X %s", kr, kr, mach_error_string(kr)); + return MACH_PORT_NULL; } - if (KERN_SUCCESS != (kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port))) - { helplog(ASL_LEVEL_ERR, "mach_port_allocate: %d %X %s", kr, kr, mach_error_string(kr)); goto error; } + if (KERN_SUCCESS != (kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND))) - { helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); goto error; } - - // XXX bootstrap_register does not modify its second argument, but the prototype does not include const. - if (KERN_SUCCESS != (kr = bootstrap_register(bootstrap_port, (char *)service_name, port))) - { helplog(ASL_LEVEL_ERR, "bootstrap_register failed: %s %d %X %s", service_name, kr, kr, mach_error_string(kr)); goto error; } + { + helplog(ASL_LEVEL_ERR, "mach_port_insert_right: %d %X %s", kr, kr, mach_error_string(kr)); + mach_port_deallocate(mach_task_self(), port); + return MACH_PORT_NULL; + } return port; -error: - if (MACH_PORT_NULL != port) mach_port_deallocate(mach_task_self(), port); - return MACH_PORT_NULL; } int main(int ac, char *av[]) @@ -281,12 +238,9 @@ int main(int ac, char *av[]) // Explicitly ensure that our Keychain operations utilize the system domain. if (opt_debug) SecKeychainSetPreferenceDomain(kSecPreferencesDomainSystem); #endif - gPort = checkin(kmDNSHelperServiceName); + gPort = register_service(kmDNSHelperServiceName); if (!gPort) - { - helplog(ASL_LEVEL_ERR, "Launchd provided no launchdata; will open Mach port explicitly"); - gPort = register_service(kmDNSHelperServiceName); - } + exit(EXIT_FAILURE); if (maxidle) actualidle = maxidle; diff --git a/mDNSMacOSX/helper-stubs.c b/mDNSMacOSX/helper-stubs.c index 9b1afaf..29fd9ae 100644 --- a/mDNSMacOSX/helper-stubs.c +++ b/mDNSMacOSX/helper-stubs.c @@ -23,6 +23,23 @@ #include "mDNSDebug.h" #include "helper.h" #include "helpermsg.h" +#include +#include + +// +// Implementation Notes about the HelperQueue: +// +// To prevent blocking the main queue, all communications with mDNSResponderHelper should happen on +// HelperQueue. There are a few calls which are still synchronous and needs to be handled separately +// case by case. +// +// When spawning off the work to the HelperQueue, any arguments that are pointers need to be copied +// explicitly as they may cease to exist after the call returns. From within the block that is scheduled, +// arrays defined on the stack can't be referenced and hence it is enclosed them in a struct. If the array is +// an argument to the function, the blocks can reference them as they are passed in as pointers. But care should +// be taken to copy them locally as they may cease to exist when the function returns. +// +static dispatch_queue_t HelperQueue; #define ERROR(x, y) y, static const char *errorstring[] = @@ -32,6 +49,17 @@ static const char *errorstring[] = }; #undef ERROR +mDNSexport mStatus mDNSHelperInit() +{ + HelperQueue = dispatch_queue_create("com.apple.mDNSResponder.HelperQueue", NULL); + if (HelperQueue == NULL) + { + LogMsg("dispatch_queue_create: Helper queue NULL"); + return mStatus_NoMemoryErr; + } + return mStatus_NoError; +} + static mach_port_t getHelperPort(int retry) { static mach_port_t port = MACH_PORT_NULL; @@ -71,71 +99,45 @@ const char *mDNSHelperError(int err) void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new) { - kern_return_t kr = KERN_FAILURE; - int retry = 0; - int err = 0; - char oldname[MAX_DOMAIN_LABEL+1] = {0}; - char newname[MAX_DOMAIN_LABEL+1] = {0}; - ConvertDomainLabelToCString_unescaped(old, oldname); - if (new) ConvertDomainLabelToCString_unescaped(new, newname); + struct { + char oldname[MAX_DOMAIN_LABEL+1]; + char newname[MAX_DOMAIN_LABEL+1]; + } names; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, oldname, newname); - MACHRETRYLOOP_END(kr, retry, err, fin); + mDNSPlatformMemZero(names.oldname, MAX_DOMAIN_LABEL + 1); + mDNSPlatformMemZero(names.newname, MAX_DOMAIN_LABEL + 1); -fin: - (void)err; -} + ConvertDomainLabelToCString_unescaped(old, names.oldname); + if (new) ConvertDomainLabelToCString_unescaped(new, names.newname); + dispatch_async(HelperQueue, ^{ -void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value) -{ - CFWriteStreamRef stream = NULL; - CFDataRef bytes = NULL; - kern_return_t kr = KERN_FAILURE; - int retry = 0; - int err = 0; + kern_return_t kr = KERN_FAILURE; + int retry = 0; + int err = 0; - if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL))) - { - err = kmDNSHelperCreationFailed; - LogMsg("%s: CFWriteStreamCreateWithAllocatedBuffers failed", __func__); - goto fin; - } - CFWriteStreamOpen(stream); - if (0 == CFPropertyListWriteToStream(value, stream, kCFPropertyListBinaryFormat_v1_0, NULL)) - { - err = kmDNSHelperPListWriteFailed; - LogMsg("%s: CFPropertyListWriteToStream failed", __func__); - goto fin; - } - if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten))) - { - err = kmDNSHelperCreationFailed; - LogMsg("%s: CFWriteStreamCopyProperty failed", __func__); - goto fin; - } - CFWriteStreamClose(stream); - CFRelease(stream); - stream = NULL; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSDynamicStoreSetConfig(getHelperPort(retry), key, subkey ? subkey : "", (vm_offset_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes)); - MACHRETRYLOOP_END(kr, retry, err, fin); + LogInfo("%s: oldname %s newname %s", __func__, names.oldname, names.newname); + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSPreferencesSetName(getHelperPort(retry), key, names.oldname, names.newname); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - if (NULL != stream) { CFWriteStreamClose(stream); CFRelease(stream); } - if (NULL != bytes) CFRelease(bytes); - (void)err; + (void)err; + }); } void mDNSRequestBPF(void) { - kern_return_t kr = KERN_FAILURE; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSRequestBPF(getHelperPort(retry)); - MACHRETRYLOOP_END(kr, retry, err, fin); + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + LogInfo("%s: BPF", __func__); + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSRequestBPF(getHelperPort(retry)); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; + (void)err; + }); } int mDNSPowerRequest(int key, int interval) @@ -162,13 +164,51 @@ fin: void mDNSNotify(const char *title, const char *msg) // Both strings are UTF-8 text { - kern_return_t kr = KERN_FAILURE; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSNotify(getHelperPort(retry), title, msg); - MACHRETRYLOOP_END(kr, retry, err, fin); + char *titleCopy = NULL; + char *msgCopy = NULL; + + if (title) + { + int len = strlen(title); + titleCopy = mDNSPlatformMemAllocate(len + 1); + if (!titleCopy) + { + LogMsg("mDNSNotify: titleCopy NULL for %s", msg); + return; + } + mDNSPlatformMemCopy(titleCopy, title, len); + titleCopy[len] = 0; + } + if (msg) + { + int len = strlen(msg); + msgCopy = mDNSPlatformMemAllocate(len + 1); + if (!msgCopy) + { + LogMsg("mDNSNotify: msgCopy NULL for %s", msg); + return; + } + mDNSPlatformMemCopy(msgCopy, msg, len); + msgCopy[len] = 0; + } + + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + + LogInfo("%s: title %s, msg %s", __func__, titleCopy, msgCopy); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSNotify(getHelperPort(retry), titleCopy, msgCopy); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; + if (titleCopy) + mDNSPlatformMemFree(titleCopy); + if (msgCopy) + mDNSPlatformMemFree(msgCopy); + (void)err; + }); } int mDNSKeychainGetSecrets(CFArrayRef *result) @@ -215,19 +255,34 @@ fin: void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn) { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10] = { 0 }; // Assume the prefix is no larger than 10 chars + struct + { + // Assume the prefix is no larger than 10 chars + char fqdnStr[MAX_ESCAPED_DOMAIN_NAME + 10]; + } name; + + mDNSPlatformMemZero(name.fqdnStr, MAX_DOMAIN_LABEL + 10); + if (fqdn) { - mDNSPlatformStrCopy(fqdnStr, prefix); - ConvertDomainNameToCString(fqdn, fqdnStr + mDNSPlatformStrLen(prefix)); + mDNSPlatformStrCopy(name.fqdnStr, prefix); + ConvertDomainNameToCString(fqdn, name.fqdnStr + mDNSPlatformStrLen(prefix)); } - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, fqdnStr); - MACHRETRYLOOP_END(kr, retry, err, fin); + + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + + LogInfo("%s: fqdnStr %s", __func__, name.fqdnStr); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSConfigureServer(getHelperPort(retry), updown, name.fqdnStr); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void)err; + (void)err; + + }); } int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner, @@ -251,47 +306,191 @@ fin: void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration) { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr, ip_addr, iteration); - MACHRETRYLOOP_END(kr, retry, err, fin); + char *ip_addr_copy = NULL; + char *eth_addr_copy = NULL; + + if (eth_addr) + { + int len = strlen(eth_addr); + eth_addr_copy = mDNSPlatformMemAllocate(len + 1); + if (!eth_addr_copy) + { + LogMsg("mDNSSendWakeupPacket: eth_addr_copy NULL for %s", eth_addr); + return; + } + mDNSPlatformMemCopy(eth_addr_copy, eth_addr, len); + eth_addr_copy[len] = 0; + } + if (ip_addr) + { + int len = strlen(ip_addr); + ip_addr_copy = mDNSPlatformMemAllocate(len + 1); + if (!ip_addr_copy) + { + LogMsg("mDNSSendWakeupPacket: ip_addr_copy NULL for %s", ip_addr); + return; + } + mDNSPlatformMemCopy(ip_addr_copy, ip_addr, len); + ip_addr_copy[len] = 0; + } + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + + LogInfo("%s: Entered ethernet address %s, ip address %s", __func__, eth_addr_copy, ip_addr_copy); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSSendWakeupPacket(getHelperPort(retry), ifid, eth_addr_copy, ip_addr_copy, iteration); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void) err; + if (eth_addr_copy) + mDNSPlatformMemFree(eth_addr_copy); + if (ip_addr_copy) + mDNSPlatformMemFree(ip_addr_copy); + (void) err; + }); } void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray) { - kern_return_t kr = KERN_SUCCESS; - int retry = 0, err = 0; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifname, count, portArray, protocolArray); - MACHRETRYLOOP_END(kr, retry, err, fin); + struct + { + pfArray_t portArray; + pfArray_t protocolArray; + } pfa; + char *ifnameCopy = NULL; + + mDNSPlatformMemCopy(pfa.portArray, portArray, sizeof(pfArray_t)); + mDNSPlatformMemCopy(pfa.protocolArray, protocolArray, sizeof(pfArray_t)); + if (ifname) + { + int len = strlen(ifname); + ifnameCopy = mDNSPlatformMemAllocate(len + 1); + if (!ifnameCopy) + { + LogMsg("mDNSPacketFilterControl: ifnameCopy NULL"); + return; + } + mDNSPlatformMemCopy(ifnameCopy, ifname, len); + ifnameCopy[len] = 0; + } + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_SUCCESS; + int retry = 0, err = 0; + + LogInfo("%s, ifname %s", __func__, ifnameCopy); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSPacketFilterControl(getHelperPort(retry), command, ifnameCopy, count, (uint16_t *)pfa.portArray, (uint16_t *)pfa.protocolArray); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + if (ifnameCopy) + mDNSPlatformMemFree(ifnameCopy); + (void) err; + }); +} + +void mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win) +{ + struct + { + v6addr_t sadd; + v6addr_t dadd; + } addr; + + mDNSPlatformMemCopy(addr.sadd, sadd, sizeof(v6addr_t)); + mDNSPlatformMemCopy(addr.dadd, dadd, sizeof(v6addr_t)); + + dispatch_async(HelperQueue, ^{ + + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + char buf1[INET6_ADDRSTRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + buf1[0] = 0; + buf2[0] = 0; + + inet_ntop(AF_INET6, addr.sadd, buf1, sizeof(buf1)); + inet_ntop(AF_INET6, addr.dadd, buf2, sizeof(buf2)); + LogInfo("%s: sadd is %s, dadd is %s", __func__, buf1, buf2); + + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSSendKeepalive(getHelperPort(retry), (uint8_t *)addr.sadd, (uint8_t *)addr.dadd, lport, rport, seq, ack, win); + MACHRETRYLOOP_END(kr, retry, err, fin); fin: - (void) err; + (void) err; + }); } -int mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win) +int mDNSRetrieveTCPInfo(int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid) { kern_return_t kr = KERN_FAILURE; int retry = 0, err = 0; MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSSendKeepalive(getHelperPort(retry), sadd, dadd, lport, rport, seq, ack, win); + kr = proxy_mDNSRetrieveTCPInfo(getHelperPort(retry), family, (uint8_t *)laddr, lport, (uint8_t *)raddr, rport, seq, ack, win, intfid); MACHRETRYLOOP_END(kr, retry, err, fin); fin: return err; } - -int mDNSInterfaceAdvtIoctl(const char *ifname, int op) +void mDNSGetRemoteMAC(mDNS *const m, int family, v6addr_t raddr) { - kern_return_t kr = KERN_FAILURE; - int retry = 0, err = 0; + struct { + v6addr_t addr; + } dst; - MACHRETRYLOOP_BEGIN(kr, retry, err, fin); - kr = proxy_mDNSInterfaceAdvtIoctl(getHelperPort(retry), ifname, op); - MACHRETRYLOOP_END(kr, retry, err, fin); + mDNSPlatformMemCopy(dst.addr, raddr, sizeof(v6addr_t)); + dispatch_async(HelperQueue, ^{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + ethaddr_t eth; + IPAddressMACMapping *addrMapping; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSGetRemoteMAC(getHelperPort(retry), family, (uint8_t *)dst.addr, eth); + MACHRETRYLOOP_END(kr, retry, err, fin); + // If the call to get the remote MAC address succeeds, allocate and copy + // the values and schedule a task to update the MAC address in the TCP Keepalive record. + if (kr == KERN_SUCCESS) + { + addrMapping = (IPAddressMACMapping *)malloc(sizeof(IPAddressMACMapping)); + snprintf(addrMapping->ethaddr, sizeof(addrMapping->ethaddr), "%02x:%02x:%02x:%02x:%02x:%02x", + eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + if (family == AF_INET) + { + addrMapping->ipaddr.type = mDNSAddrType_IPv4; + mDNSPlatformMemCopy(addrMapping->ipaddr.ip.v4.b, dst.addr, sizeof(v6addr_t)); + } + else + { + addrMapping->ipaddr.type = mDNSAddrType_IPv6; + mDNSPlatformMemCopy(addrMapping->ipaddr.ip.v6.b, dst.addr, sizeof(v6addr_t)); + } + mDNSPlatformDispatchAsync(m, addrMapping, UpdateRMACCallback); + } fin: - return err; + (void) err; + }); + +} + +void mDNSStoreSPSMACAddress(int family, v6addr_t spsaddr, char *ifname) +{ + struct { + v6addr_t saddr; + } addr; + mDNSPlatformMemCopy(addr.saddr, spsaddr, sizeof(v6addr_t)); + + dispatch_async(HelperQueue, ^{ + kern_return_t kr = KERN_FAILURE; + int retry = 0, err = 0; + MACHRETRYLOOP_BEGIN(kr, retry, err, fin); + kr = proxy_mDNSStoreSPSMACAddress(getHelperPort(retry), family, (uint8_t *)addr.saddr, ifname); + MACHRETRYLOOP_END(kr, retry, err, fin); +fin: + (void)err; + }); } diff --git a/mDNSMacOSX/helper.c b/mDNSMacOSX/helper.c index c1f2497..813ab52 100644 --- a/mDNSMacOSX/helper.c +++ b/mDNSMacOSX/helper.c @@ -42,12 +42,11 @@ #include #include #include -#include #include -#include #include #include #include +#include #include "mDNSEmbeddedAPI.h" #include "dns_sd.h" @@ -61,7 +60,6 @@ #include #include -#include #ifndef RTF_IFSCOPE #define RTF_IFSCOPE 0x1000000 @@ -150,44 +148,6 @@ kern_return_t do_mDNSRequestBPF(__unused mach_port_t port, audit_token_t token) return KERN_SUCCESS; } -kern_return_t do_mDNSInterfaceAdvtIoctl(__unused mach_port_t port, const char *ifname, int op, audit_token_t token) -{ - struct in6_ndireq nd; - mDNSu32 newflags; - int sock; - - if (!authorized(&token)) - { - return KERN_SUCCESS; - } - - if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) - { - helplog(ASL_LEVEL_ERR, "%s: Socket call failed - error (%d) %s", __func__, errno, strerror(errno)); - return errno; - } - memset(&nd, 0, sizeof(nd)); - strlcpy(nd.ifname, ifname, sizeof(nd.ifname)); - - if (ioctl(sock, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) - { - helplog(ASL_LEVEL_ERR, "%s: ioctl call SIOCGIFINFO_IN6 failed - error (%d) %s", __func__, errno, strerror(errno)); - close(sock); - return errno; - } - newflags = nd.ndi.flags; - newflags = (op == 0) ? (newflags | ND6_IFF_IGNORE_NA) : (newflags & ~(ND6_IFF_IGNORE_NA)); - - if (ioctl(sock, SIOCSIFINFO_FLAGS, (caddr_t)&nd) < 0) - { - helplog(ASL_LEVEL_ERR, "%s: ioctl call SIOCSIFINFO_IN6 failed - error (%d) %s", __func__, errno, strerror(errno)); - close(sock); - return errno; - } - close(sock); - return KERN_SUCCESS; -} - kern_return_t do_mDNSPowerRequest(__unused mach_port_t port, int key, int interval, int *err, audit_token_t token) { *err = -1; @@ -393,86 +353,6 @@ kern_return_t do_mDNSNotify(__unused mach_port_t port, const char *title, const return KERN_SUCCESS; } -kern_return_t -do_mDNSDynamicStoreSetConfig(__unused mach_port_t port, int key, - const char* subkey, vm_offset_t value, mach_msg_type_number_t valueCnt, - audit_token_t token) -{ - CFStringRef sckey = NULL; - Boolean release_sckey = FALSE; - CFDataRef bytes = NULL; - CFPropertyListRef plist = NULL; - SCDynamicStoreRef store = NULL; - - debug("entry"); - if (!authorized(&token)) goto fin; - - switch ((enum mDNSDynamicStoreSetConfigKey)key) - { - case kmDNSMulticastConfig: - sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS); - break; - case kmDNSDynamicConfig: - sckey = CFSTR("State:/Network/DynamicDNS"); - break; - case kmDNSPrivateConfig: - sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS); - break; - case kmDNSBackToMyMacConfig: - sckey = CFSTR("State:/Network/BackToMyMac"); - break; - case kmDNSSleepProxyServersState: - { - CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0); - CFStringAppend(tmp, CFSTR("State:/Network/Interface/")); - CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8); - CFStringAppend(tmp, CFSTR("/SleepProxyServers")); - sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp); - release_sckey = TRUE; - CFRelease(tmp); - break; - } - default: - debug("unrecognized key %d", key); - goto fin; - } - if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value, - valueCnt, kCFAllocatorNull))) - { - debug("CFDataCreateWithBytesNoCopy of value failed"); - goto fin; - } - if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes, - kCFPropertyListImmutable, NULL))) - { - debug("CFPropertyListCreateFromXMLData of bytes failed"); - goto fin; - } - CFRelease(bytes); - bytes = NULL; - if (NULL == (store = SCDynamicStoreCreate(NULL, - CFSTR(kmDNSHelperServiceName), NULL, NULL))) - { - debug("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - goto fin; - } - SCDynamicStoreSetValue(store, sckey, plist); - debug("succeeded"); - -fin: - if (NULL != bytes) - CFRelease(bytes); - if (NULL != plist) - CFRelease(plist); - if (NULL != store) - CFRelease(store); - if (release_sckey && sckey) - CFRelease(sckey); - vm_deallocate(mach_task_self(), value, valueCnt); - update_idle_timer(); - return KERN_SUCCESS; -} - char usercompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name the user saw char userhostname[MAX_DOMAIN_LABEL+1] = {0}; // the last local host name the user saw char lastcompname[MAX_DOMAIN_LABEL+1] = {0}; // the last computer name saved to preferences @@ -1365,6 +1245,13 @@ createAnonymousRacoonConfiguration(const char *fqdn) " proposal_check claim;\n" " proposal {\n" " encryption_algorithm aes;\n" + " hash_algorithm sha256;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + " proposal {\n" + " encryption_algorithm aes;\n" " hash_algorithm sha1;\n" " authentication_method pre_shared_key;\n" " dh_group 2;\n" @@ -1375,7 +1262,7 @@ createAnonymousRacoonConfiguration(const char *fqdn) " pfs_group 2;\n" " lifetime time 10 min;\n" " encryption_algorithm aes;\n" - " authentication_algorithm hmac_sha1;\n" + " authentication_algorithm hmac_sha256,hmac_sha1;\n" " compression_algorithm deflate;\n" "}\n"; char tmp_config_path[64]; @@ -1591,7 +1478,7 @@ startRacoon(void) } u_int32_t btmm_cookie = 0x4d4d5442; - vpnctl_hdr h = { VPNCTL_CMD_PING, 0, btmm_cookie, 0, 0, 0 }; + vpnctl_hdr h = { htons(VPNCTL_CMD_PING), 0, btmm_cookie, 0, 0, 0 }; size_t bytes = 0; ssize_t ret = 0; @@ -2114,6 +2001,13 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete, " proposal_check claim;\n" " proposal {\n" " encryption_algorithm aes;\n" + " hash_algorithm sha256;\n" + " authentication_method pre_shared_key;\n" + " dh_group 2;\n" + " lifetime time 15 min;\n" + " }\n" + " proposal {\n" + " encryption_algorithm aes;\n" " hash_algorithm sha1;\n" " authentication_method pre_shared_key;\n" " dh_group 2;\n" @@ -2124,14 +2018,14 @@ do_mDNSAutoTunnelSetKeys(__unused mach_port_t port, int replacedelete, " pfs_group 2;\n" " lifetime time 10 min;\n" " encryption_algorithm aes;\n" - " authentication_algorithm hmac_sha1;\n" + " authentication_algorithm hmac_sha256,hmac_sha1;\n" " compression_algorithm deflate;\n" "}\n\n" "sainfo address %s any address %s any {\n" " pfs_group 2;\n" " lifetime time 10 min;\n" " encryption_algorithm aes;\n" - " authentication_algorithm hmac_sha1;\n" + " authentication_algorithm hmac_sha256,hmac_sha1;\n" " compression_algorithm deflate;\n" "}\n"; char path[PATH_MAX] = ""; @@ -2683,3 +2577,268 @@ again: close(sock); return KERN_SUCCESS; } + + +kern_return_t do_mDNSRetrieveTCPInfo(__unused mach_port_t port, int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, + uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid, audit_token_t token) +{ + struct tcp_info ti; + struct info_tuple itpl; + int mib[4]; + unsigned int miblen; + size_t len; + size_t sz; + + memset(&itpl, 0, sizeof(struct info_tuple)); + memset(&ti, 0, sizeof(struct tcp_info)); + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSRetrieveTCPInfo: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + if (family == AF_INET) + { + memcpy(&itpl.itpl_local_sin.sin_addr, laddr, sizeof(struct in_addr)); + memcpy(&itpl.itpl_remote_sin.sin_addr, raddr, sizeof(struct in_addr)); + itpl.itpl_local_sin.sin_port = lport; + itpl.itpl_remote_sin.sin_port = rport; + itpl.itpl_local_sin.sin_family = AF_INET; + itpl.itpl_remote_sin.sin_family = AF_INET; + } + else + { + memcpy(&itpl.itpl_local_sin6.sin6_addr, laddr, sizeof(struct in6_addr)); + memcpy(&itpl.itpl_remote_sin6.sin6_addr, raddr, sizeof(struct in6_addr)); + itpl.itpl_local_sin6.sin6_port = lport; + itpl.itpl_remote_sin6.sin6_port = rport; + itpl.itpl_local_sin6.sin6_family = AF_INET6; + itpl.itpl_remote_sin6.sin6_family = AF_INET6; + } + itpl.itpl_proto = IPPROTO_TCP; + sz = sizeof(mib)/sizeof(mib[0]); + if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1) + { + helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno)); + return errno; + } + miblen = (unsigned int)sz; + len = sizeof(struct tcp_info); + if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1) + { + helplog(ASL_LEVEL_ERR, "do_RetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno)); + return errno; + } + + *seq = ti.tcpi_snd_nxt - 1; + *ack = ti.tcpi_rcv_nxt; + *win = ti.tcpi_rcv_space >> ti.tcpi_rcv_wscale; + *intfid = ti.tcpi_last_outif; + return KERN_SUCCESS; +} + +static int getMACAddress(int family, v6addr_t raddr, v6addr_t gaddr, int *gfamily, ethaddr_t eth) +{ + struct + { + struct rt_msghdr m_rtm; + char m_space[512]; + } m_rtmsg; + + struct rt_msghdr *rtm = &(m_rtmsg.m_rtm); + char *cp = m_rtmsg.m_space; + int seq = 6367, sock, rlen, i; + struct sockaddr_in *sin = NULL; + struct sockaddr_in6 *sin6 = NULL; + struct sockaddr_dl *sdl = NULL; + struct sockaddr_storage sins; + struct sockaddr_dl sdl_m; + +#define NEXTADDR(w, s, len) \ + if (rtm->rtm_addrs & (w)) \ + { \ + bcopy((char *)s, cp, len); \ + cp += len; \ + } + + bzero(&sins, sizeof(struct sockaddr_storage)); + bzero(&sdl_m, sizeof(struct sockaddr_dl)); + bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); + + sock = socket(PF_ROUTE, SOCK_RAW, 0); + if (sock < 0) + { + helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Can not open the socket - %s", strerror(errno)); + return errno; + } + + rtm->rtm_addrs |= RTA_DST | RTA_GATEWAY; + rtm->rtm_type = RTM_GET; + rtm->rtm_flags = 0; + rtm->rtm_version = RTM_VERSION; + rtm->rtm_seq = ++seq; + + sdl_m.sdl_len = sizeof(sdl_m); + sdl_m.sdl_family = AF_LINK; + if (family == AF_INET) + { + sin = (struct sockaddr_in*)&sins; + sin->sin_family = AF_INET; + sin->sin_len = sizeof(struct sockaddr_in); + memcpy(&sin->sin_addr, raddr, sizeof(struct in_addr)); + NEXTADDR(RTA_DST, sin, sin->sin_len); + } + else if (family == AF_INET6) + { + sin6 = (struct sockaddr_in6 *)&sins; + sin6->sin6_len = sizeof(struct sockaddr_in6); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, raddr, sizeof(struct in6_addr)); + NEXTADDR(RTA_DST, sin6, sin6->sin6_len); + } + NEXTADDR(RTA_GATEWAY, &sdl_m, sdl_m.sdl_len); + rtm->rtm_msglen = rlen = cp - (char *)&m_rtmsg; + + if (write(sock, (char *)&m_rtmsg, rlen) < 0) + { + helplog(ASL_LEVEL_INFO, "do_mDNSGetRemoteMAC: writing to routing socket: %s", strerror(errno)); + close(sock); + return errno; + } + + do + { + rlen = read(sock, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } + while (rlen > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != getpid())); + + if (rlen < 0) + helplog(ASL_LEVEL_ERR, "do_mDNSGetRemoteMAC: Read from routing socket failed"); + + if (family == AF_INET) + { + sin = (struct sockaddr_in *) (rtm + 1); + sdl = (struct sockaddr_dl *) (sin->sin_len + (char *) sin); + } + else if (family == AF_INET6) + { + sin6 = (struct sockaddr_in6 *) (rtm +1); + sdl = (struct sockaddr_dl *) (sin6->sin6_len + (char *) sin6); + } + // If the address is not on the local net, we get the IP address of the gateway. + // We would have to repeat the process to get the MAC address of the gateway + *gfamily = sdl->sdl_family; + if (sdl->sdl_family == AF_INET) + { + struct sockaddr_in *new_sin = (struct sockaddr_in *)(sin->sin_len +(char*) sin); + memcpy(gaddr, &new_sin->sin_addr, sizeof(struct in_addr)); + close(sock); + return -1; + } + else if (sdl->sdl_family == AF_INET6) + { + struct sockaddr_in6 *new_sin6 = (struct sockaddr_in6 *)(sin6->sin6_len +(char*) sin6); + memcpy(gaddr, &new_sin6->sin6_addr, sizeof(struct in6_addr)); + close(sock); + return -1; + } + + unsigned char *ptr = (unsigned char *)LLADDR(sdl); + for (i = 0; i < ETHER_ADDR_LEN; i++) + (eth)[i] = *(ptr +i); + + close(sock); + return KERN_SUCCESS; +} + +kern_return_t do_mDNSGetRemoteMAC(__unused mach_port_t port, int family, v6addr_t raddr, ethaddr_t eth, audit_token_t token) +{ + int ret = 0; + v6addr_t gateway; + int gfamily; + int count = 0; + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSGetRemoteMAC: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + do + { + ret = getMACAddress(family, raddr, gateway, &gfamily, eth); + if (ret == -1) + { + memcpy(raddr, gateway, sizeof(family)); + family = gfamily; + count++; + } + } + while ((ret == -1) && (count < 5)); + return ret; +} + + +kern_return_t do_mDNSStoreSPSMACAddress(__unused mach_port_t port, int family, v6addr_t spsaddr, const char *ifname, audit_token_t token) +{ + ethaddr_t eth; + char spsip[INET6_ADDRSTRLEN]; + int ret = 0; + CFStringRef sckey = NULL; + SCDynamicStoreRef store = NULL; + CFMutableDictionaryRef dict = NULL; + + if (!authorized(&token)) + { + helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Not authorized"); + return kmDNSHelperNotAuthorized; + } + + // Get the MAC address of the Sleep Proxy Server + memset(eth, 0, sizeof(eth)); + ret = do_mDNSGetRemoteMAC(port, family, spsaddr, eth, token); + if (ret != KERN_SUCCESS) + { + helplog(ASL_LEVEL_ERR, "mDNSStoreSPSMAC: Failed to determine the MAC address"); + goto fin; + } + + // Create/Update the dynamic store entry for the specified interface + sckey = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s%s"), "State:/Network/Interface/", ifname, "/BonjourSleepProxyAddress"); + dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (!dict) + { + helplog(ASL_LEVEL_ERR, "SPSCreateDict: Could not create CFDictionary dict"); + ret = KERN_FAILURE; + goto fin; + } + + CFStringRef macaddr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%02x:%02x:%02x:%02x:%02x:%02x"), eth[0], eth[1], eth[2], eth[3], eth[4], eth[5]); + CFDictionarySetValue(dict, CFSTR("MACAddress"), macaddr); + CFRelease(macaddr); + + if( NULL == inet_ntop(family, (void *)spsaddr, spsip, sizeof(spsip))) + { + helplog(ASL_LEVEL_ERR, "inet_ntop failed: %s", strerror(errno)); + ret = kmDNSHelperInvalidNetworkAddress; + goto fin; + } + + CFStringRef ipaddr = CFStringCreateWithCString(NULL, spsip, kCFStringEncodingUTF8); + CFDictionarySetValue(dict, CFSTR("IPAddress"), ipaddr); + CFRelease(ipaddr); + + SCDynamicStoreSetValue(store, sckey, dict); + +fin: + if (NULL != store) + CFRelease(store); + if (NULL != sckey) + CFRelease(sckey); + if (NULL != dict) + CFRelease(dict); + + update_idle_timer(); + return ret; +} diff --git a/mDNSMacOSX/helper.h b/mDNSMacOSX/helper.h index c7dabc5..a298237 100644 --- a/mDNSMacOSX/helper.h +++ b/mDNSMacOSX/helper.h @@ -20,15 +20,6 @@ #define kmDNSHelperServiceName "com.apple.mDNSResponderHelper" -enum mDNSDynamicStoreSetConfigKey -{ - kmDNSMulticastConfig = 1, - kmDNSDynamicConfig, - kmDNSPrivateConfig, - kmDNSBackToMyMacConfig, - kmDNSSleepProxyServersState -}; - enum mDNSPreferencesSetNameKey { kmDNSComputerName = 1, @@ -72,11 +63,11 @@ enum mDNSHelperErrors extern const char *mDNSHelperError(int errornum); +extern mStatus mDNSHelperInit(void); extern void mDNSRequestBPF(void); extern int mDNSPowerRequest(int key, int interval); extern int mDNSSetLocalAddressCacheEntry(int ifindex, int family, const v6addr_t ip, const ethaddr_t eth); extern void mDNSNotify(const char *title, const char *msg); // Both strings are UTF-8 text -extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value); extern void mDNSPreferencesSetName(int key, domainlabel *old, domainlabel *new); extern int mDNSKeychainGetSecrets(CFArrayRef *secrets); extern void mDNSConfigureServer(int updown, const char *const prefix, const domainname *const fqdn); @@ -85,7 +76,9 @@ extern int mDNSAutoTunnelSetKeys(int replacedelete, v6addr_t local_inner, v6addr_t remote_outer, short remote_port, const char *const prefix, const domainname *const fqdn); extern void mDNSSendWakeupPacket(unsigned ifid, char *eth_addr, char *ip_addr, int iteration); extern void mDNSPacketFilterControl(uint32_t command, char * ifname, uint32_t count, pfArray_t portArray, pfArray_t protocolArray); -extern int mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win); -extern int mDNSInterfaceAdvtIoctl(const char *ifname, int op); +extern void mDNSSendKeepalive(v6addr_t sadd, v6addr_t dadd, uint16_t lport, uint16_t rport, unsigned seq, unsigned ack, uint16_t win); +extern int mDNSRetrieveTCPInfo(int family, v6addr_t laddr, uint16_t lport, v6addr_t raddr, uint16_t rport, uint32_t *seq, uint32_t *ack, uint16_t *win, int32_t *intfid); +extern void mDNSGetRemoteMAC(mDNS *const m, int family, v6addr_t raddr); +extern void mDNSStoreSPSMACAddress(int family, v6addr_t spsaddr, char *ifname); #endif /* H_HELPER_H */ diff --git a/mDNSMacOSX/helpermsg.defs b/mDNSMacOSX/helpermsg.defs index 02c8af4..5836308 100644 --- a/mDNSMacOSX/helpermsg.defs +++ b/mDNSMacOSX/helpermsg.defs @@ -58,13 +58,6 @@ simpleroutine mDNSNotify( port : mach_port_t; msg : string_t; ServerAuditToken token : audit_token_t); -simpleroutine mDNSDynamicStoreSetConfig( - port : mach_port_t; - key : int; - subkey : string_t; - value : pointer_t; - ServerAuditToken token : audit_token_t); - simpleroutine mDNSPreferencesSetName( port : mach_port_t; key : int; @@ -124,7 +117,27 @@ simpleroutine mDNSSendKeepalive( port : mach_port_t; win : uint16_t; ServerAuditToken token : audit_token_t); -simpleroutine mDNSInterfaceAdvtIoctl( port : mach_port_t; - ifname : string_t; - op : int; - ServerAuditToken token : audit_token_t); +routine mDNSRetrieveTCPInfo( + port : mach_port_t; + family : int; + laddr : v6addr_t; + lport : uint16_t; + raddr : v6addr_t; + rport : uint16_t; + out seq : uint32_t; + out ack : uint32_t; + out win : uint16_t; + out intfid : int32_t; + ServerAuditToken token : audit_token_t); + +routine mDNSGetRemoteMAC( port : mach_port_t; + family : int; + raddr : v6addr_t; + out eth : ethaddr_t; + ServerAuditToken token : audit_token_t); + +simpleroutine mDNSStoreSPSMACAddress( port : mach_port_t; + family : int; + spsaddr : v6addr_t; + ifname : string_t; + ServerAuditToken token : audit_token_t); diff --git a/mDNSMacOSX/mDNSMacOSX.c b/mDNSMacOSX/mDNSMacOSX.c index e48ce48..3c42ad3 100644 --- a/mDNSMacOSX/mDNSMacOSX.c +++ b/mDNSMacOSX/mDNSMacOSX.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,23 +24,6 @@ // including ones that mDNSResponder chooses not to use. #define LIST_ALL_INTERFACES 0 -// For enabling AAAA records over IPv4. Setting this to 0 sends only -// A records over IPv4 and AAAA over IPv6. Setting this to 1 sends both -// AAAA and A records over both IPv4 and IPv6. -#define AAAA_OVER_V4 1 - -// In Mac OS X 10.4 and earlier, to reduce traffic, we would send and receive using IPv6 only on interfaces that had no routable -// IPv4 address. Having a routable IPv4 address assigned is a reasonable indicator of being on a large configured network, -// which means there's a good chance that most or all the other devices on that network should also have IPv4. -// By doing this we lost the ability to talk to true IPv6-only devices on that link, but we cut the packet rate in half. -// At that time, reducing the packet rate was more important than v6-only devices on a large configured network, -// so were willing to make that sacrifice. -// In Mac OS X 10.5, in 2007, two things have changed: -// 1. IPv6-only devices are starting to become more common, so we can't ignore them. -// 2. Other efficiency improvements in the code mean that crude hacks like this should no longer be necessary. - -#define USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 0 - #include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above #include "DNSCommon.h" #include "uDNS.h" @@ -49,7 +32,6 @@ #include "PlatformCommon.h" #include "uds_daemon.h" #include "CryptoSupport.h" -#include "MobileInternetSharing_priv.h" #include #include // For va_list support @@ -70,7 +52,7 @@ #include #include // for getaddrinfo #include // for SIOCGIFEFLAGS - +#include #include // For IP_RECVTTL #ifndef IP_RECVTTL #define IP_RECVTTL 24 // bool; receive reception TTL w/dgram @@ -83,16 +65,6 @@ #include -#if TARGET_OS_EMBEDDED -#define NO_SECURITYFRAMEWORK 1 -#define NO_CFUSERNOTIFICATION 1 -#endif - -#ifndef NO_SECURITYFRAMEWORK -#include -#include -#endif /* NO_SECURITYFRAMEWORK */ - #include #include "dnsinfo.h" @@ -101,10 +73,9 @@ #include #include -#if USE_IOPMCOPYACTIVEPMPREFERENCES #include #include -#endif +#include #include #include @@ -113,12 +84,7 @@ #include "P2PPacketFilter.h" #include - -#if DNSINFO_VERSION >= 20110420 #include -#else -#include -#endif // DNSINFO_VERSION >= 20110420 // Include definition of opaque_presence_indication for KEV_DL_NODE_PRESENCE handling logic. #include @@ -126,18 +92,17 @@ #if APPLE_OSX_mDNSResponder #include #include - #if !NO_D2D D2DStatus D2DInitialize(CFRunLoopRef runLoop, D2DServiceCallback serviceCallback, void* userData) __attribute__((weak_import)); -D2DStatus D2DTerminate() __attribute__((weak_import)); -D2DStatus D2DStartAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); -D2DStatus D2DStopAdvertisingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); -D2DStatus D2DStartBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import)); -D2DStatus D2DStopBrowsingForKey(const Byte *key, const size_t keySize) __attribute__((weak_import)); -void D2DStartResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); -void D2DStopResolvingPair(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize) __attribute__((weak_import)); D2DStatus D2DRetain(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); +D2DStatus D2DStopAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transportType) __attribute__((weak_import)); +D2DStatus D2DStartAdvertisingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); +D2DStatus D2DStartBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import)); +D2DStatus D2DStopBrowsingForKeyOnTransport(const Byte *key, const size_t keySize, D2DTransportType transport) __attribute__((weak_import)); +void D2DStartResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); +void D2DStopResolvingPairOnTransport(const Byte *key, const size_t keySize, const Byte *value, const size_t valueSize, D2DTransportType transport) __attribute__((weak_import)); +D2DStatus D2DTerminate() __attribute__((weak_import)); #endif // ! NO_D2D @@ -146,8 +111,18 @@ D2DStatus D2DRelease(D2DServiceInstance instanceHandle, D2DTransportType transpo #define NO_AWACS 1 #endif // APPLE_OSX_mDNSResponder +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED +#include +#endif // APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + + #define kInterfaceSpecificOption "interface=" +#define mDNS_IOREG_KEY "mDNS_KEY" +#define mDNS_IOREG_VALUE "2009-07-30" +#define mDNS_IOREG_KA_KEY "mDNS_Keepalive" +#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS' + // cache the InterfaceID of the AWDL interface static mDNSInterfaceID AWDLInterfaceID; @@ -179,11 +154,11 @@ static CFStringRef NetworkChangedKey_IPv6; static CFStringRef NetworkChangedKey_Hostnames; static CFStringRef NetworkChangedKey_Computername; static CFStringRef NetworkChangedKey_DNS; +static CFStringRef NetworkChangedKey_StateInterfacePrefix; static CFStringRef NetworkChangedKey_DynamicDNS = CFSTR("Setup:/Network/DynamicDNS"); static CFStringRef NetworkChangedKey_BackToMyMac = CFSTR("Setup:/Network/BackToMyMac"); static CFStringRef NetworkChangedKey_BTMMConnectivity = CFSTR("State:/Network/Connectivity"); static CFStringRef NetworkChangedKey_PowerSettings = CFSTR("State:/IOKit/PowerManagement/CurrentSettings"); -static CFStringRef NetworkChangedKey_InternetSharing = CFSTR("com.apple.InternetSharing"); static char HINFO_HWstring_buffer[32]; static char *HINFO_HWstring = "Device"; @@ -191,8 +166,13 @@ static int HINFO_HWstring_prefixlen = 6; mDNSexport int WatchDogReportingThreshold = 250; -#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM dispatch_queue_t SSLqueue; + +//To prevent blocking the main queue, all writes to DynamicStore happen on the DynamicStoreQueue +static dispatch_queue_t DynamicStoreQueue; + +#if TARGET_OS_EMBEDDED +#define kmDNSResponderManagedPrefsID CFSTR("/Library/Managed Preferences/mobile/com.apple.mDNSResponder.plist") #endif #if APPLE_OSX_mDNSResponder @@ -205,10 +185,16 @@ mDNSexport int ActiveDirectoryPrimaryDomainLabelCount; mDNSexport mDNSAddr ActiveDirectoryPrimaryDomainServer; #endif // APPLE_OSX_mDNSResponder +// Don't send triggers too often. We arbitrarily limit it to three minutes. +#define DNS_TRIGGER_INTERVAL (180 * mDNSPlatformOneSecond) + // Used by AutoTunnel const char btmmprefix[] = "btmmdns:"; const char dnsprefix[] = "dns:"; +// String Array used to write list of private domains to Dynamic Store +static CFArrayRef privateDnsArray = NULL; + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -217,6 +203,30 @@ const char dnsprefix[] = "dns:"; #if !NO_D2D +mDNSexport void D2D_start_advertising_interface(NetworkInterfaceInfo *interface) +{ + // AWDL wants the address and reverse address PTR record communicated + // via the D2D interface layer. + if (interface->InterfaceID == AWDLInterfaceID) + { + LogInfo("D2D_start_advertising_interface: %s", interface->ifname); + external_start_advertising_service(&interface->RR_A.resrec, NULL); + external_start_advertising_service(&interface->RR_PTR.resrec, NULL); + } +} + +mDNSexport void D2D_stop_advertising_interface(NetworkInterfaceInfo *interface) +{ + if (interface->InterfaceID == AWDLInterfaceID) + { + LogInfo("D2D_stop_advertising_interface: %s", interface->ifname); + if (interface->RR_A.resrec.RecordType) + external_stop_advertising_service(&interface->RR_A.resrec, NULL); + if (interface->RR_PTR.resrec.RecordType) + external_stop_advertising_service(&interface->RR_PTR.resrec, NULL); + } +} + // Name compression items for fake packet version number 1 static const mDNSu8 compression_packet_v1 = 0x01; @@ -336,7 +346,7 @@ mDNSlocal mStatus DNSNameCompressionParseBytes(mDNS *const m, const mDNSu8 *cons return mStatus_NoError; } -mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname const *typeDomain, DNS_TypeValues qtype) +mDNSlocal mDNSu8 * DNSNameCompressionBuildLHS(const domainname* typeDomain, DNS_TypeValues qtype) { mDNSu8 *ptr = putDomainNameAsLabels(&compression_base_msg, compression_lhs, compression_limit, typeDomain); if (!ptr) return ptr; @@ -425,17 +435,15 @@ mDNSexport void external_connection_release(const domainname *instance) } } -mDNSlocal void xD2DClearCache(const domainname *regType) +mDNSlocal void xD2DClearCache(const domainname *regType, DNS_TypeValues qtype) { D2DRecordListElem *ptr = D2DRecords; for ( ; ptr ; ptr = ptr->next) { - if (SameDomainName(&ptr->ar.namestorage, regType)) + if ((ptr->ar.resrec.rrtype == qtype) && SameDomainName(&ptr->ar.namestorage, regType)) { - char buffer[MAX_ESCAPED_DOMAIN_NAME]; mDNS_Deregister(&mDNSStorage, &ptr->ar); - ConvertDomainNameToCString(regType, buffer); - LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", buffer); + LogInfo("xD2DClearCache: Clearing cache record and deregistering %s", ARDisplayString(&mDNSStorage, &ptr->ar)); } } } @@ -826,7 +834,10 @@ mDNSexport void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, if (D2DStopBrowsingForKeyOnTransport) D2DStopBrowsingForKeyOnTransport(compression_lhs, end - compression_lhs, transportType); } - xD2DClearCache(&lower); + // The D2D driver may not generate the D2DServiceLost event for this key after + // the D2DStopBrowsingForKey*() call above. So, we flush the key from the D2D + // record cache now. + xD2DClearCache(&lower, qtype); } } @@ -913,6 +924,7 @@ mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, co domainname lower; mDNSu8 *rhs = NULL; mDNSu8 *end = NULL; + mDNSBool AWDL_used = false; // whether AWDL was used for this resolve D2DTransportType transportType, excludedTransport; DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); @@ -924,16 +936,34 @@ mDNSexport void external_start_resolving_service(mDNSInterfaceID InterfaceID, co transportType = xD2DInterfaceToTransportType(InterfaceID, flags, & excludedTransport); if (transportType == D2DTransportMax) { + // Resolving over all the transports, except for excludedTransport if set. D2DTransportType i; for (i = 0; i < D2DTransportMax; i++) { if (i == excludedTransport) continue; if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + + if (i == D2DAWDLTransport) + AWDL_used = true; } } else { + // Resolving over one specific transport. if (D2DStartResolvingPairOnTransport) D2DStartResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + + if (transportType == D2DAWDLTransport) + AWDL_used = true; + } + + // AWDL wants the SRV and TXT record queries communicated over the D2D interface. + // We only want these records going to AWDL, so use AWDLInterfaceID as the + // interface and don't set any other flags. + if (AWDL_used && AWDLInterfaceID) + { + LogInfo("external_start_resolving_service: browse for TXT and SRV over AWDL"); + external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL); + external_start_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL); } } @@ -942,6 +972,7 @@ mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, con domainname lower; mDNSu8 *rhs = NULL; mDNSu8 *end = NULL; + mDNSBool AWDL_used = false; // whether AWDL was used for this resolve D2DTransportType transportType, excludedTransport; DomainnameToLower(SkipLeadingLabels(fqdn, 1), &lower); @@ -958,11 +989,27 @@ mDNSexport void external_stop_resolving_service(mDNSInterfaceID InterfaceID, con { if (i == excludedTransport) continue; if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, i); + + if (i == D2DAWDLTransport) + AWDL_used = true; } } else { if (D2DStopResolvingPairOnTransport) D2DStopResolvingPairOnTransport(compression_lhs, rhs - compression_lhs, rhs, end - rhs, transportType); + + if (transportType == D2DAWDLTransport) + AWDL_used = true; + } + + // AWDL wants the SRV and TXT record queries communicated over the D2D interface. + // We only want these records going to AWDL, so use AWDLInterfaceID as the + // interface and don't set any other flags. + if (AWDL_used && AWDLInterfaceID) + { + LogInfo("external_stop_resolving_service: stop browse for TXT and SRV on AWDL"); + external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_TXT, NULL); + external_stop_browsing_for_service(AWDLInterfaceID, fqdn, kDNSType_SRV, NULL); } } @@ -1024,6 +1071,31 @@ mDNSexport void NotifyOfElusiveBug(const char *title, const char *msg) // Both #endif /* NO_CFUSERNOTIFICATION */ } +// Returns true if it is an AppleTV based hardware running iOS, false otherwise +mDNSlocal mDNSBool IsAppleTV(void) +{ +#if TARGET_OS_EMBEDDED + static mDNSBool sInitialized = mDNSfalse; + static mDNSBool sIsAppleTV = mDNSfalse; + CFStringRef deviceClass = NULL; + + if(!sInitialized) + { + deviceClass = (CFStringRef) MGCopyAnswer(kMGQDeviceClass, NULL); + if(deviceClass) + { + if(CFEqual(deviceClass, kMGDeviceClassAppleTV)) + sIsAppleTV = mDNStrue; + CFRelease(deviceClass); + } + sInitialized = mDNStrue; + } + return(sIsAppleTV); +#else + return mDNSfalse; +#endif // TARGET_OS_EMBEDDED +} + mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh) { static struct ifaddrs *ifa = NULL; @@ -1034,10 +1106,155 @@ mDNSlocal struct ifaddrs *myGetIfAddrs(int refresh) ifa = NULL; } - if (ifa == NULL) getifaddrs(&ifa); + if (ifa == NULL) + getifaddrs(&ifa); return ifa; } +mDNSlocal void DynamicStoreWrite(int key, const char* subkey, uintptr_t value, signed long valueCnt) +{ + CFStringRef sckey = NULL; + Boolean release_sckey = FALSE; + CFDataRef bytes = NULL; + CFPropertyListRef plist = NULL; + SCDynamicStoreRef store = NULL; + + switch ((enum mDNSDynamicStoreSetConfigKey)key) + { + case kmDNSMulticastConfig: + sckey = CFSTR("State:/Network/" kDNSServiceCompMulticastDNS); + break; + case kmDNSDynamicConfig: + sckey = CFSTR("State:/Network/DynamicDNS"); + break; + case kmDNSPrivateConfig: + sckey = CFSTR("State:/Network/" kDNSServiceCompPrivateDNS); + break; + case kmDNSBackToMyMacConfig: + sckey = CFSTR("State:/Network/BackToMyMac"); + break; + case kmDNSSleepProxyServersState: + { + CFMutableStringRef tmp = CFStringCreateMutable(kCFAllocatorDefault, 0); + CFStringAppend(tmp, CFSTR("State:/Network/Interface/")); + CFStringAppendCString(tmp, subkey, kCFStringEncodingUTF8); + CFStringAppend(tmp, CFSTR("/SleepProxyServers")); + sckey = CFStringCreateCopy(kCFAllocatorDefault, tmp); + release_sckey = TRUE; + CFRelease(tmp); + break; + } + case kmDNSDebugState: + sckey = CFSTR("State:/Network/mDNSResponder/DebugState"); + break; + default: + LogMsg("unrecognized key %d", key); + goto fin; + } + if (NULL == (bytes = CFDataCreateWithBytesNoCopy(NULL, (void *)value, + valueCnt, kCFAllocatorNull))) + { + LogMsg("CFDataCreateWithBytesNoCopy of value failed"); + goto fin; + } + if (NULL == (plist = CFPropertyListCreateFromXMLData(NULL, bytes, + kCFPropertyListImmutable, NULL))) + { + LogMsg("CFPropertyListCreateFromXMLData of bytes failed"); + goto fin; + } + CFRelease(bytes); + bytes = NULL; + if (NULL == (store = SCDynamicStoreCreate(NULL, + CFSTR(kmDNSResponderServName), NULL, NULL))) + { + LogMsg("SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + goto fin; + } + SCDynamicStoreSetValue(store, sckey, plist); + +fin: + if (NULL != bytes) + CFRelease(bytes); + if (NULL != plist) + CFRelease(plist); + if (NULL != store) + CFRelease(store); + if (release_sckey && sckey) + CFRelease(sckey); +} + +mDNSexport void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value) +{ + CFPropertyListRef valueCopy; + char *subkeyCopy = NULL; + if (!value) + return; + + // We need to copy the key and value before we dispatch off the block below as the + // caller will free the memory once we return from this function. + valueCopy = CFPropertyListCreateDeepCopy(NULL, value, kCFPropertyListImmutable); + if (!valueCopy) + { + LogMsg("mDNSDynamicStoreSetConfig: ERROR valueCopy NULL"); + return; + } + if (subkey) + { + int len = strlen(subkey); + subkeyCopy = mDNSPlatformMemAllocate(len + 1); + if (!subkeyCopy) + { + LogMsg("mDNSDynamicStoreSetConfig: ERROR subkeyCopy NULL"); + return; + } + mDNSPlatformMemCopy(subkeyCopy, subkey, len); + subkeyCopy[len] = 0; + } + + dispatch_async(DynamicStoreQueue, ^{ + CFWriteStreamRef stream = NULL; + CFDataRef bytes = NULL; + CFStringRef error; + CFIndex ret; + + if (NULL == (stream = CFWriteStreamCreateWithAllocatedBuffers(NULL, NULL))) + { + LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCreateWithAllocatedBuffers failed (Object creation failed)"); + goto END; + } + CFWriteStreamOpen(stream); + ret = CFPropertyListWriteToStream(valueCopy, stream, kCFPropertyListBinaryFormat_v1_0, &error); + if (ret == 0) + { + LogMsg("mDNSDynamicStoreSetConfig : CFPropertyListWriteToStream failed (Could not write property list to stream)"); + goto END; + } + if (NULL == (bytes = CFWriteStreamCopyProperty(stream, kCFStreamPropertyDataWritten))) + { + LogMsg("mDNSDynamicStoreSetConfig : CFWriteStreamCopyProperty failed (Object creation failed) "); + goto END; + } + CFWriteStreamClose(stream); + CFRelease(stream); + stream = NULL; + LogInfo("mDNSDynamicStoreSetConfig: key %d subkey %s", key, subkeyCopy); + DynamicStoreWrite(key, subkeyCopy ? subkeyCopy : "", (uintptr_t)CFDataGetBytePtr(bytes), CFDataGetLength(bytes)); + + END: + CFRelease(valueCopy); + if (NULL != stream) + { + CFWriteStreamClose(stream); + CFRelease(stream); + } + if (NULL != bytes) + CFRelease(bytes); + if (subkeyCopy) + mDNSPlatformMemFree(subkeyCopy); + }); +} + // To match *either* a v4 or v6 instance of this interface name, pass AF_UNSPEC for type mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const char *ifname, int type) { @@ -1050,6 +1267,52 @@ mDNSlocal NetworkInterfaceInfoOSX *SearchForInterfaceByName(mDNS *const m, const return(NULL); } +#if TARGET_OS_EMBEDDED +mDNSlocal SCPreferencesRef mDNSManagedPrefsGet(void) +{ + SCPreferencesRef smDNSManagedPrefs = NULL; + smDNSManagedPrefs = SCPreferencesCreate(kCFAllocatorDefault, CFSTR("mDNSManagedPrefs"), kmDNSResponderManagedPrefsID); + + return (smDNSManagedPrefs); +} + +mDNSlocal mDNSBool GetmDNSManagedPrefKeyVal(SCPreferencesRef prefs, CFStringRef key) +{ + mDNSBool val = mDNSfalse; + CFBooleanRef val_cf = NULL; + + if (prefs != NULL) + { + val_cf = SCPreferencesGetValue(prefs, key); + if (isA_CFBoolean(val_cf) != NULL) + val = CFBooleanGetValue(val_cf); //When mDNSResponder-Debug-profile is Installed + else + val = mDNSfalse; //When mDNSResponder-Debug-profile is Uninstalled + } + else + { + LogMsg("GetmDNSManagedPrefKeyVal: mDNSManagedPrefs are NULL!"); + val = mDNSfalse; + } + if (val_cf) + CFRelease(val_cf); + return (val); +} + +mDNSexport mDNSBool GetmDNSManagedPref(CFStringRef key) +{ + SCPreferencesRef managed = NULL; + mDNSBool ret_value; + + managed = mDNSManagedPrefsGet(); + ret_value = GetmDNSManagedPrefKeyVal(managed, key); + + if (managed) + CFRelease(managed); + return (ret_value); +} +#endif //TARGET_OS_EMBEDDED + mDNSlocal int myIfIndexToName(u_short ifindex, char *name) { struct ifaddrs *ifa; @@ -1121,7 +1384,8 @@ mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNS #if APPLE_OSX_mDNSResponder mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...) { - if (OSXVers < OSXVers_10_6_SnowLeopard) return; // Only do ASL on Mac OS X 10.6 and later (not on iOS) + if (iOSVers) + return; // No ASL on iOS static char buffer[512]; aslmsg asl_msg = asl_new(ASL_TYPE_MSG); @@ -1151,6 +1415,230 @@ mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *resu asl_set_filter(NULL, old_filter); asl_free(asl_msg); } + + +mDNSlocal void mDNSLogDNSSECStatistics(mDNS *const m) +{ + char buffer[16]; + + aslmsg aslmsg = asl_new(ASL_TYPE_MSG); + + // If we failed to allocate an aslmsg structure, keep accumulating + // the statistics and try again at the next log interval. + if (!aslmsg) + { + LogMsg("mDNSLogDNSSECStatistics: asl_new() failed!"); + return; + } + + asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.DNSSECstatistics"); + + if (m->rrcache_totalused_unicast) + { + mDNS_snprintf(buffer, sizeof(buffer), "%u", (mDNSu32) ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast); + } + else + { + LogMsg("mDNSLogDNSSECStatistics: unicast is zero"); + buffer[0] = 0; + } + asl_set(aslmsg,"com.apple.message.MemUsage", buffer); + + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency0); + asl_set(aslmsg,"com.apple.message.Latency0", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency10); + asl_set(aslmsg,"com.apple.message.Latency10", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency20); + asl_set(aslmsg,"com.apple.message.Latency20", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency50); + asl_set(aslmsg,"com.apple.message.Latency50", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.Latency100); + asl_set(aslmsg,"com.apple.message.Latency100", buffer); + + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets0); + asl_set(aslmsg,"com.apple.message.ExtraPackets0", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets3); + asl_set(aslmsg,"com.apple.message.ExtraPackets3", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets7); + asl_set(aslmsg,"com.apple.message.ExtraPackets7", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.ExtraPackets10); + asl_set(aslmsg,"com.apple.message.ExtraPackets10", buffer); + + // Ignore IndeterminateStatus as we don't log them + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.SecureStatus); + asl_set(aslmsg,"com.apple.message.SecureStatus", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.InsecureStatus); + asl_set(aslmsg,"com.apple.message.InsecureStatus", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.BogusStatus); + asl_set(aslmsg,"com.apple.message.BogusStatus", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NoResponseStatus); + asl_set(aslmsg,"com.apple.message.NoResponseStatus", buffer); + + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.NumProbesSent); + asl_set(aslmsg,"com.apple.message.NumProbesSent", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize0); + asl_set(aslmsg,"com.apple.message.MsgSize0", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize1); + asl_set(aslmsg,"com.apple.message.MsgSize1", buffer); + mDNS_snprintf(buffer, sizeof(buffer), "%u", m->DNSSECStats.MsgSize2); + asl_set(aslmsg,"com.apple.message.MsgSize2", buffer); + + asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, ""); + asl_free(aslmsg); +} + +// Calculate packets per hour given total packet count and interval in seconds. +// Cast one term of multiplication to (long) to use 64-bit arithmetic +// and avoid a potential 32-bit overflow prior to the division. +#define ONE_HOUR 3600 +#define PACKET_RATE(PACKETS, INTERVAL) (int)(((long) (PACKETS) * ONE_HOUR)/(INTERVAL)) + +// Put packet rate data in discrete buckets. +mDNSlocal int mDNSBucketData(int inputData, int interval) +{ + if (!interval) + { + LogMsg("mDNSBucketData: interval is zero!"); + return 0; + } + + int ratePerHour = PACKET_RATE(inputData, interval); + int bucket; + + if (ratePerHour == 0) + bucket = 0; + else if (ratePerHour <= 10) + bucket = 10; + else if (ratePerHour <= 100) + bucket = 100; + else if (ratePerHour <= 1000) + bucket = 1000; + else if (ratePerHour <= 5000) + bucket = 5000; + else if (ratePerHour <= 10000) + bucket = 10000; + else if (ratePerHour <= 50000) + bucket = 50000; + else if (ratePerHour <= 100000) + bucket = 100000; + else if (ratePerHour <= 250000) + bucket = 250000; + else if (ratePerHour <= 500000) + bucket = 500000; + else + bucket = 1000000; + + return bucket; +} + +mDNSlocal void mDNSLogBonjourStatistics(mDNS *const m) +{ + static mDNSs32 last_PktNum, last_MPktNum; + static mDNSs32 last_UnicastPacketsSent, last_MulticastPacketsSent; + static mDNSs32 last_RemoteSubnet; + + mDNSs32 interval; + char buffer[16]; + mDNSs32 inMulticast = m->MPktNum - last_MPktNum; + mDNSs32 inUnicast = m->PktNum - last_PktNum - inMulticast; + mDNSs32 outUnicast = m->UnicastPacketsSent - last_UnicastPacketsSent; + mDNSs32 outMulticast = m->MulticastPacketsSent - last_MulticastPacketsSent; + mDNSs32 remoteSubnet = m->RemoteSubnet - last_RemoteSubnet; + + + // save starting values for new interval + last_PktNum = m->PktNum; + last_MPktNum = m->MPktNum; + last_UnicastPacketsSent = m->UnicastPacketsSent; + last_MulticastPacketsSent = m->MulticastPacketsSent; + last_RemoteSubnet = m->RemoteSubnet; + + // Need a non-zero active time interval. + if (!m->ActiveStatTime) + return; + + // Round interval time to nearest hour boundary. Less then 30 minutes rounds to zero. + interval = (m->ActiveStatTime + ONE_HOUR/2)/ONE_HOUR; + + // Use a minimum of 30 minutes of awake time to calculate average packet rates. + // The rounded awake interval should not be greater than the rounded reporting + // interval. + if ((interval == 0) || (interval > (kDefaultNextStatsticsLogTime + ONE_HOUR/2)/ONE_HOUR)) + return; + + aslmsg aslmsg = asl_new(ASL_TYPE_MSG); + + if (!aslmsg) + { + LogMsg("mDNSLogBonjourStatistics: asl_new() failed!"); + return; + } + // log in MessageTracer format + asl_set(aslmsg,"com.apple.message.domain", "com.apple.mDNSResponder.statistics"); + + snprintf(buffer, sizeof(buffer), "%d", interval); + asl_set(aslmsg,"com.apple.message.interval", buffer); + + // log the packet rates as packets per hour + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(inUnicast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.UnicastIn", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(inMulticast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.MulticastIn", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(outUnicast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.UnicastOut", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(outMulticast, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.MulticastOut", buffer); + + snprintf(buffer, sizeof(buffer), "%d", + mDNSBucketData(remoteSubnet, m->ActiveStatTime)); + asl_set(aslmsg,"com.apple.message.RemoteSubnet", buffer); + + asl_log(NULL, aslmsg, ASL_LEVEL_NOTICE, ""); + + asl_free(aslmsg); +} + +// Log multicast and unicast traffic statistics to MessageTracer on OSX +mDNSexport void mDNSLogStatistics(mDNS *const m) +{ + // MessageTracer only available on OSX + if (iOSVers) + return; + + mDNSs32 currentUTC = mDNSPlatformUTC(); + + // log runtime statistics + if ((currentUTC - m->NextStatLogTime) >= 0) + { + m->NextStatLogTime = currentUTC + kDefaultNextStatsticsLogTime; + // If StatStartTime is zero, it hasn't been reinitialized yet + // in the wakeup code path. + if (m->StatStartTime) + { + m->ActiveStatTime += currentUTC - m->StatStartTime; + } + + // Only log statistics if we have recorded some active time during + // this statistics interval. + if (m->ActiveStatTime) + { + mDNSLogBonjourStatistics(m); + mDNSLogDNSSECStatistics(m); + } + + // Start a new statistics gathering interval. + m->StatStartTime = currentUTC; + m->ActiveStatTime = 0; + } +} + #endif // APPLE_OSX_mDNSResponder #if COMPILER_LIKES_PRAGMA_MARK @@ -1162,13 +1650,10 @@ mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr) { mDNSBool result = mDNSfalse; SCNetworkConnectionFlags flags; -#if DNSINFO_VERSION >= 20110420 CFDataRef remote_addr; CFMutableDictionaryRef options; -#endif // DNSINFO_VERSION >= 20110420 SCNetworkReachabilityRef ReachRef = NULL; -#if DNSINFO_VERSION >= 20110420 options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); remote_addr = CFDataCreate(NULL, (const UInt8 *)addr, addr->sa_len); CFDictionarySetValue(options, kSCNetworkReachabilityOptionRemoteAddress, remote_addr); @@ -1176,16 +1661,22 @@ mDNSlocal mDNSBool AddrRequiresPPPConnection(const struct sockaddr *addr) ReachRef = SCNetworkReachabilityCreateWithOptions(kCFAllocatorDefault, options); CFRelease(options); CFRelease(remote_addr); - if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions"); goto end; } -#else - ReachRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, addr); - if (!ReachRef) { LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithAddress"); goto end; } -#endif // DNSINFO_VERSION >= 20110420 - if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) { LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); goto end; } + + if (!ReachRef) + { + LogMsg("ERROR: RequiresConnection - SCNetworkReachabilityCreateWithOptions"); + goto end; + } + if (!SCNetworkReachabilityGetFlags(ReachRef, &flags)) + { + LogMsg("ERROR: AddrRequiresPPPConnection - SCNetworkReachabilityGetFlags"); + goto end; + } result = flags & kSCNetworkFlagsConnectionRequired; end: - if (ReachRef) CFRelease(ReachRef); + if (ReachRef) + CFRelease(ReachRef); return result; } @@ -1202,6 +1693,38 @@ mDNSlocal void setTrafficClass(int socketfd, mDNSBool useBackgroundTrafficClass) (void) setsockopt(socketfd, SOL_SOCKET, SO_TRAFFIC_CLASS, (void *)&traffic_class, sizeof(traffic_class)); } +mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +{ + if (src) + { + int s; + + if (dst->type == mDNSAddrType_IPv4) + { + s = src->ss.sktv4; + } + else + { + s = src->ss.sktv6; + } + + if (q->pid) + { + if (setsockopt(s, SOL_SOCKET, SO_DELEGATED, &q->pid, sizeof(q->pid)) == -1) + { + LogInfo("mDNSPlatformSetDelegatePID: Delegate PID failed %s for PID %d", strerror(errno), q->pid); + } + } + else + { + if (setsockopt(s, SOL_SOCKET, SO_DELEGATED_UUID, &q->uuid, sizeof(q->uuid)) == -1) + { + LogInfo("mDNSPlatformSetDelegatePID: Delegate UUID failed %s", strerror(errno)); + } + } + } +} + // Note: If InterfaceID is NULL, it means, "send this packet through our anonymous unicast socket" // Note: If InterfaceID is non-NULL it means, "send this packet through our port 5353 socket on the specified interface" // OR send via our primary v4 unicast socket @@ -1284,7 +1807,7 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms #endif } } -#ifndef NO_IPV6 + else if (dst->type == mDNSAddrType_IPv6) { struct sockaddr_in6 *sin6_to = (struct sockaddr_in6*)&to; @@ -1308,7 +1831,7 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms } } } -#endif + else { LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!"); @@ -1356,8 +1879,8 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow)); else { - MessageCount += 100; - if (MessageCount < 1000) // Cap and ensure NO spamming of LogMsgs + MessageCount++; + if (MessageCount < 50) // Cap and ensure NO spamming of LogMsgs LogMsg("mDNSPlatformSendUDP: sendto(%d) failed to send packet on InterfaceID %p %5s/%d to %#a:%d skt %d error %d errno %d (%s) %lu MessageCount is %d", s, InterfaceID, ifa_name, dst->type, dst, mDNSVal16(dstPort), s, err, errno, strerror(errno), (mDNSu32)(m->timenow), MessageCount); else // If logging is enabled, remove the cap and log aggressively @@ -1379,7 +1902,7 @@ mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const ms return(result); } -mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, +mDNSexport ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char ifname[IF_NAMESIZE], mDNSu8 *ttl) { static unsigned int numLogMessages = 0; @@ -1409,8 +1932,8 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, } if (msg.msg_controllen < (int)sizeof(struct cmsghdr)) { - if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu", - s, n, msg.msg_controllen, sizeof(struct cmsghdr)); + if (numLogMessages++ < 100) LogMsg("mDNSMacOSX.c: recvmsg(%d) returned %d msg.msg_controllen %d < sizeof(struct cmsghdr) %lu, errno %d", + s, n, msg.msg_controllen, sizeof(struct cmsghdr), errno); return(-1); } if (msg.msg_flags & MSG_CTRUNC) @@ -1457,7 +1980,65 @@ mDNSlocal ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, return(n); } -mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) +mDNSlocal mDNSInterfaceID FindMyInterface(mDNS *const m, const mDNSAddr *addr) +{ + NetworkInterfaceInfo *intf; + + if (addr->type == mDNSAddrType_IPv4) + { + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->ip.type == addr->type && intf->McastTxRx) + { + if ((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) == 0) + { + return(intf->InterfaceID); + } + } + } + } + + if (addr->type == mDNSAddrType_IPv6) + { + for (intf = m->HostInterfaces; intf; intf = intf->next) + { + if (intf->ip.type == addr->type && intf->McastTxRx) + { + if (((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) == 0) && + ((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) == 0) && + ((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) == 0) && + (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) == 0))) + { + return(intf->InterfaceID); + } + } + } + } + return(mDNSInterface_Any); +} + +mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src) +{ + // We should have a DNSMessage header followed by the question and an answer + // which also includes a CNAME (that's when this function is called). To keep it + // simple, we expect at least the size of DNSMessage header(12) and size of "A" + // record (14 bytes). + char buffer[26]; + int ret; + + (void) m; + + if (!src) + return mDNSfalse; + + ret = recv(src->ss.sktv4, buffer, sizeof(buffer), MSG_PEEK); + if (ret > 0) + return mDNStrue; + else + return mDNSfalse; +} + +mDNSexport void myKQSocketCallBack(int s1, short filter, void *context) { KQSocketSet *const ss = (KQSocketSet *)context; mDNS *const m = ss->m; @@ -1466,17 +2047,10 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) if (filter != EVFILT_READ) LogMsg("myKQSocketCallBack: Why is filter %d not EVFILT_READ (%d)?", filter, EVFILT_READ); - if (s1 != ss->sktv4 -#ifndef NO_IPV6 - && s1 != ss->sktv6 -#endif - ) + if (s1 != ss->sktv4 && s1 != ss->sktv6) { LogMsg("myKQSocketCallBack: native socket %d", s1); - LogMsg("myKQSocketCallBack: sktv4 %d", ss->sktv4); -#ifndef NO_IPV6 - LogMsg("myKQSocketCallBack: sktv6 %d", ss->sktv6); -#endif + LogMsg("myKQSocketCallBack: sktv4 %d sktv6 %d", ss->sktv4, ss->sktv6); } while (!closed) @@ -1526,8 +2100,15 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) // during that time, which would confuse mDNSCoreReceive, because as far // as it's concerned, we should have no active interfaces any more. // Hence we ignore multicasts for which we can find no matching InterfaceID. - if (intf) InterfaceID = intf->ifinfo.InterfaceID; - else if (mDNSAddrIsDNSMulticast(&destAddr)) continue; + if (intf) + InterfaceID = intf->ifinfo.InterfaceID; + else if (mDNSAddrIsDNSMulticast(&destAddr)) + continue; + + if (!InterfaceID) + { + InterfaceID = FindMyInterface(m, &destAddr); + } // LogMsg("myKQSocketCallBack got packet from %#a to %#a on interface %#a/%s", // &senderAddr, &destAddr, &ss->info->ifinfo.ip, ss->info->ifinfo.ifname); @@ -1538,7 +2119,15 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) // if it closes the socketset. ss->closeFlag = &closed; - mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID); + if (ss->proxy) + { + m->p->UDPProxyCallback(m, &m->p->UDPProxy, (unsigned char *)&m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, + senderPort, &destAddr, ss->port, InterfaceID, NULL); + } + else + { + mDNSCoreReceive(m, &m->imsg, (unsigned char*)&m->imsg + err, &senderAddr, senderPort, &destAddr, ss->port, InterfaceID); + } // if we didn't close, we can safely dereference the socketset, and should to // reset the closeFlag, since it points to something on the stack @@ -1586,37 +2175,7 @@ mDNSlocal void myKQSocketCallBack(int s1, short filter, void *context) } } -// TCP socket support - -typedef enum -{ - handshake_required, - handshake_in_progress, - handshake_completed, - handshake_to_be_closed -} handshakeStatus; - -struct TCPSocket_struct -{ - TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags - TCPConnectionCallback callback; - int fd; - KQueueEntry *kqEntry; - KQSocketSet ss; -#ifndef NO_SECURITYFRAMEWORK - SSLContextRef tlsContext; - pthread_t handshake_thread; -#endif /* NO_SECURITYFRAMEWORK */ - domainname hostname; - void *context; - mDNSBool setup; - mDNSBool connected; - handshakeStatus handshake; - mDNS *m; // So we can call KQueueLock from the SSLHandshake thread - mStatus err; -}; - -mDNSlocal void doTcpSocketCallback(TCPSocket *sock) +mDNSlocal void doTcpSocketCallback(TCPSocket *sock) { mDNSBool c = !sock->connected; sock->connected = mDNStrue; @@ -1652,39 +2211,68 @@ mDNSlocal OSStatus tlsReadSock(SSLConnectionRef connection, void *data, size_t * return(errSSLClosedAbort); } -mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, mDNSBool server) +mDNSlocal OSStatus tlsSetupSock(TCPSocket *sock, SSLProtocolSide pside, SSLConnectionType ctype) { char domname_cstr[MAX_ESCAPED_DOMAIN_NAME]; - mStatus err = SSLNewContext(server, &sock->tlsContext); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLNewContext failed with error code: %d", err); return(err); } + sock->tlsContext = SSLCreateContext(kCFAllocatorDefault, pside, ctype); + if (!sock->tlsContext) + { + LogMsg("ERROR: tlsSetupSock: SSLCreateContext failed"); + return(mStatus_UnknownErr); + } - err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); return(err); } + mStatus err = SSLSetIOFuncs(sock->tlsContext, tlsReadSock, tlsWriteSock); + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetIOFuncs failed with error code: %d", err); + goto fail; + } err = SSLSetConnection(sock->tlsContext, (SSLConnectionRef) sock); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); return(err); } + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetConnection failed with error code: %d", err); + goto fail; + } // Instead of listing all the acceptable ciphers, we just disable the bad ciphers. It does not disable // all the bad ciphers like RC4_MD5, but it assumes that the servers don't offer them. err = SSLSetAllowAnonymousCiphers(sock->tlsContext, 0); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); return(err); } + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetAllowAnonymousCiphers failed with error code: %d", err); + goto fail; + } // We already checked for NULL in hostname and this should never happen. Hence, returning -1 // (error not in OSStatus space) is okay. - if (!sock->hostname.c[0]) {LogMsg("ERROR: tlsSetupSock: hostname NULL"); return -1; } + if (!sock->hostname.c[0]) + { + LogMsg("ERROR: tlsSetupSock: hostname NULL"); + err = -1; + goto fail; + } ConvertDomainNameToCString(&sock->hostname, domname_cstr); err = SSLSetPeerDomainName(sock->tlsContext, domname_cstr, strlen(domname_cstr)); - if (err) { LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); return(err); } + if (err) + { + LogMsg("ERROR: tlsSetupSock: SSLSetPeerDomainname: %s failed with error code: %d", domname_cstr, err); + goto fail; + } + + return(err); +fail: + if (sock->tlsContext) + CFRelease(sock->tlsContext); return(err); } #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM -mDNSlocal void doSSLHandshake(void *ctx) +mDNSlocal void doSSLHandshake(TCPSocket *sock) { - TCPSocket *sock = (TCPSocket*)ctx; mStatus err = SSLHandshake(sock->tlsContext); //Can't have multiple threads in mDNS core. When MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM is @@ -1720,7 +2308,7 @@ mDNSlocal void doSSLHandshake(void *ctx) if (err) { LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); - SSLDisposeContext(sock->tlsContext); + CFRelease(sock->tlsContext); sock->tlsContext = NULL; } @@ -1736,12 +2324,11 @@ mDNSlocal void doSSLHandshake(void *ctx) return; }); } -#else -mDNSlocal void *doSSLHandshake(void *ctx) +#else // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM +mDNSlocal void *doSSLHandshake(TCPSocket *sock) { // Warning: Touching sock without the kqueue lock! // We're protected because sock->handshake == handshake_in_progress - TCPSocket *sock = (TCPSocket*)ctx; mDNS * const m = sock->m; // Get m now, as we may free sock if marked to be closed while we're waiting on SSLHandshake mStatus err = SSLHandshake(sock->tlsContext); @@ -1765,7 +2352,7 @@ mDNSlocal void *doSSLHandshake(void *ctx) if (err) { LogMsg("SSLHandshake failed: %d%s", err, err == errSSLPeerInternalError ? " (server busy)" : ""); - SSLDisposeContext(sock->tlsContext); + CFRelease(sock->tlsContext); sock->tlsContext = NULL; } @@ -1781,37 +2368,21 @@ mDNSlocal void *doSSLHandshake(void *ctx) KQueueUnlock(m, "doSSLHandshake"); return NULL; } -#endif +#endif // MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM -mDNSlocal mStatus spawnSSLHandshake(TCPSocket* sock) +mDNSlocal void spawnSSLHandshake(TCPSocket* sock) { debugf("spawnSSLHandshake %p: entry", sock); - mStatus err; if (sock->handshake != handshake_required) LogMsg("spawnSSLHandshake: handshake status not required: %d", sock->handshake); sock->handshake = handshake_in_progress; KQueueSet(sock->fd, EV_DELETE, EVFILT_READ, sock->kqEntry); -#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM - // Dispatch it on a separate serial queue to avoid deadlocks with threads running on main queue + // Dispatch it on a separate queue to help avoid blocking other threads/queues, and + // to limit the number of threads used for SSLHandshake dispatch_async(SSLqueue, ^{doSSLHandshake(sock);}); - err = 0; -#else - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - err = pthread_create(&sock->handshake_thread, &attr, doSSLHandshake, sock); - pthread_attr_destroy(&attr); - if (err) - { - LogMsg("Could not start SSLHandshake thread: (%d) %s", err, strerror(err)); - sock->handshake = handshake_completed; - sock->err = err; - KQueueSet(sock->fd, EV_ADD, EVFILT_READ, sock->kqEntry); - } -#endif + debugf("spawnSSLHandshake %p: done for %d", sock, sock->fd); - return err; } #endif /* NO_SECURITYFRAMEWORK */ @@ -1824,21 +2395,38 @@ mDNSlocal void tcpKQSocketCallback(__unused int fd, short filter, void *context) //if (filter == EVFILT_READ ) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_READ", filter); //if (filter == EVFILT_WRITE) LogMsg("myKQSocketCallBack: tcpKQSocketCallback %d is EVFILT_WRITE", filter); // EV_ONESHOT doesn't seem to work, so we add the filter with EV_ADD, and explicitly delete it here with EV_DELETE - if (filter == EVFILT_WRITE) KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry); + if (filter == EVFILT_WRITE) + KQueueSet(sock->fd, EV_DELETE, EVFILT_WRITE, sock->kqEntry); if (sock->flags & kTCPSocketFlags_UseTLS) { #ifndef NO_SECURITYFRAMEWORK - if (!sock->setup) { sock->setup = mDNStrue; tlsSetupSock(sock, mDNSfalse); } - - if (sock->handshake == handshake_required) { if (spawnSSLHandshake(sock) == 0) return;} - else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) return; + if (!sock->setup) + { + sock->setup = mDNStrue; + sock->err = tlsSetupSock(sock, kSSLClientSide, kSSLStreamType); + if (sock->err) + { + LogMsg("ERROR: tcpKQSocketCallback: tlsSetupSock failed with error code: %d", sock->err); + return; + } + } + if (sock->handshake == handshake_required) + { + spawnSSLHandshake(sock); + return; + } + else if (sock->handshake == handshake_in_progress || sock->handshake == handshake_to_be_closed) + { + return; + } else if (sock->handshake != handshake_completed) { - if (!sock->err) sock->err = mStatus_UnknownErr; + if (!sock->err) + sock->err = mStatus_UnknownErr; LogMsg("tcpKQSocketCallback called with unexpected SSLHandshake status: %d", sock->handshake); } -#else +#else /* NO_SECURITYFRAMEWORK */ sock->err = mStatus_UnsupportedErr; #endif /* NO_SECURITYFRAMEWORK */ } @@ -1940,7 +2528,7 @@ mDNSexport void KQueueLock(mDNS *const m) m->p->BigMutexStartTime = mDNSPlatformRawTime(); } -mDNSexport void KQueueUnlock(mDNS *const m, const char const *task) +mDNSexport void KQueueUnlock(mDNS *const m, const char* task) { mDNSs32 end = mDNSPlatformRawTime(); (void)task; @@ -1981,13 +2569,8 @@ mDNSexport void mDNSPlatformCloseFD(KQueueEntry *kq, int fd) mDNSlocal mStatus SetupTCPSocket(TCPSocket *sock, u_short sa_family, mDNSIPPort *port, mDNSBool useBackgroundTrafficClass) { KQSocketSet *cp = &sock->ss; -#ifndef NO_IPV6 int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; -#else - int *s = &cp->sktv4; - KQueueEntry *k = &cp->kqsv4; -#endif const int on = 1; // "on" for setsockopt mStatus err; @@ -2064,17 +2647,14 @@ mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, sock->ss.m = m; sock->ss.sktv4 = -1; -#ifndef NO_IPV6 sock->ss.sktv6 = -1; -#endif err = SetupTCPSocket(sock, AF_INET, port, useBackgroundTrafficClass); -#ifndef NO_IPV6 + if (!err) { err = SetupTCPSocket(sock, AF_INET6, port, useBackgroundTrafficClass); if (err) { mDNSPlatformCloseFD(&sock->ss.kqsv4, sock->ss.sktv4); sock->ss.sktv4 = -1; } } -#endif if (err) { LogMsg("mDNSPlatformTCPSocket: socket error %d errno %d (%s)", sock->fd, errno, strerror(errno)); @@ -2098,13 +2678,8 @@ mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context) { KQSocketSet *cp = &sock->ss; -#ifndef NO_IPV6 int *s = (dst->type == mDNSAddrType_IPv4) ? &cp->sktv4 : &cp->sktv6; KQueueEntry *k = (dst->type == mDNSAddrType_IPv4) ? &cp->kqsv4 : &cp->kqsv6; -#else - int *s = &cp->sktv4; - KQueueEntry *k = &cp->kqsv4; -#endif mStatus err = mStatus_NoError; struct sockaddr_storage ss; @@ -2225,7 +2800,7 @@ mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int fd) #ifndef NO_SECURITYFRAMEWORK if (!ServerCerts) { LogMsg("ERROR: mDNSPlatformTCPAccept: unable to find TLS certificates"); err = mStatus_UnknownErr; goto exit; } - err = tlsSetupSock(sock, mDNStrue); + err = tlsSetupSock(sock, kSSLServerSide, kSSLStreamType); if (err) { LogMsg("ERROR: mDNSPlatformTCPAccept: tlsSetupSock failed with error code: %d", err); goto exit; } err = SSLSetCertificate(sock->tlsContext, ServerCerts); @@ -2242,6 +2817,18 @@ exit: return(sock); } +mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) +{ + mDNSu16 port; + + port = -1; + if (sock) + { + port = sock->ss.port.NotAnInteger; + } + return port; +} + mDNSlocal void CloseSocketSet(KQSocketSet *ss) { if (ss->sktv4 != -1) @@ -2249,13 +2836,11 @@ mDNSlocal void CloseSocketSet(KQSocketSet *ss) mDNSPlatformCloseFD(&ss->kqsv4, ss->sktv4); ss->sktv4 = -1; } -#ifndef NO_IPV6 if (ss->sktv6 != -1) { mDNSPlatformCloseFD(&ss->kqsv6, ss->sktv6); ss->sktv6 = -1; } -#endif if (ss->closeFlag) *ss->closeFlag = 1; } @@ -2276,14 +2861,14 @@ mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) } SSLClose(sock->tlsContext); - SSLDisposeContext(sock->tlsContext); + CFRelease(sock->tlsContext); sock->tlsContext = NULL; } #endif /* NO_SECURITYFRAMEWORK */ - if (sock->ss.sktv4 != -1) shutdown(sock->ss.sktv4, 2); -#ifndef NO_IPV6 - if (sock->ss.sktv6 != -1) shutdown(sock->ss.sktv6, 2); -#endif + if (sock->ss.sktv4 != -1) + shutdown(sock->ss.sktv4, 2); + if (sock->ss.sktv6 != -1) + shutdown(sock->ss.sktv6, 2); CloseSocketSet(&sock->ss); sock->fd = -1; @@ -2293,7 +2878,7 @@ mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock) mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed) { - size_t nread = 0; + ssize_t nread = 0; *closed = mDNSfalse; if (sock->flags & kTCPSocketFlags_UseTLS) @@ -2304,7 +2889,7 @@ mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long bu else if (sock->handshake != handshake_completed) LogMsg("mDNSPlatformReadTCP called with unexpected SSLHandshake status: %d", sock->handshake); //LogMsg("Starting SSLRead %d %X", sock->fd, fcntl(sock->fd, F_GETFL, 0)); - mStatus err = SSLRead(sock->tlsContext, buf, buflen, &nread); + mStatus err = SSLRead(sock->tlsContext, buf, buflen, (size_t *)&nread); //LogMsg("SSLRead returned %d (%d) nread %d buflen %d", err, errSSLWouldBlock, nread, buflen); if (err == errSSLClosedGraceful) { nread = 0; *closed = mDNStrue; } else if (err && err != errSSLWouldBlock) @@ -2395,21 +2980,13 @@ mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock) // If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa_family, mDNSIPPort *const outport) { -#ifndef NO_IPV6 int *s = (sa_family == AF_INET) ? &cp->sktv4 : &cp->sktv6; KQueueEntry *k = (sa_family == AF_INET) ? &cp->kqsv4 : &cp->kqsv6; -#else - int *s = &cp->sktv4; - KQueueEntry *k = &cp->kqsv4; -#endif const int on = 1; const int twofivefive = 255; mStatus err = mStatus_NoError; char *errstr = mDNSNULL; - -#ifdef NO_IPV6 - if (sa_family != AF_INET) return -1; -#endif + const int mtu = 0; cp->closeFlag = mDNSNULL; @@ -2465,10 +3042,9 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa if (err) { errstr = "bind"; goto fail; } if (outport) outport->NotAnInteger = listening_sockaddr.sin_port; } -#ifndef NO_IPV6 else if (sa_family == AF_INET6) { - // NAT-PMP Announcements make no sense on IPv6, so bail early w/o error + // NAT-PMP Announcements make no sense on IPv6, and we don't support IPv6 for PCP, so bail early w/o error if (mDNSSameIPPort(port, NATPMPAnnouncementPort)) { if (outport) *outport = zeroIPPort;return mStatus_NoError; } // We want to receive destination addresses and receive interface identifiers @@ -2496,6 +3072,12 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa err = setsockopt(skt, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &on, sizeof(on)); if (err < 0) { errstr = "setsockopt - IPV6_MULTICAST_LOOP"; goto fail; } + // Disable default option to send mDNSv6 packets at min IPv6 MTU: RFC 3542, Sec 11 + err = setsockopt(skt, IPPROTO_IPV6, IPV6_USE_MIN_MTU, &mtu, sizeof(mtu)); + if (err < 0) // Since it is an optimization if we fail just log the err, no need to close the skt + LogMsg("SetupSocket: setsockopt - IPV6_USE_MIN_MTU: IP6PO_MINMTU_DISABLE socket %d err %d errno %d (%s)", + skt, err, errno, strerror(errno)); + // And start listening for packets struct sockaddr_in6 listening_sockaddr6; mDNSPlatformMemZero(&listening_sockaddr6, sizeof(listening_sockaddr6)); @@ -2509,7 +3091,6 @@ mDNSlocal mStatus SetupSocket(KQSocketSet *cp, const mDNSIPPort port, u_short sa if (err) { errstr = "bind"; goto fail; } if (outport) outport->NotAnInteger = listening_sockaddr6.sin6_port; } -#endif fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK); // set non-blocking fcntl(skt, F_SETFD, 1); // set close-on-exec @@ -2559,22 +3140,19 @@ mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requ p->ss.port = zeroIPPort; p->ss.m = m; p->ss.sktv4 = -1; -#ifndef NO_IPV6 p->ss.sktv6 = -1; -#endif + p->ss.proxy = mDNSfalse; do { // The kernel doesn't do cryptographically strong random port allocation, so we do it ourselves here if (randomizePort) port = mDNSOpaque16fromIntVal(0xC000 + mDNSRandom(0x3FFF)); err = SetupSocket(&p->ss, port, AF_INET, &p->ss.port); -#ifndef NO_IPV6 if (!err) { err = SetupSocket(&p->ss, port, AF_INET6, &p->ss.port); if (err) { mDNSPlatformCloseFD(&p->ss.kqsv4, p->ss.sktv4); p->ss.sktv4 = -1; } } -#endif i--; } while (err == EADDRINUSE && randomizePort && i); @@ -2635,7 +3213,7 @@ mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSA if (info == NULL) { LogMsg("mDNSPlatformSetLocalAddressCacheEntry: Invalid interface index %p", InterfaceID); return; } // Manually inject an entry into our local ARP cache. // (We can't do this by sending an ARP broadcast, because the kernel only pays attention to incoming ARP packets, not outgoing.) - if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa)) + if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, tpa, mDNSNULL)) LogSPS("Don't need address cache entry for %s %#a %.6a", info->ifinfo.ifname, tpa, tha); else { @@ -2734,6 +3312,38 @@ mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIP mDNSSendKeepalive(sadd->ip.v6.b, dadd->ip.v6.b, lport->NotAnInteger, rport->NotAnInteger, seq, ack, win); } +mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) +{ + int family = (spsaddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + LogSPS("mDNSPlatformStoreSPSMACAddr : Storing %#a on interface %s", spsaddr, ifname); + mDNSStoreSPSMACAddress(family, spsaddr->ip.v6.b, ifname); + return KERN_SUCCESS; +} + +mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr) +{ + int family = (raddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + + mDNSGetRemoteMAC(m, family, raddr->ip.v6.b); + return KERN_SUCCESS; +} + +mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) +{ + mDNSs32 intfid; + mDNSs32 error = 0; + int family = (laddr->type == mDNSAddrType_IPv4) ? AF_INET : AF_INET6; + + error = mDNSRetrieveTCPInfo(family, laddr->ip.v6.b, lport->NotAnInteger, raddr->ip.v6.b, rport->NotAnInteger, (uint32_t *)&(mti->seq), (uint32_t *)&(mti->ack), (uint16_t *)&(mti->window), (int32_t*)&intfid); + if (error != KERN_SUCCESS) + { + LogMsg("%s: mDNSRetrieveTCPInfo returned : %d", __func__, error); + return error; + } + mti->IntfId = mDNSPlatformInterfaceIDfromInterfaceIndex(m, intfid); + return error; +} + #define BPF_SetOffset(from, cond, to) (from)->cond = (to) - 1 - (from) mDNSlocal int CountProxyTargets(mDNS *const m, NetworkInterfaceInfoOSX *x, int *p4, int *p6) @@ -2919,9 +3529,9 @@ mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID // prog.bf_len = 0; This seems to panic the kernel if (x->BPF_fd < 0) return; // If we've already closed our BPF_fd, no need to generate an error message below } - - if (ioctl(x->BPF_fd, BIOCSETF, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETF(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno)); - else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETF(%d) successful", prog.bf_len); + + if (ioctl(x->BPF_fd, BIOCSETFNR, &prog) < 0) LogMsg("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) failed %d (%s)", prog.bf_len, errno, strerror(errno)); + else LogSPS("mDNSPlatformUpdateProxyList: BIOCSETFNR(%d) successful", prog.bf_len); } mDNSexport void mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd) @@ -3227,11 +3837,68 @@ mDNSlocal int GetMAC(mDNSEthAddr *eth, u_short ifindex) #define ifr_wake_flags ifr_ifru.ifru_intval #endif +mDNSlocal mDNSBool CheckInterfaceSupport(NetworkInterfaceInfo *const intf, const char *key) +{ + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname)); + if (!service) + { + LogSPS("CheckInterfaceSupport: No service for interface %s", intf->ifname); + return mDNSfalse; + } + + io_name_t n1, n2; + IOObjectGetClass(service, n1); + io_object_t parent; + mDNSBool ret = mDNSfalse; + kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); + if (kr == KERN_SUCCESS) + { + CFStringRef keystr = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8); + IOObjectGetClass(parent, n2); + LogSPS("CheckInterfaceSupport: Interface %s service %s parent %s", intf->ifname, n1, n2); + const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, keystr, kCFAllocatorDefault, mDNSNULL); + if (!ref) + { + LogSPS("CheckInterfaceSupport: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2); + ret = mDNSfalse; + } + else + { + ret = mDNStrue; + CFRelease(ref); + } + IOObjectRelease(parent); + CFRelease(keystr); + } + else + { + LogSPS("CheckInterfaceSupport: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr); + ret = mDNSfalse; + } + IOObjectRelease(service); + return ret; +} + + +mDNSlocal mDNSBool InterfaceSupportsKeepAlive(NetworkInterfaceInfo *const intf) +{ + return CheckInterfaceSupport(intf, mDNS_IOREG_KA_KEY); +} + mDNSlocal mDNSBool NetWakeInterface(NetworkInterfaceInfoOSX *i) { if (!MulticastInterface(i) ) return(mDNSfalse); // We only use Sleep Proxy Service on multicast-capable interfaces if (i->ifa_flags & IFF_LOOPBACK) return(mDNSfalse); // except loopback + // If the interface supports TCPKeepalive, it is capable of waking up for a magic packet + // This check is needed since the SIOCGIFWAKEFLAGS ioctl returns wrong values for WOMP capability + // when the power source is not AC Power. + if (InterfaceSupportsKeepAlive(&i->ifinfo)) + { + LogSPS("NetWakeInterface: %s supports TCP Keepalive returning true", i->ifinfo.ifname); + return mDNStrue; + } + int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { LogMsg("NetWakeInterface socket failed %s error %d errno %d (%s)", i->ifinfo.ifname, s, errno, strerror(errno)); return(mDNSfalse); } @@ -3334,6 +4001,8 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad mDNS_Unlock(m); } } + // Reset the flag if it has changed this time. + (*p)->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue; return(*p); } @@ -3352,6 +4021,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad i->ifinfo.Advertise = m->DivertMulticastAdvertisements ? ((ifa->ifa_flags & IFF_LOOPBACK) ? mDNStrue : mDNSfalse) : m->AdvertiseLocalAddresses; i->ifinfo.McastTxRx = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList i->ifinfo.Loopback = ((ifa->ifa_flags & IFF_LOOPBACK) != 0) ? mDNStrue : mDNSfalse; + i->ifinfo.IgnoreIPv4LL = ((eflags & IFEF_ARPLL) != 0) ? mDNSfalse : mDNStrue; i->next = mDNSNULL; i->m = m; @@ -3359,6 +4029,7 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad i->Flashing = mDNSfalse; i->Occulting = mDNSfalse; i->D2DInterface = (eflags & IFEF_LOCALNET_PRIVATE) ? mDNStrue: mDNSfalse; + i->DirectLink = (eflags & IFEF_DIRECTLINK) ? mDNStrue: mDNSfalse; if (eflags & IFEF_AWDL) { AWDLInterfaceID = i->ifinfo.InterfaceID; @@ -3385,18 +4056,6 @@ mDNSlocal NetworkInterfaceInfoOSX *AddInterfaceToList(mDNS *const m, struct ifad return(i); } -#if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 -mDNSlocal NetworkInterfaceInfoOSX *FindRoutableIPv4(mDNS *const m, mDNSu32 scope_id) -{ - NetworkInterfaceInfoOSX *i; - for (i = m->p->InterfaceList; i; i = i->next) - if (i->Exists && i->scope_id == scope_id && i->ifinfo.ip.type == mDNSAddrType_IPv4) - if (!mDNSv4AddressIsLinkLocal(&i->ifinfo.ip.ip.v4)) - return(i); - return(mDNSNULL); -} -#endif - #if APPLE_OSX_mDNSResponder #if COMPILER_LIKES_PRAGMA_MARK @@ -3465,7 +4124,7 @@ mDNSlocal mStatus UpdateRRStatus(const mDNS *const m, char *buffer, int bufsz, c for (ptr = m->AuthInfoList; ptr; ptr = ptr->next) if (SameDomainName(&ptr->domain, n)) { - if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey)) + if (ptr == info && (r->updateError == mStatus_BadSig || r->updateError == mStatus_BadKey || r->updateError == mStatus_BadTime)) { mDNS_snprintf(buffer, bufsz, "Resource record update failed for %##s", r->resrec.name); return r->updateError; @@ -3486,7 +4145,9 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut (void) m; (void)info; #else - const NATTraversalInfo *const llq = m->LLQNAT.clientContext ? &m->LLQNAT : mDNSNULL; + // Note that in the LLQNAT, the clientCallback being non-zero means it's in use, + // whereas in the AutoTunnelNAT, the clientContext being non-zero means it's in use + const NATTraversalInfo *const llq = m->LLQNAT.clientCallback ? &m->LLQNAT : mDNSNULL; const NATTraversalInfo *const tun = m->AutoTunnelNAT.clientContext ? &m->AutoTunnelNAT : mDNSNULL; char buffer[1024]; mDNSu32 buflen = 0; @@ -3535,16 +4196,6 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut CFRelease(tmp); } - mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &m->ExternalAddress); - tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); - if (!tmp) - LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress"); - else - { - CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp); - CFRelease(tmp); - } - if (llq) { mDNSu32 port = mDNSVal16(llq->ExternalPort); @@ -3584,6 +4235,16 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut CFRelease(num); } + mDNS_snprintf(buffer, sizeof(buffer), "%.4a", &tun->ExternalAddress); + tmp = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingUTF8); + if (!tmp) + LogMsg("UpdateAutoTunnelDomainStatus: Could not create CFString ExternalAddress"); + else + { + CFDictionarySetValue(dict, CFSTR("ExternalAddress"), tmp); + CFRelease(tmp); + } + if (tun->Result) { num = CFNumberCreate(NULL, kCFNumberSInt32Type, &tun->Result); @@ -3648,14 +4309,14 @@ mDNSlocal void UpdateAutoTunnelDomainStatus(const mDNS *const m, const DomainAut else if ((llq && llq->Result == mStatus_DoubleNAT) || (tun && tun->Result == mStatus_DoubleNAT)) { status = mStatus_DoubleNAT; - mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting an external address"); + mDNS_snprintf(buffer, sizeof(buffer), "Double NAT: Router is reporting a private address"); } else if ((llq && llq->Result == mStatus_NATPortMappingDisabled) || (tun && tun->Result == mStatus_NATPortMappingDisabled) || (m->LastNATMapResultCode == NATErr_Refused && ((llq && !llq->Result && mDNSIPPortIsZero(llq->ExternalPort)) || (tun && !tun->Result && mDNSIPPortIsZero(tun->ExternalPort))))) { status = mStatus_NATPortMappingDisabled; - mDNS_snprintf(buffer, sizeof(buffer), "NAT-PMP is disabled on the router"); + mDNS_snprintf(buffer, sizeof(buffer), "PCP/NAT-PMP is disabled on the router"); } else if ((llq && llq->Result) || (tun && tun->Result)) { @@ -3899,11 +4560,8 @@ mDNSlocal void UpdateAutoTunnelDeviceInfoRecord(mDNS *m, DomainAuthInfo *info) { mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AuthRecordAny, AutoTunnelRecordCallback, info); ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain); - mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6; - mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6); - mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len); - info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string - info->AutoTunnelDeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string + + info->AutoTunnelDeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, info->AutoTunnelDeviceInfo.resrec.rdata->u.data); info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique; mStatus err = mDNS_Register_internal(m, &info->AutoTunnelDeviceInfo); @@ -4484,10 +5142,13 @@ mDNSexport void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q) p->q.RetryWithSearchDomains = mDNSfalse; p->q.TimeoutQuestion = 0; p->q.WakeOnResolve = 0; - p->q.UseBrackgroundTrafficClass = mDNSfalse; + p->q.UseBackgroundTrafficClass = mDNSfalse; p->q.ValidationRequired = 0; p->q.ValidatingResponse = 0; + p->q.ProxyQuestion = 0; p->q.qnameOrig = mDNSNULL; + p->q.AnonInfo = mDNSNULL; + p->q.pid = mDNSPlatformGetPID(); p->q.QuestionCallback = AutoTunnelCallback; p->q.QuestionContext = p; @@ -4510,10 +5171,9 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) struct ifaddrs *v4Loopback = NULL; struct ifaddrs *v6Loopback = NULL; char defaultname[64]; -#ifndef NO_IPV6 int InfoSocket = socket(AF_INET6, SOCK_DGRAM, 0); - if (InfoSocket < 3 && errno != EAFNOSUPPORT) LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); -#endif + if (InfoSocket < 3 && errno != EAFNOSUPPORT) + LogMsg("UpdateInterfaceList: InfoSocket error %d errno %d (%s)", InfoSocket, errno, strerror(errno)); while (ifa) { @@ -4578,7 +5238,7 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) // getifaddrs is returning invalid netmask family for fw0 and vmnet ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; int ifru_flags6 = 0; -#ifndef NO_IPV6 + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0) { @@ -4590,23 +5250,25 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) ifru_flags6 = ifr6.ifr_ifru.ifru_flags6; verbosedebugf("%s %.16a %04X %04X", ifa->ifa_name, &sin6->sin6_addr, ifa->ifa_flags, ifru_flags6); } -#endif + if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY))) { if (ifa->ifa_flags & IFF_LOOPBACK) { - if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa; -#ifndef NO_IPV6 - else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa; -#endif + if (ifa->ifa_addr->sa_family == AF_INET) + v4Loopback = ifa; + else if (sin6->sin6_addr.s6_addr[0] != 0xFD) + v6Loopback = ifa; } else { NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc); if (i && MulticastInterface(i) && i->ifinfo.Advertise) { - if (ifa->ifa_addr->sa_family == AF_INET) foundav4 = mDNStrue; - else foundav6 = mDNStrue; + if (ifa->ifa_addr->sa_family == AF_INET) + foundav4 = mDNStrue; + else + foundav6 = mDNStrue; } } } @@ -4625,9 +5287,6 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) if (i->Exists) { mDNSBool txrx = MulticastInterface(i); -#if USE_V6_ONLY_WHEN_NO_ROUTABLE_V4 - txrx = txrx && ((i->ifinfo.ip.type == mDNSAddrType_IPv4) || !FindRoutableIPv4(m, i->scope_id)); -#endif if (i->ifinfo.McastTxRx != txrx) { i->ifinfo.McastTxRx = txrx; @@ -4635,9 +5294,8 @@ mDNSlocal mStatus UpdateInterfaceList(mDNS *const m, mDNSs32 utc) } } -#ifndef NO_IPV6 - if (InfoSocket >= 0) close(InfoSocket); -#endif + if (InfoSocket >= 0) + close(InfoSocket); mDNS_snprintf(defaultname, sizeof(defaultname), "%.*s-%02X%02X%02X%02X%02X%02X", HINFO_HWstring_prefixlen, HINFO_HWstring, m->PrimaryMAC.b[0], m->PrimaryMAC.b[1], m->PrimaryMAC.b[2], m->PrimaryMAC.b[3], m->PrimaryMAC.b[4], m->PrimaryMAC.b[5]); @@ -4724,7 +5382,7 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) if (i->Exists) { NetworkInterfaceInfo *const n = &i->ifinfo; - NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC); if (!primary) LogMsg("SetupActiveInterfaces ERROR! SearchForInterfaceByName didn't find %s", i->ifinfo.ifname); if (i->Registered && i->Registered != primary) // Sanity check @@ -4752,9 +5410,10 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) // as flashing and occulting, that is, flapping. If an interface is marked as flapping, // mDNS_RegisterInterface() changes the probe delay from 1/2 second to 5 seconds and // logs a warning message to system.log noting frequent interface transitions. - if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) + // Same logic applies when IFEF_DIRECTLINK flag is set on the interface. + if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->DirectLink) { - LogInfo("SetupActiveInterfaces: P2P %s interface registering %s %s", i->ifinfo.ifname, + LogInfo("SetupActiveInterfaces: %s interface registering %s %s", i->ifinfo.ifname, i->Flashing ? " (Flashing)" : "", i->Occulting ? " (Occulting)" : ""); mDNS_RegisterInterface(m, n, 0); @@ -4806,7 +5465,6 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) if (err < 0 && (errno != EADDRINUSE)) LogMsg("setsockopt - IP_ADD_MEMBERSHIP error %d errno %d (%s) group %.4a on %.4a", err, errno, strerror(errno), &imr.imr_multiaddr, &imr.imr_interface); } -#ifndef NO_IPV6 if (i->sa_family == AF_INET6) { struct ipv6_mreq i6mr; @@ -4827,7 +5485,6 @@ mDNSlocal int SetupActiveInterfaces(mDNS *const m, mDNSs32 utc) if (err < 0 && (errno != EADDRINUSE)) LogMsg("setsockopt - IPV6_JOIN_GROUP error %d errno %d (%s) group %.16a on %u", err, errno, strerror(errno), &i6mr.ipv6mr_multiaddr, i6mr.ipv6mr_interface); } -#endif } } } @@ -4858,7 +5515,7 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) for (i = m->p->InterfaceList; i; i = i->next) { // If this interface is no longer active, or its InterfaceID is changing, deregister it - NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AAAA_OVER_V4 ? AF_UNSPEC : i->sa_family); + NetworkInterfaceInfoOSX *primary = SearchForInterfaceByName(m, i->ifinfo.ifname, AF_UNSPEC); if (i->Registered) if (i->Exists == 0 || i->Exists == 2 || i->Registered != primary) { @@ -4875,9 +5532,10 @@ mDNSlocal int ClearInactiveInterfaces(mDNS *const m, mDNSs32 utc) // as flashing and occulting. The "core" does not flush the cache for this case. This leads to // stale data returned to the application even after the interface is removed. The application // then starts to send data but the new interface is not yet created. - if (strncmp(i->ifinfo.ifname, "p2p", 3) == 0) + // Same logic applies when IFEF_DIRECTLINK flag is set on the interface. + if ((strncmp(i->ifinfo.ifname, "p2p", 3) == 0) || i->DirectLink) { - LogInfo("ClearInactiveInterfaces: P2P %s interface deregistering %s %s", i->ifinfo.ifname, + LogInfo("ClearInactiveInterfaces: %s interface deregistering %s %s", i->ifinfo.ifname, i->Flashing ? " (Flashing)" : "", i->Occulting ? " (Occulting)" : ""); mDNS_DeregisterInterface(m, &i->ifinfo, 0); @@ -5001,346 +5659,405 @@ mDNSlocal void FinalizeSearchDomainHash(mDNS *const m, MD5_CTX *sdc) else { LogInfo("FinalizeSearchDomains: The hash is same"); } } -// ConfigResolvers is called twice - once to parse the "scoped_resolver" list and second time to parse the "resolver" list. -// "scoped_resolver" has entries that should only be used for "scoped_questions" (for questions that specify an interface index -// q->InterfaceID) and "resolver" entries should only be used for non-scoped questions. Entries in either of the list can specify -// an ifindex. This means that the dns query should be scoped to that interface when sent out on the wire. The flag value -// "DNS_RESOLVER_FLAGS_SCOPED" itself appears only in "scoped" list of resolvers. -// -// Before "scoped_resolver" was introduced, the entries in "resolver" list can contain options like "interface=en0" which -// was meant to scope the query (non-scoped queries) to a specific interface. We still support this option. On top of that, -// we also support a new way of specifying the interface index as described above. -mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSBool scope, mDNSBool setsearch, mDNSBool setservers, MD5_CTX *sdc, mDNSu16 resGroupID) +mDNSexport const char *DNSScopeToString(mDNSu32 scope) +{ + switch (scope) + { + case kScopeNone: + return "Unscoped"; + case kScopeInterfaceID: + return "InterfaceScoped"; + case kScopeServiceID: + return "ServiceScoped"; + default: + return "Unknown"; + } +} + +mDNSlocal void ConfigSearchDomains(mDNS *const m, dns_resolver_t *resolver, mDNSInterfaceID interface, mDNSu32 scope, MD5_CTX *sdc) +{ + const char *scopeString = DNSScopeToString(scope); + int j; + + if (scope != kScopeNone) + { + LogInfo("ConfigSearchDomains: (%s) Ignoring search domain for Interface %p", scopeString, interface); + return; + } + for (j = 0; j < resolver->n_search; j++) + { + LogInfo("ConfigSearchDomains: (%s) configuring search list %s", scopeString, resolver->search[j]); + UpdateSearchDomainHash(m, sdc, resolver->search[j], NULL); + mDNS_AddSearchDomain_CString(resolver->search[j], NULL); + } +} + +mDNSlocal mDNSInterfaceID ConfigParseInterfaceID(mDNS *const m, mDNSu32 ifindex) +{ + NetworkInterfaceInfoOSX *ni; + mDNSInterfaceID interface; + + for (ni = m->p->InterfaceList; ni; ni = ni->next) + { + if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) + break; + } + if (ni != NULL) + { + interface = ni->ifinfo.InterfaceID; + } + else + { + // In rare circumstances, we could potentially hit this case where we cannot parse the InterfaceID + // (see ). At this point, we still accept the DNS Config from configd + // Note: We currently ack the whole dns configuration and not individual resolvers or DNS servers. + // As the caller is going to ack the configuration always, we have to add all the DNS servers + // in the configuration. Otherwise, we won't have any DNS servers up until the network change. + + LogMsg("ConfigParseInterfaceID: interface specific index %d not found (interface may not be UP)",ifindex); + + // Set the correct interface from configd before passing this to mDNS_AddDNSServer() below + interface = (mDNSInterfaceID)(unsigned long)ifindex; + } + return interface; +} + +mDNSlocal void ConfigNonUnicastResolver(mDNS *const m, dns_resolver_t *r) +{ + char *opt = r->options; + domainname d; + + if (opt && !strncmp(opt, "mdns", strlen(opt))) + { + if (!MakeDomainNameFromDNSNameString(&d, r->domain)) + { + LogMsg("ConfigNonUnicastResolver: config->resolver bad domain %s", r->domain); + return; + } + mDNS_AddMcastResolver(m, &d, mDNSInterface_Any, r->timeout); + } +} + +mDNSlocal void ConfigDNSServers(mDNS *const m, dns_resolver_t *r, mDNSInterfaceID interface, mDNSu32 scope, mDNSu16 resGroupID) { - int i, j; + int n; domainname d; -#if DNSINFO_VERSION >= 20091104 - dns_resolver_t **resolver = scope ? config->scoped_resolver : config->resolver; - int nresolvers = scope ? config->n_scoped_resolver : config->n_resolver; -#else - (void) scope; // unused - dns_resolver_t **resolver = config->resolver; - int nresolvers = config->n_resolver; + int serviceID = 0; + mDNSBool cellIntf = mDNSfalse; + mDNSBool scopedDNS = mDNSfalse; + mDNSBool reqA, reqAAAA; + + if (!r->domain || !*r->domain) + { + d.c[0] = 0; + } + else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) + { + LogMsg("ConfigDNSServers: bad domain %s", r->domain); + return; + } + // Parse the resolver specific attributes that affects all the DNS servers. + if (scope == kScopeInterfaceID) + { + scopedDNS = mDNStrue; + } + else if (scope == kScopeServiceID) + { + serviceID = r->service_identifier; + } + +#if TARGET_OS_IPHONE + cellIntf = (r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN) ? mDNStrue : mDNSfalse; #endif + reqA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_A_RECORDS ? mDNStrue : mDNSfalse); + reqAAAA = (r->flags & DNS_RESOLVER_FLAGS_REQUEST_AAAA_RECORDS ? mDNStrue : mDNSfalse); - if (setsearch && !scope && nresolvers) + for (n = 0; n < r->n_nameserver; n++) { - // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains - // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if - // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and - // instead use the search domain list as the sole authority for what domains to search and in what order - // (and the domain from the "domain" field will also appear somewhere in that list). - // Also, all search domains get added to the search list for resolver[0], so the domains and/or - // search lists for other resolvers in the list need to be ignored. - // - // Note: Starting DNSINFO_VERSION 20091104, search list is present only in the first resolver (resolver 0). - // i.e., n_search for the first resolver is always non-zero. We don't guard it with #ifs for better readability + mDNSAddr saddr; + DNSServer *s; - if (resolver[0]->n_search == 0) + if (r->nameserver[n]->sa_family != AF_INET && r->nameserver[n]->sa_family != AF_INET6) + continue; + + if (SetupAddr(&saddr, r->nameserver[n])) { - LogInfo("ConfigResolvers: (%s) configuring zeroth domain as search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->domain); - UpdateSearchDomainHash(m, sdc, resolver[0]->domain, NULL); - mDNS_AddSearchDomain_CString(resolver[0]->domain, mDNSNULL); + LogMsg("ConfigDNSServers: Bad address"); + continue; } - else + + // The timeout value is for all the DNS servers in a given resolver, hence we pass + // the timeout value only for the first DNSServer. If we don't have a value in the + // resolver, then use the core's default value + // + // Note: this assumes that when the core picks a list of DNSServers for a question, + // it takes the sum of all the timeout values for all DNS servers. By doing this, it + // tries all the DNS servers in a specified timeout + s = mDNS_AddDNSServer(m, &d, interface, serviceID, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scope, + (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf, resGroupID, reqA, reqAAAA, mDNStrue); + if (s) { - for (i = 0; i < resolver[0]->n_search; i++) - { - LogInfo("ConfigResolvers: (%s) configuring search list %s", scope ? "Scoped" : "Non-scoped", resolver[0]->search[i]); - UpdateSearchDomainHash(m, sdc, resolver[0]->search[i], NULL); - mDNS_AddSearchDomain_CString(resolver[0]->search[i], mDNSNULL); - } + LogInfo("ConfigDNSServers(%s): DNS server %#a:%d for domain %##s", DNSScopeToString(scope), &s->addr, mDNSVal16(s->port), d.c); } } +} - // scoped search domains are set below. If neither scoped nor setting servers, we have nothing to do - if (!scope && !setservers) return; - - // For the "default" resolver ("resolver #1") the "domain" value is bogus and we need to ignore it. - // e.g. the default resolver's "domain" value might say "apple.com", which indicates that this resolver - // is only for names that fall under "apple.com", but that's not correct. Actually the default resolver is - // for all names not covered by a more specific resolver (i.e. its domain should be ".", the root domain). - // - // Note: Starting DNSINFO_VERSION 20091104, domain value of this first resolver (resolver 0) is always NULL. - // We don't guard it with #ifs for better readability - // - if ((nresolvers != 0) && resolver[0]->domain) - resolver[0]->domain[0] = 0; // don't stop pointing at the memory, just change the first byte +// ConfigResolvers is called for different types of resolvers: Unscoped resolver, Interface scope resolver and +// Service scope resolvers. This is indicated by the scope argument. +// +// "resolver" has entries that should only be used for unscoped questions. +// +// "scoped_resolver" has entries that should only be used for Interface scoped question i.e., questions that specify an +// interface index (q->InterfaceID) +// +// "service_specific_resolver" has entries that should be used for Service scoped question i.e., questions that specify +// a service identifier (q->ServiceID) +// +mDNSlocal void ConfigResolvers(mDNS *const m, dns_config_t *config, mDNSu32 scope, mDNSBool setsearch, mDNSBool setservers, MD5_CTX *sdc, mDNSu16 resGroupID) +{ + int i; + dns_resolver_t **resolver; + int nresolvers; + const char *scopeString = DNSScopeToString(scope); + mDNSInterfaceID interface; + switch (scope) + { + case kScopeNone: + resolver = config->resolver; + nresolvers = config->n_resolver; + break; + case kScopeInterfaceID: + resolver = config->scoped_resolver; + nresolvers = config->n_scoped_resolver; + break; + case kScopeServiceID: + resolver = config->service_specific_resolver; + nresolvers = config->n_service_specific_resolver; + break; + default: + return; + } qsort(resolver, nresolvers, sizeof(dns_resolver_t*), compare_dns_configs); for (i = 0; i < nresolvers; i++) { - int n; dns_resolver_t *r = resolver[i]; - mDNSInterfaceID interface = mDNSInterface_Any; - int disabled = 0; - - LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scope ? "Scoped" : "", i, r->domain, r->n_nameserver); - - // On Tiger, dnsinfo entries for mDNS domains have port 5353, the mDNS port. Ignore them. - // Note: Unlike the BSD Sockets APIs (where TCP and UDP port numbers are universally in network byte order) - // in Apple's "dnsinfo.h" API the port number is declared to be a "uint16_t in host byte order" - // We also don't need to do any more work if there are no nameserver addresses - if (r->port == 5353 || r->n_nameserver == 0) - { - char *opt = r->options; - if (opt && !strncmp(opt, "mdns", strlen(opt))) - { - if (!MakeDomainNameFromDNSNameString(&d, r->domain)) - { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } - mDNS_AddMcastResolver(m, &d, interface, r->timeout); - } - continue; - } + LogInfo("ConfigResolvers: %s resolver[%d] domain %s n_nameserver %d", scopeString, i, r->domain, r->n_nameserver); - if (!r->domain || !*r->domain) d.c[0] = 0; - else if (!MakeDomainNameFromDNSNameString(&d, r->domain)) - { LogMsg("ConfigResolvers: config->resolver[%d] bad domain %s", i, r->domain); continue; } + interface = mDNSInterface_Any; - // DNS server option parsing - if (r->options != NULL) + // Parse the interface index + if (r->if_index != 0) { - char *nextOption = r->options; - char *currentOption = NULL; - while ((currentOption = strsep(&nextOption, " ")) != NULL && currentOption[0] != 0) - { - // The option may be in the form of interface=xxx where xxx is an interface name. - if (strncmp(currentOption, kInterfaceSpecificOption, sizeof(kInterfaceSpecificOption) - 1) == 0) - { - NetworkInterfaceInfoOSX *ni; - char ifname[IF_NAMESIZE+1]; - mDNSu32 ifindex = 0; - // If something goes wrong finding the interface, create the server entry anyhow but mark it as disabled. - // This allows us to block these special queries from going out on the wire. - strlcpy(ifname, currentOption + sizeof(kInterfaceSpecificOption)-1, sizeof(ifname)); - ifindex = if_nametoindex(ifname); - if (ifindex == 0) { disabled = 1; LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - interface %s not found", ifname); continue; } - LogInfo("ConfigResolvers: interface specific entry: %s on %s (%d)", r->domain, ifname, ifindex); - // Find the interface. Can't use mDNSPlatformInterfaceIDFromInterfaceIndex - // because that will call mDNSMacOSXNetworkChanged if the interface doesn't exist - for (ni = m->p->InterfaceList; ni; ni = ni->next) - if (ni->ifinfo.InterfaceID && ni->scope_id == ifindex) break; - if (ni != NULL) interface = ni->ifinfo.InterfaceID; - if (interface == mDNSNULL) - { - disabled = 1; - LogMsg("ConfigResolvers: RegisterSplitDNS interface specific - index %d (%s) not found", ifindex, ifname); - continue; - } - } - } + interface = ConfigParseInterfaceID(m, r->if_index); } - // flags and if_index are defined only from this DNSINFO_VERSION onwards. - // Parse the interface index if we have not already parsed one above. -#if DNSINFO_VERSION >= 20091104 - if ((interface == mDNSInterface_Any) && (r->if_index != 0)) + if (setsearch) { - NetworkInterfaceInfoOSX *ni; - interface = mDNSNULL; - for (ni = m->p->InterfaceList; ni; ni = ni->next) - if (ni->ifinfo.InterfaceID && ni->scope_id == r->if_index) break; - if (ni != NULL) interface = ni->ifinfo.InterfaceID; - if (interface == mDNSNULL) - { - disabled = 1; - LogMsg("ConfigResolvers: interface specific index %d not found", r->if_index); + ConfigSearchDomains(m, resolver[i], interface, scope, sdc); + // Parse other scoped resolvers for search lists + if (!setservers) continue; - } } -#endif - if (setsearch) + if (r->port == 5353 || r->n_nameserver == 0) { - // For non-scoped resolvers unlike scoped resolvers, only zeroth resolver has search lists if any. For scoped - // resolvers, we need to parse all the entries. - if (scope) - { - for (j = 0; j < resolver[i]->n_search; j++) - { - LogInfo("ConfigResolvers: (%s) configuring search list %s, Interface %p", scope ? "Scoped" : "Non-scoped", resolver[i]->search[j], interface); - UpdateSearchDomainHash(m, sdc, resolver[i]->search[j], interface); - mDNS_AddSearchDomain_CString(resolver[i]->search[j], interface); - } - // Parse other scoped resolvers for search lists - if (!setservers) continue; - } + ConfigNonUnicastResolver(m, r); } + else + { + // Each scoped resolver gets its own ID (i.e., they are in their own group) so that responses from the + // scoped resolver are not used by other non-scoped or scoped resolvers. + if (scope != kScopeNone) + resGroupID++; - // Each scoped resolver gets its own ID (i.e., they are in their own group) so that responses from the - // scoped resolver are not used by other non-scoped or scoped resolvers. - if (scope) resGroupID++; + ConfigDNSServers(m, r, interface, scope, resGroupID); + } + } +} - for (n = 0; n < r->n_nameserver; n++) - if (r->nameserver[n]->sa_family == AF_INET || r->nameserver[n]->sa_family == AF_INET6) - { - mDNSAddr saddr; - // mDNSAddr saddr = { mDNSAddrType_IPv4, { { { 192, 168, 1, 1 } } } }; // for testing - if (SetupAddr(&saddr, r->nameserver[n])) LogMsg("RegisterSplitDNS: bad IP address"); - else - { - mDNSBool cellIntf = mDNSfalse; - mDNSBool scopedDNS = mDNSfalse; - DNSServer *s; -#if DNSINFO_VERSION >= 20091104 - // By setting scoped, this DNSServer can only be picked if the right interfaceID - // is given in the question. - if (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED) && (interface == mDNSNULL)) - LogMsg("ConfigResolvers: ERROR: scoped is set but if_index %d is invalid for DNSServer %#a:%d", - r->if_index, &saddr, mDNSVal16(r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort)); - else - scopedDNS = (scope && (r->flags & DNS_RESOLVER_FLAGS_SCOPED)) ? mDNStrue : mDNSfalse; -#endif -#if (DNSINFO_VERSION >= 20110420) && TARGET_OS_IPHONE - cellIntf = r->reach_flags & kSCNetworkReachabilityFlagsIsWWAN; -#endif - // The timeout value is for all the DNS servers in a given resolver, hence we pass - // the timeout value only for the first DNSServer. If we don't have a value in the - // resolver, then use the core's default value - // - // Note: this assumes that when the core picks a list of DNSServers for a question, - // it takes the sum of all the timeout values for all DNS servers. By doing this, it - // tries all the DNS servers in a specified timeout - s = mDNS_AddDNSServer(m, &d, interface, &saddr, r->port ? mDNSOpaque16fromIntVal(r->port) : UnicastDNSPort, scopedDNS, - (n == 0 ? (r->timeout ? r->timeout : DEFAULT_UDNS_TIMEOUT) : 0), cellIntf, resGroupID); - if (s) - { - if (disabled) s->teststate = DNSServer_Disabled; - LogInfo("ConfigResolvers: DNS server %#a:%d for domain %##s from slot %d, %d", - &s->addr, mDNSVal16(s->port), d.c, i, n); - } - } - } +#if APPLE_OSX_mDNSResponder +mDNSlocal mDNSBool QuestionValidForDNSTrigger(DNSQuestion *q) +{ + if (QuerySuppressed(q)) + { + debugf("QuestionValidForDNSTrigger: Suppressed: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + if (mDNSOpaque16IsZero(q->TargetQID)) + { + debugf("QuestionValidForDNSTrigger: Multicast: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; } + // If we answered using LocalOnly records e.g., /etc/hosts, don't consider that a valid response + // for trigger. + if (q->LOAddressAnswers) + { + debugf("QuestionValidForDNSTrigger: LocalOnly answers: %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + return mDNStrue; } +#endif -mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains) +// This function is called if we are not delivering unicast answers to "A" or "AAAA" questions. +// We set our state appropriately so that if we start receiving answers, trigger the +// upper layer to retry DNS questions. +#if APPLE_OSX_mDNSResponder +mDNSexport void mDNSPlatformUpdateDNSStatus(mDNS *const m, DNSQuestion *q) { - int i; - char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL - domainname d; - MD5_CTX sdc; // search domain context - static mDNSu16 resolverGroupID = 0; + if (!QuestionValidForDNSTrigger(q)) + return; - // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed - if (fqdn) fqdn->c[0] = 0; - if (RegDomains ) *RegDomains = NULL; - if (BrowseDomains) *BrowseDomains = NULL; + // Ignore applications that start and stop queries for no reason before we ever talk + // to any DNS server. + if (!q->triedAllServersOnce) + { + LogInfo("QuestionValidForDNSTrigger: question %##s (%s) stopped too soon", q->qname.c, DNSTypeName(q->qtype)); + return; + } + if (q->qtype == kDNSType_A) + m->p->v4answers = 0; + if (q->qtype == kDNSType_AAAA) + m->p->v6answers = 0; + if (!m->p->v4answers || !m->p->v6answers) + { + LogInfo("mDNSPlatformUpdateDNSStatus: Trigger needed v4 %d, v6 %d, quesiton %##s (%s)", m->p->v4answers, m->p->v6answers, q->qname.c, + DNSTypeName(q->qtype)); + } +} +#endif - LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s", - setservers ? " setservers" : "", - setsearch ? " setsearch" : "", - fqdn ? " fqdn" : "", - RegDomains ? " RegDomains" : "", - BrowseDomains ? " BrowseDomains" : ""); +mDNSlocal void AckConfigd(mDNS *const m, dns_config_t *config) +{ + mDNS_CheckLock(m); - if (setsearch) MD5_Init(&sdc); + // Acking the configuration triggers configd to reissue the reachability queries + m->p->DNSTrigger = NonZeroTime(m->timenow); + _dns_configuration_ack(config, "com.apple.mDNSResponder"); +} - // Add the inferred address-based configuration discovery domains - // (should really be in core code I think, not platform-specific) - if (setsearch) +// If v4q is non-NULL, it means we have received some answers for "A" type questions +// If v6q is non-NULL, it means we have received some answers for "AAAA" type questions +#if APPLE_OSX_mDNSResponder +mDNSexport void mDNSPlatformTriggerDNSRetry(mDNS *const m, DNSQuestion *v4q, DNSQuestion *v6q) +{ + mDNSBool trigger = mDNSfalse; + mDNSs32 timenow; + + // Don't send triggers too often. + // If we have started delivering answers to questions, we should send a trigger + // if the time permits. If we are delivering answers, we should set the state + // of v4answers/v6answers to 1 and avoid sending a trigger. But, we don't know + // whether the answers that are being delivered currently is for configd or some + // other application. If we set the v4answers/v6answers to 1 and not deliver a trigger, + // then we won't deliver the trigger later when it is okay to send one as the + // "answers" are already set to 1. Hence, don't affect the state of v4answers and + // v6answers if we are not delivering triggers. + mDNS_Lock(m); + timenow = m->timenow; + if (m->p->DNSTrigger && (timenow - m->p->DNSTrigger) < DNS_TRIGGER_INTERVAL) { - struct ifaddrs *ifa = mDNSNULL; - struct sockaddr_in saddr; - mDNSPlatformMemZero(&saddr, sizeof(saddr)); - saddr.sin_len = sizeof(saddr); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4; + if (!m->p->v4answers || !m->p->v6answers) + { + debugf("mDNSPlatformTriggerDNSRetry: not triggering, time since last trigger %d ms, v4ans %d, v6ans %d", + (timenow - m->p->DNSTrigger), m->p->v4answers, m->p->v6answers); + } + mDNS_Unlock(m); + return; + } + mDNS_Unlock(m); + if (v4q != NULL && QuestionValidForDNSTrigger(v4q)) + { + int old = m->p->v4answers; - // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation - if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa = myGetIfAddrs(1); + m->p->v4answers = 1; - while (ifa) + // If there are IPv4 answers now and previously we did not have + // any answers, trigger a DNS change so that reachability + // can retry the queries again. + if (!old) { - mDNSAddr a, n; - if (ifa->ifa_addr->sa_family == AF_INET && - ifa->ifa_netmask && - !(ifa->ifa_flags & IFF_LOOPBACK) && - !SetupAddr(&a, ifa->ifa_addr) && - !mDNSv4AddressIsLinkLocal(&a.ip.v4) ) - { - // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr - // getifaddrs is returning invalid netmask family for fw0 and vmnet - ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; // Make sure ifa_netmask->sa_family is set correctly - SetupAddr(&n, ifa->ifa_netmask); - // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code - mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3], - a.ip.v4.b[2] & n.ip.v4.b[2], - a.ip.v4.b[1] & n.ip.v4.b[1], - a.ip.v4.b[0] & n.ip.v4.b[0]); - UpdateSearchDomainHash(m, &sdc, buf, NULL); - mDNS_AddSearchDomain_CString(buf, mDNSNULL); - } - ifa = ifa->ifa_next; + LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv4, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger), + v4q->qname.c, DNSTypeName(v4q->qtype)); + trigger = mDNStrue; } } + if (v6q != NULL && QuestionValidForDNSTrigger(v6q)) + { + int old = m->p->v6answers; -#ifndef MDNS_NO_DNSINFO - if (setservers || setsearch) + m->p->v6answers = 1; + // If there are IPv6 answers now and previously we did not have + // any answers, trigger a DNS change so that reachability + // can retry the queries again. + if (!old) + { + LogInfo("mDNSPlatformTriggerDNSRetry: Triggering because of IPv6, last trigger %d ms, %##s (%s)", (timenow - m->p->DNSTrigger), + v6q->qname.c, DNSTypeName(v6q->qtype)); + trigger = mDNStrue; + } + } + if (trigger) { dns_config_t *config = dns_configuration_copy(); - if (!config) + if (config) { - // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed - // On 10.4, calls to dns_configuration_copy() early in the boot process often fail. - // Apparently this is expected behaviour -- "not a bug". - // Accordingly, we suppress syslog messages for the first three minutes after boot. - // If we are still getting failures after three minutes, then we log them. - if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180)) - LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL"); + mDNS_Lock(m); + AckConfigd(m, config); + mDNS_Unlock(m); + dns_configuration_free(config); } else { - LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver); + LogMsg("mDNSPlatformTriggerDNSRetry: ERROR!! configd did not return config"); + } + } +} -#if APPLE_OSX_mDNSResponder - // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up - // by someone using Microsoft Active Directory using "local" as a private internal top-level domain - if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && config->resolver[0]->nameserver[0]) - MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain); - else ActiveDirectoryPrimaryDomain.c[0] = 0; - //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local"); - ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain); - if (config->n_resolver && config->resolver[0]->n_nameserver && SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain)) - SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]); - else - { - AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)""); - ActiveDirectoryPrimaryDomainLabelCount = 0; - ActiveDirectoryPrimaryDomainServer = zeroAddr; - } -#endif +mDNSlocal void SetupActiveDirectoryDomain(dns_config_t *config) +{ + // Record the so-called "primary" domain, which we use as a hint to tell if the user is on a network set up + // by someone using Microsoft Active Directory using "local" as a private internal top-level domain + if (config->n_resolver && config->resolver[0]->domain && config->resolver[0]->n_nameserver && + config->resolver[0]->nameserver[0]) + { + MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, config->resolver[0]->domain); + } + else + { + ActiveDirectoryPrimaryDomain.c[0] = 0; + } - // With scoped DNS, we don't want to answer a non-scoped question using a scoped cache entry - // and vice-versa. As we compare resolverGroupID for matching cache entry with question, we need - // to make sure that they don't match. We ensure this by always bumping up resolverGroupID between - // the two calls to ConfigResolvers DNSServers for scoped and non-scoped can never have the - // same resolverGroupID. - // - // All non-scoped resolvers use the same resolverGroupID i.e, we treat them all equally. - ConfigResolvers(m, config, mDNSfalse, setsearch, setservers, &sdc, ++resolverGroupID); - resolverGroupID += config->n_resolver; -#if DNSINFO_VERSION >= 20091104 - ConfigResolvers(m, config, mDNStrue, setsearch, setservers, &sdc, resolverGroupID); -#endif - // Acking provides a hint that we processed this current configuration and - // we will use that from now on, assuming we don't get another one immediately - // after we return from here. - _dns_configuration_ack(config, "com.apple.mDNSResponder"); - dns_configuration_free(config); - if (setsearch) FinalizeSearchDomainHash(m, &sdc); - setservers = mDNSfalse; // Done these now -- no need to fetch the same data from SCDynamicStore - setsearch = mDNSfalse; - } + //MakeDomainNameFromDNSNameString(&ActiveDirectoryPrimaryDomain, "test.local"); + ActiveDirectoryPrimaryDomainLabelCount = CountLabels(&ActiveDirectoryPrimaryDomain); + if (config->n_resolver && config->resolver[0]->n_nameserver && + SameDomainName(SkipLeadingLabels(&ActiveDirectoryPrimaryDomain, ActiveDirectoryPrimaryDomainLabelCount - 1), &localdomain)) + { + SetupAddr(&ActiveDirectoryPrimaryDomainServer, config->resolver[0]->nameserver[0]); } -#endif // MDNS_NO_DNSINFO + else + { + AssignDomainName(&ActiveDirectoryPrimaryDomain, (const domainname *)""); + ActiveDirectoryPrimaryDomainLabelCount = 0; + ActiveDirectoryPrimaryDomainServer = zeroAddr; + } +} +#endif + +mDNSlocal void SetupDDNSDomains(domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains) +{ + int i; + char buf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL + domainname d; SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformSetDNSConfig"), NULL, NULL); if (!store) - LogMsg("mDNSPlatformSetDNSConfig: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + { + LogMsg("SetupDDNSDomains: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); + } else { CFDictionaryRef ddnsdict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DynamicDNS); @@ -5448,72 +6165,148 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN } CFRelease(btmm); } - } + } + CFRelease(store); + } +} + +// Returns mDNSfalse, if it does not set the configuration i.e., if the DNS configuration did not change +mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, + DNameListElem **RegDomains, DNameListElem **BrowseDomains, mDNSBool ackConfig) +{ + MD5_CTX sdc; // search domain context + static mDNSu16 resolverGroupID = 0; + + // Need to set these here because we need to do this even if SCDynamicStoreCreate() or SCDynamicStoreCopyValue() below don't succeed + if (fqdn) fqdn->c[0] = 0; + if (RegDomains ) *RegDomains = NULL; + if (BrowseDomains) *BrowseDomains = NULL; + + LogInfo("mDNSPlatformSetDNSConfig:%s%s%s%s%s", + setservers ? " setservers" : "", + setsearch ? " setsearch" : "", + fqdn ? " fqdn" : "", + RegDomains ? " RegDomains" : "", + BrowseDomains ? " BrowseDomains" : ""); + + if (setsearch) MD5_Init(&sdc); + + // Add the inferred address-based configuration discovery domains + // (should really be in core code I think, not platform-specific) + if (setsearch) + { + struct ifaddrs *ifa = mDNSNULL; + struct sockaddr_in saddr; + mDNSPlatformMemZero(&saddr, sizeof(saddr)); + saddr.sin_len = sizeof(saddr); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = *(in_addr_t *)&m->Router.ip.v4; + + // Don't add any reverse-IP search domains if doing the WAB bootstrap queries would cause dial-on-demand connection initiation + if (!AddrRequiresPPPConnection((struct sockaddr *)&saddr)) ifa = myGetIfAddrs(1); + + while (ifa) + { + mDNSAddr a, n; + char buf[64]; + + if (ifa->ifa_addr->sa_family == AF_INET && + ifa->ifa_netmask && + !(ifa->ifa_flags & IFF_LOOPBACK) && + !SetupAddr(&a, ifa->ifa_addr) && + !mDNSv4AddressIsLinkLocal(&a.ip.v4) ) + { + // Apparently it's normal for the sa_family of an ifa_netmask to sometimes be incorrect, so we explicitly fix it here before calling SetupAddr + // getifaddrs is returning invalid netmask family for fw0 and vmnet + ifa->ifa_netmask->sa_family = ifa->ifa_addr->sa_family; // Make sure ifa_netmask->sa_family is set correctly + SetupAddr(&n, ifa->ifa_netmask); + // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code + mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", a.ip.v4.b[3] & n.ip.v4.b[3], + a.ip.v4.b[2] & n.ip.v4.b[2], + a.ip.v4.b[1] & n.ip.v4.b[1], + a.ip.v4.b[0] & n.ip.v4.b[0]); + UpdateSearchDomainHash(m, &sdc, buf, NULL); + mDNS_AddSearchDomain_CString(buf, mDNSNULL); + } + ifa = ifa->ifa_next; + } + } + +#ifndef MDNS_NO_DNSINFO + if (setservers || setsearch) + { + dns_config_t *config = dns_configuration_copy(); + if (!config) + { + // When running on 10.3 (build 7xxx) and earlier, we don't expect dns_configuration_copy() to succeed + // On 10.4, calls to dns_configuration_copy() early in the boot process often fail. + // Apparently this is expected behaviour -- "not a bug". + // Accordingly, we suppress syslog messages for the first three minutes after boot. + // If we are still getting failures after three minutes, then we log them. + if ((mDNSu32)mDNSPlatformRawTime() > (mDNSu32)(mDNSPlatformOneSecond * 180)) + LogMsg("mDNSPlatformSetDNSConfig: Error: dns_configuration_copy returned NULL"); + } + else + { + LogInfo("mDNSPlatformSetDNSConfig: config->n_resolver = %d, generation %llu", config->n_resolver, config->generation); + if (m->p->LastConfigGeneration == config->generation) + { + LogInfo("mDNSPlatformSetDNSConfig: generation number %llu same, not processing", config->generation); + dns_configuration_free(config); + SetupDDNSDomains(fqdn, RegDomains, BrowseDomains); + return mDNSfalse; + } +#if APPLE_OSX_mDNSResponder + SetupActiveDirectoryDomain(config); +#endif - if (setservers || setsearch) - { - CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_DNS); - if (dict) + // With scoped DNS, we don't want to answer a non-scoped question using a scoped cache entry + // and vice-versa. As we compare resolverGroupID for matching cache entry with question, we need + // to make sure that they don't match. We ensure this by always bumping up resolverGroupID between + // the two calls to ConfigResolvers DNSServers for scoped and non-scoped can never have the + // same resolverGroupID. + // + // All non-scoped resolvers use the same resolverGroupID i.e, we treat them all equally. + ConfigResolvers(m, config, kScopeNone, setsearch, setservers, &sdc, ++resolverGroupID); + resolverGroupID += config->n_resolver; + + ConfigResolvers(m, config, kScopeInterfaceID, setsearch, setservers, &sdc, resolverGroupID); + resolverGroupID += config->n_scoped_resolver; + + ConfigResolvers(m, config, kScopeServiceID, setsearch, setservers, &sdc, resolverGroupID); + + // Acking provides a hint that we processed this current configuration and + // we will use that from now on, assuming we don't get another one immediately + // after we return from here. + if (ackConfig) { - if (setservers) - { - CFArrayRef values = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); - if (values) - { - LogInfo("DNS Server Address values: %d", (int)CFArrayGetCount(values)); - for (i = 0; i < CFArrayGetCount(values); i++) - { - CFStringRef s = CFArrayGetValueAtIndex(values, i); - mDNSAddr addr = { mDNSAddrType_IPv4, { { { 0 } } } }; - if (s && CFStringGetCString(s, buf, 256, kCFStringEncodingUTF8) && - inet_aton(buf, (struct in_addr *) &addr.ip.v4)) - { - LogInfo("Adding DNS server from dict: %s", buf); - mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse, 0); - } - } - } - else LogInfo("No DNS Server Address values"); - } - if (setsearch) - { - // Add the manual and/or DHCP-dicovered search domains - CFArrayRef searchDomains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); - if (searchDomains) - { - for (i = 0; i < CFArrayGetCount(searchDomains); i++) - { - CFStringRef s = CFArrayGetValueAtIndex(searchDomains, i); - if (s && CFStringGetCString(s, buf, sizeof(buf), kCFStringEncodingUTF8)) - { - UpdateSearchDomainHash(m, &sdc, buf, NULL); - mDNS_AddSearchDomain_CString(buf, mDNSNULL); - } - } - } - else // No kSCPropNetDNSSearchDomains, so use kSCPropNetDNSDomainName - { - // Due to the vagaries of Apple's SystemConfiguration and dnsinfo.h APIs, if there are no search domains - // listed, then you're supposed to interpret the "domain" field as also being the search domain, but if - // there *are* search domains listed, then you're supposed to ignore the "domain" field completely and - // instead use the search domain list as the sole authority for what domains to search and in what order - // (and the domain from the "domain" field will also appear somewhere in that list). - CFStringRef string = CFDictionaryGetValue(dict, kSCPropNetDNSDomainName); - if (string && CFStringGetCString(string, buf, sizeof(buf), kCFStringEncodingUTF8)) - { - UpdateSearchDomainHash(m, &sdc, buf, NULL); - mDNS_AddSearchDomain_CString(buf, mDNSNULL); - } - } - FinalizeSearchDomainHash(m, &sdc); - } - CFRelease(dict); + // Note: We have to set the generation number here when we are acking. + // For every DNS configuration change, we do the following: + // + // 1) Copy dns configuration, handle search domains change + // 2) Copy dns configuration, handle dns server change + // + // If we update the generation number at step (1), we won't process the + // DNS servers the second time because generation number would be the same. + // As we ack only when we process dns servers, we set the generation number + // during acking. + m->p->LastConfigGeneration = config->generation; + LogInfo("mDNSPlatformSetDNSConfig: Acking configuration setservers %d, setsearch %d", setservers, setsearch); + AckConfigd(m, config); } + dns_configuration_free(config); + if (setsearch) FinalizeSearchDomainHash(m, &sdc); + setservers = mDNSfalse; // Done these now -- no need to fetch the same data from SCDynamicStore + setsearch = mDNSfalse; } - CFRelease(store); } +#endif // MDNS_NO_DNSINFO + SetupDDNSDomains(fqdn, RegDomains, BrowseDomains); + return mDNStrue; } + mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *r) { char buf[256]; @@ -5996,7 +6789,16 @@ mDNSexport void SetDomainSecrets(mDNS *m) } CFRelease(secrets); } - mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); + + if (!privateDnsArray || !CFEqual(privateDnsArray, sa)) + { + if (privateDnsArray) + CFRelease(privateDnsArray); + + privateDnsArray = sa; + CFRetain(privateDnsArray); + mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray); + } CFRelease(sa); #if APPLE_OSX_mDNSResponder @@ -6044,7 +6846,8 @@ mDNSexport void SetDomainSecrets(mDNS *m) { // stop the NAT operation, reset port, cleanup state mDNS_StopNATOperation_internal(m, &m->AutoTunnelNAT); - m->AutoTunnelNAT.ExternalAddress = m->ExternalAddress; + m->AutoTunnelNAT.ExternalAddress = zerov4Addr; + m->AutoTunnelNAT.NewAddress = zerov4Addr; m->AutoTunnelNAT.ExternalPort = zeroIPPort; m->AutoTunnelNAT.RequestedPort = zeroIPPort; m->AutoTunnelNAT.Lifetime = 0; @@ -6288,55 +7091,11 @@ mDNSlocal mDNSs32 GetSystemSleepTimerSetting(void) return val; } -#if !TARGET_OS_IPHONE -mDNSlocal mDNSBool GetInternetSharingStatus(void) -{ - mDNSBool ret = mDNSfalse; - - // Currently, we do not have a mechanism to advertise sleep proxy services only on the network on which Internet sharing - // is enabled. As a result, we start acting as a very unreliable sleep proxy on other networks when Internet sharing is - // turned on. - // We should look at ways to advertise only on the relevant network. Till this is done, we should turn off this feature. - LogInfo("GetInternetSharingStatus: Disabled"); - return mDNSfalse; - - SCDynamicStoreRef store = SCDynamicStoreCreate(NULL, CFSTR("mDNSResponder:mDNSPlatformInternetSharing"), NULL, NULL); - if (!store) - LogMsg("GetInternetSharingStatus: SCDynamicStoreCreate failed: %s", SCErrorString(SCError())); - else - { - CFDictionaryRef dict = SCDynamicStoreCopyValue(store, NetworkChangedKey_InternetSharing); - if (dict) - { - CFNumberRef state = (CFNumberRef)CFDictionaryGetValue(dict, CFSTR("State")); - if (state) - { - mDNSu32 val; - if (CFNumberGetValue(state, kCFNumberSInt32Type, &val)) - ret = (val == MIS_SVC_STATE_ON) ? mDNStrue : mDNSfalse; - else - LogMsg("GetInternetSharingStatus: CFNumberGetValue error"); - } - CFRelease(dict); - } - CFRelease(store); - } - LogInfo("GetInternetSharingStatus: Internet Sharing state %s", (!ret ? "OFF" : "ON")); - return ret; -} -#else -// We don't want to support this yet in embedded -mDNSlocal mDNSBool GetInternetSharingStatus(void) -{ - LogInfo("GetInternetSharingStatus: Disabled on embedded"); - return mDNSfalse; -} -#endif - mDNSlocal void SetSPS(mDNS *const m) { - mDNSu8 sps = GetInternetSharingStatus() ? mDNSSleepProxyMetric_PrimarySoftware : - (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0; + + // If we ever want to know InternetSharing status in the future, use DNSXEnableProxy() + mDNSu8 sps = (OfferSleepProxyService && GetSystemSleepTimerSetting() == 0) ? mDNSSleepProxyMetric_IncidentalSoftware : 0; // For devices that are not running NAT, but are set to never sleep, we may choose to act // as a Sleep Proxy, but only for non-portable Macs (Portability > 35 means nominal weight < 3kg) @@ -6360,10 +7119,6 @@ mDNSlocal void SetSPS(mDNS *const m) // However, since these definitions can't really be changed without breaking binary compatibility, // they should never change, so in practice it should not be a big problem to have them defined here. -#define mDNS_IOREG_KEY "mDNS_KEY" -#define mDNS_IOREG_VALUE "2009-07-30" -#define mDNS_USER_CLIENT_CREATE_TYPE 'mDNS' - enum { // commands from the daemon to the driver cmd_mDNSOffloadRR = 21, // give the mdns update buffer to the driver @@ -6409,16 +7164,71 @@ mDNSlocal mDNSu16 GetPortArray(mDNS *const m, int trans, mDNSIPPort *portarray) return(count); } +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED +mDNSlocal mDNSBool SupportsTCPKeepAlive() +{ + IOReturn ret = kIOReturnSuccess; + CFTypeRef obj = NULL; + mDNSBool supports = mDNSfalse; + + ret = IOPlatformCopyFeatureActive(CFSTR("TCPKeepAliveDuringSleep"), &obj); + if ((kIOReturnSuccess == ret) && (obj != NULL)) + { + supports = (obj == kCFBooleanTrue)? mDNStrue : mDNSfalse; + CFRelease(obj); + } + LogSPS("%s: The hardware %s TCP Keep Alive", __func__, (supports ? "supports" : "does not support")); + return supports; +} + +mDNSlocal mDNSBool OnBattery(void) +{ + CFTypeRef powerInfo = IOPSCopyPowerSourcesInfo(); + CFTypeRef powerSrc = IOPSGetProvidingPowerSourceType(powerInfo); + mDNSBool result = mDNSfalse; + + if (powerInfo != NULL) + { + result = CFEqual(CFSTR(kIOPSBatteryPowerValue), powerSrc); + CFRelease(powerInfo); + } + LogSPS("%s: The system is on %s", __func__, (result)? "Battery" : "AC Power"); + return result; +} + +#endif // !TARGET_OS_EMBEDDED + #define TfrRecordToNIC(RR) \ ((!(RR)->resrec.InterfaceID && ((RR)->ForceMCast || IsLocalDomain((RR)->resrec.name)))) -mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes) +mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes, NetworkInterfaceInfo *const intf, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA) { *numbytes = 0; int count = 0; + AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { + if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + mDNSBool isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec); + // Skip over all other records if we are registering TCP KeepAlive records only + // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive. + if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA)) + continue; + + // Update the record before calculating the number of bytes required + // We offload the TCP Keepalive record even if the update fails. When the driver gets the record, it will + // attempt to update the record again. + if (isKeepAliveRecord && (UpdateKeepaliveRData(m, rr, intf, mDNSfalse, mDNSNULL) != mStatus_NoError)) + LogSPS("CountProxyRecords: Failed to update keepalive record - %s", ARDisplayString(m, rr)); +#else + (void) TCPKAOnly; // unused + (void) supportsTCPKA; // unused + (void) intf; // unused +#endif // APPLE_OSX_mDNSResponder if (TfrRecordToNIC(rr)) { *numbytes += DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate; @@ -6426,10 +7236,12 @@ mDNSlocal mDNSu32 CountProxyRecords(mDNS *const m, uint32_t *const numbytes) count, DomainNameLength(rr->resrec.name) + 10 + rr->resrec.rdestimate, *numbytes, ARDisplayString(m,rr)); count++; } + } + } return(count); } -mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records) +mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *const numbytes, FatPtr *const records, mDNSBool TCPKAOnly, mDNSBool supportsTCPKA) { mDNSu8 *p = msg->data; const mDNSu8 *const limit = p + *numbytes; @@ -6437,8 +7249,23 @@ mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *c int count = 0; AuthRecord *rr; + for (rr = m->ResourceRecords; rr; rr=rr->next) - if (rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { + if (!(rr->AuthFlags & AuthFlagsWakeOnly) && rr->resrec.RecordType > kDNSRecordTypeDeregistering) + { +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + mDNSBool isKeepAliveRecord = mDNS_KeepaliveRecord(&rr->resrec); + + // Skip over all other records if we are registering TCP KeepAlive records only + // Skip over TCP KeepAlive records if the policy prohibits it or if the interface does not support TCP Keepalive + if ((TCPKAOnly && !isKeepAliveRecord) || (isKeepAliveRecord && !supportsTCPKA)) + continue; +#else + (void) TCPKAOnly; // unused + (void) supportsTCPKA; // unused +#endif // APPLE_OSX_mDNSResponder + if (TfrRecordToNIC(rr)) { records[count].sixtyfourbits = zeroOpaque64; @@ -6451,6 +7278,8 @@ mDNSlocal void GetProxyRecords(mDNS *const m, DNSMessage *const msg, uint32_t *c count, records[count].ptr, p, p - (mDNSu8 *)records[count].ptr, p - msg->data, ARDisplayString(m,rr)); count++; } + } + } *numbytes = p - msg->data; } @@ -6477,35 +7306,61 @@ IOConnectCallStructMethod( } #endif -mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called with the lock held +mDNSexport mDNSBool SupportsInNICProxy(NetworkInterfaceInfo *const intf) { - mStatus result = mStatus_UnknownErr; - io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, ifname)); - if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", ifname); return(mStatus_UnknownErr); } + if(!UseInternalSleepProxy) + { + LogSPS("SupportsInNICProxy: Internal Sleep Proxy is disabled"); + return mDNSfalse; + } + return CheckInterfaceSupport(intf, mDNS_IOREG_KEY); +} + +mDNSexport mStatus ActivateLocalProxy(mDNS *const m, NetworkInterfaceInfo *const intf) // Called with the lock held +{ + mStatus result = mStatus_UnknownErr; + mDNSBool TCPKAOnly = mDNSfalse; + mDNSBool supportsTCPKA = mDNSfalse; + mDNSBool onbattery = mDNSfalse; + io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOBSDNameMatching(kIOMasterPortDefault, 0, intf->ifname)); + +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + onbattery = OnBattery(); + // Check if the interface supports TCP Keepalives and the system policy says it is ok to offload TCP Keepalive records + supportsTCPKA = (InterfaceSupportsKeepAlive(intf) && SupportsTCPKeepAlive()); + + // Only TCP Keepalive records are to be offloaded if + // - The system is on battery + // - OR wake for network access is not set but powernap is enabled + TCPKAOnly = supportsTCPKA && ((m->SystemWakeOnLANEnabled == mDNS_WakeOnBattery) || onbattery); +#else + (void) onbattery; // unused; +#endif + if (!service) { LogMsg("ActivateLocalProxy: No service for interface %s", intf->ifname); return(mStatus_UnknownErr); } io_name_t n1, n2; IOObjectGetClass(service, n1); io_object_t parent; kern_return_t kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); - if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", ifname, n1, kr); + if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IORegistryEntryGetParentEntry for %s/%s failed %d", intf->ifname, n1, kr); else { IOObjectGetClass(parent, n2); - LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", ifname, n1, n2); + LogSPS("ActivateLocalProxy: Interface %s service %s parent %s", intf->ifname, n1, n2); const CFTypeRef ref = IORegistryEntryCreateCFProperty(parent, CFSTR(mDNS_IOREG_KEY), kCFAllocatorDefault, mDNSNULL); - if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", ifname, n1, n2); + if (!ref) LogSPS("ActivateLocalProxy: No mDNS_IOREG_KEY for interface %s/%s/%s", intf->ifname, n1, n2); else { if (CFGetTypeID(ref) != CFStringGetTypeID() || !CFEqual(ref, CFSTR(mDNS_IOREG_VALUE))) LogMsg("ActivateLocalProxy: mDNS_IOREG_KEY for interface %s/%s/%s value %s != %s", - ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE); + intf->ifname, n1, n2, CFStringGetCStringPtr(ref, mDNSNULL), mDNS_IOREG_VALUE); else if (!UseInternalSleepProxy) - LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", ifname); + LogSPS("ActivateLocalProxy: Not using internal (NIC) sleep proxy for interface %s", intf->ifname); else { io_connect_t conObj; kr = IOServiceOpen(parent, mach_task_self(), mDNS_USER_CLIENT_CREATE_TYPE, &conObj); - if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", ifname, n1, n2, kr); + if (kr != KERN_SUCCESS) LogMsg("ActivateLocalProxy: IOServiceOpen for %s/%s/%s failed %d", intf->ifname, n1, n2, kr); else { mDNSOffloadCmd cmd; @@ -6513,7 +7368,7 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called wi cmd.command = cmd_mDNSOffloadRR; cmd.numUDPPorts = GetPortArray(m, mDNSTransport_UDP, mDNSNULL); cmd.numTCPPorts = GetPortArray(m, mDNSTransport_TCP, mDNSNULL); - cmd.numRRRecords = CountProxyRecords(m, &cmd.rrBufferSize); + cmd.numRRRecords = CountProxyRecords(m, &cmd.rrBufferSize, intf, TCPKAOnly, supportsTCPKA); cmd.compression = sizeof(DNSMessageHeader); DNSMessage *msg = (DNSMessage *)mallocL("mDNSOffloadCmd msg", sizeof(DNSMessageHeader) + cmd.rrBufferSize); @@ -6535,13 +7390,13 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called wi cmd.tcpPorts.ptr, cmd.numTCPPorts); else { - GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr); + GetProxyRecords(m, msg, &cmd.rrBufferSize, cmd.rrRecords.ptr, TCPKAOnly, supportsTCPKA); GetPortArray(m, mDNSTransport_UDP, cmd.udpPorts.ptr); GetPortArray(m, mDNSTransport_TCP, cmd.tcpPorts.ptr); char outputData[2]; size_t outputDataSize = sizeof(outputData); kr = IOConnectCallStructMethod(conObj, 0, &cmd, sizeof(cmd), outputData, &outputDataSize); - LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", ifname, n1, n2, kr); + LogSPS("ActivateLocalProxy: IOConnectCallStructMethod for %s/%s/%s %d", intf->ifname, n1, n2, kr); if (kr == KERN_SUCCESS) result = mStatus_NoError; } @@ -6562,37 +7417,30 @@ mDNSexport mStatus ActivateLocalProxy(mDNS *const m, char *ifname) // Called wi #endif // APPLE_OSX_mDNSResponder -static io_service_t g_rootdomain = MACH_PORT_NULL; - -mDNSlocal mDNSBool SystemWakeForNetworkAccess(void) +mDNSlocal mDNSu8 SystemWakeForNetworkAccess(void) { mDNSs32 val = 0; - CFBooleanRef clamshellStop = NULL; - mDNSBool retnow = mDNSfalse; + mDNSu8 ret = (mDNSu8)mDNS_NoWake; - if (DisableSleepProxyClient) { LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); return mDNSfalse; } + if (DisableSleepProxyClient) + { + LogSPS("SystemWakeForNetworkAccess: Sleep Proxy Client disabled by command-line option"); + return mDNSfalse; + } GetCurrentPMSetting(CFSTR("Wake On LAN"), &val); - LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", val); - if (!val) return mDNSfalse; - - if (!g_rootdomain) g_rootdomain = IORegistryEntryFromPath(MACH_PORT_NULL, kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain"); - if (!g_rootdomain) { LogMsg("SystemWakeForNetworkAccess: IORegistryEntryFromPath failed; assuming no clamshell so can WOMP"); return mDNStrue; } - clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellStateKey), kCFAllocatorDefault, 0); - if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } - retnow = clamshellStop == kCFBooleanFalse; - CFRelease(clamshellStop); - if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellStateKey is false; clamshell is open so can WOMP"); return mDNStrue; } + ret = (mDNSu8)(val != 0) ? mDNS_WakeOnAC : mDNS_NoWake; - clamshellStop = (CFBooleanRef)IORegistryEntryCreateCFProperty(g_rootdomain, CFSTR(kAppleClamshellCausesSleepKey), kCFAllocatorDefault, 0); - if (!clamshellStop) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey does not exist; assuming no clamshell so can WOMP"); return mDNStrue; } - retnow = (clamshellStop == kCFBooleanFalse); - CFRelease(clamshellStop); - if (retnow) { LogSPS("SystemWakeForNetworkAccess: kAppleClamshellCausesSleepKey is false; clamshell is closed but can WOMP"); return mDNStrue; } +#if APPLE_OSX_mDNSResponder && !TARGET_OS_EMBEDDED + // If we have TCP Keepalive support, system is capable of registering for TCP Keepalives. + // Further policy decisions on whether to offload the records is handled during sleep processing. + if ((ret == mDNS_NoWake) && SupportsTCPKeepAlive()) + ret = (mDNSu8)mDNS_WakeOnBattery; +#endif // APPLE_OSX_mDNSResponder - LogSPS("SystemWakeForNetworkAccess: clamshell is closed and can't WOMP"); - return mDNSfalse; + LogSPS("SystemWakeForNetworkAccess: Wake On LAN: %d", ret); + return ret; } mDNSlocal mDNSBool SystemSleepOnlyIfWakeOnLAN(void) @@ -6904,6 +7752,20 @@ mDNSlocal void ProcessConndConfigChanges(mDNS *const m) } #endif /* APPLE_OSX_mDNSResponder */ +mDNSlocal mDNSBool IsAppleNetwork(mDNS *const m) +{ + DNSServer *s; + // Determine if we're on AppleNW based on DNSServer having 17.x.y.z IPv4 addr + for (s = m->DNSServers; s; s = s->next) + { + if (s->addr.ip.v4.b[0] == 17) + { + LogInfo("IsAppleNetwork: Found 17.x.y.z DNSServer concluding that we are on AppleNW: %##s %#a", s->domain.c, &s->addr); + return mDNStrue; + } + } + return mDNSfalse; +} mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) { @@ -6920,6 +7782,7 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) SetupActiveInterfaces(m, utc); #if APPLE_OSX_mDNSResponder + mDNS_Lock(m); ProcessConndConfigChanges(m); mDNS_Unlock(m); @@ -6989,6 +7852,14 @@ mDNSexport void mDNSMacOSXNetworkChanged(mDNS *const m) uDNS_SetupDNSConfig(m); mDNS_ConfigChanged(m); + + if (IsAppleNetwork(m) != mDNS_McastTracingEnabled) + { + mDNS_McastTracingEnabled = mDNS_McastTracingEnabled ? mDNSfalse : mDNStrue; + LogMsg("mDNSMacOSXNetworkChanged: Multicast Tracing %s", mDNS_McastTracingEnabled ? "Enabled" : "Disabled"); + UpdateDebugState(); + } + } // Called with KQueueLock & mDNS lock @@ -7162,26 +8033,55 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v int c2 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_Computername) != 0); int c3 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DynamicDNS ) != 0); int c4 = (CFArrayContainsValue(changedKeys, range, NetworkChangedKey_DNS ) != 0); - if (c && c - c1 - c2 - c3 - c4 == 0) delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay + if (c && c - c1 - c2 - c3 - c4 == 0) + delay = mDNSPlatformOneSecond/10; // If these were the only changes, shorten delay + // Do immediate network changed processing for "p2p*" interfaces and + // for interfaces with the IFEF_DIRECTLINK flag set. { - int i; - for (i=0; i/IPv6 + // Thus five '/' seperated fields, the 4th one being the string. + if (n == 5) { - if (strstr(buf, "p2p")) + char buf[256]; + + // The 4th label (index = 3) should be the interface name. + if (CFStringGetCString(CFArrayGetValueAtIndex(labels, 3), buf, sizeof(buf), kCFStringEncodingUTF8) + && (strstr(buf, "p2p") || (getExtendedFlags(buf) & IFEF_DIRECTLINK))) { - LogInfo("NetworkChanged SC key: %s, not delaying network change", buf); + LogInfo("NetworkChanged: interface %s, not delaying network change", buf); changeNow = mDNStrue; + CFRelease(labels); break; } } + CFRelease(labels); } } + mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac); + if (btmmChanged) delay = 0; + if (mDNS_LoggingEnabled) { int i; @@ -7197,13 +8097,11 @@ mDNSlocal void NetworkChanged(SCDynamicStoreRef store, CFArrayRef changedKeys, v c2 ? "(Computer Name) " : "", c3 ? "(DynamicDNS) " : "", c4 ? "(DNS) " : "", - delay); + changeNow ? 0 : delay); } - mDNSBool btmmChanged = CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac); - if (btmmChanged) delay = 0; - - SetNetworkChanged(m, delay); + if (!changeNow) + SetNetworkChanged(m, delay); // Other software might pick up these changes to register or browse in WAB or BTMM domains, // so in order for secure updates to be made to the server, make sure to read the keychain and @@ -7230,7 +8128,8 @@ mDNSlocal void RefreshSPSStatus(const void *key, const void *value, void *contex CFStringRef ifnameStr = (CFStringRef)key; CFArrayRef array = (CFArrayRef)value; - if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) buf[0] = 0; + if (!CFStringGetCString(ifnameStr, buf, sizeof(buf), kCFStringEncodingUTF8)) + buf[0] = 0; LogInfo("RefreshSPSStatus: Updating SPS state for key %s, array count %d", buf, CFArrayGetCount(array)); mDNSDynamicStoreSetConfig(kmDNSSleepProxyServersState, buf, value); @@ -7256,31 +8155,18 @@ mDNSlocal void DynamicStoreReconnected(SCDynamicStoreRef store, void *info) // so that if things are done differently in the future, this code still works. // State:/Network/PrivateDNS - CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (!sa) - LogMsg("DynamicStoreReconnected:PrivateDNS: CFArrayCreateMutable failed"); - else - { - DomainAuthInfo *FoundInList; - char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal domainname as C-string, including terminating NUL - for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next) - { - ConvertDomainNameToCString(&FoundInList->domain, stringbuf); - CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8); - if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); } - } - mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, sa); - CFRelease(sa); - } + if (privateDnsArray) + mDNSDynamicStoreSetConfig(kmDNSPrivateConfig, mDNSNULL, privateDnsArray); - // State:/Network/BackToMyMac #if APPLE_OSX_mDNSResponder mDNS_Lock(m); + // State:/Network/BackToMyMac UpdateAutoTunnelDomainStatuses(m); mDNS_Unlock(m); // State:/Network/Interface/en0/SleepProxyServers - if (spsStatusDict) CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL); + if (spsStatusDict) + CFDictionaryApplyFunction(spsStatusDict, RefreshSPSStatus, NULL); #endif } @@ -7306,7 +8192,6 @@ mDNSlocal mStatus WatchForNetworkChanges(mDNS *const m) CFArrayAppendValue(keys, NetworkChangedKey_BackToMyMac); CFArrayAppendValue(keys, NetworkChangedKey_PowerSettings); // should remove as part of CFArrayAppendValue(keys, NetworkChangedKey_BTMMConnectivity); - CFArrayAppendValue(keys, NetworkChangedKey_InternetSharing); CFArrayAppendValue(patterns, pattern1); CFArrayAppendValue(patterns, pattern2); CFArrayAppendValue(patterns, CFSTR("State:/Network/Interface/[^/]+/AirPort")); @@ -7376,7 +8261,8 @@ mDNSlocal void mDNSSetPacketFilterRules(mDNS *const m, char * ifname, const Reso for (rr = m->ResourceRecords; rr; rr=rr->next) { - if ((rr->resrec.rrtype == kDNSServiceType_SRV) && (rr->ARType == AuthRecordAnyIncludeP2P)) + if ((rr->resrec.rrtype == kDNSServiceType_SRV) + && ((rr->ARType == AuthRecordAnyIncludeP2P) || (rr->ARType == AuthRecordAnyIncludeAWDLandP2P))) { const mDNSu8 *p; @@ -7491,7 +8377,8 @@ mDNSlocal void newMasterElected(mDNS *const m, struct net_event_data * ptr) for (rr = m->ResourceRecords; rr; rr=rr->next) { - if ((!rr->resrec.InterfaceID && (rr->ARType == AuthRecordAnyIncludeAWDL)) + if ((!rr->resrec.InterfaceID + && ((rr->ARType == AuthRecordAnyIncludeAWDL) || ((rr->ARType == AuthRecordAnyIncludeAWDLandP2P)))) || rr->resrec.InterfaceID == InterfaceID) { LogInfo("newMasterElected: restarting %s announcements for %##s", DNSTypeName(rr->resrec.rrtype), rr->namestorage.c); @@ -7506,6 +8393,16 @@ mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op) int i; int *intp = (int *) op->ssth; + // MAX_SSTH_SIZE should always be a multiple of sizeof(int), if + // it's not, print an error message and return false so that + // corresponding peer records are not flushed when KEV_DL_NODE_PRESENCE event + // is received. + if (MAX_SSTH_SIZE % sizeof(int)) + { + LogInfo("allZeroSSTH: MAX_SSTH_SIZE = %d not a multiple of sizeof(int)", MAX_SSTH_SIZE); + return mDNSfalse; + } + for (i = 0; i < (int)(MAX_SSTH_SIZE / sizeof(int)); i++, intp++) { if (*intp) @@ -7514,6 +8411,10 @@ mDNSlocal mDNSBool allZeroSSTH(struct opaque_presence_indication *op) return mDNStrue; } +// mDNS_Reconfirm_internal() adds 33% to this interval, so the records should +// be removed in 4 seconds. +#define kAWDLReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 3) + // Mark records from this peer for deletion from the cache. mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr *ap) { @@ -7534,7 +8435,7 @@ mDNSlocal void removeCachedPeerRecords(mDNS *const m, mDNSu32 ifindex, mDNSAddr { LogInfo("removeCachedPeerRecords: %s %##s marking for deletion", DNSTypeName(cr->resrec.rrtype), cr->resrec.name->c); - mDNS_PurgeCacheResourceRecord(m, cr); + mDNS_Reconfirm_internal(m, cr, kAWDLReconfirmTime); } } } @@ -7552,12 +8453,7 @@ mDNSlocal void nodePresence(mDNS *const m, struct kev_dl_node_presence * p) // 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. - // We should have already flushed the cache when the previous - // KEV_DL_NODE_ABSENCE was received or upon receipt of the goodbye packets - // when the service was deregistered. Thus, the following - // cache flush logic is disabled by mDNSHandlePeerEvents being false - // until we determine that a cache flush is required on receipt of this event. - if (m->mDNSHandlePeerEvents && allZeroSSTH(op)) + if (allZeroSSTH(op)) { mDNSAddr peerAddr; @@ -7638,7 +8534,7 @@ mDNSlocal void SysEventCallBack(int s1, short __unused filter, void *context) if (msg.k.event_code == KEV_DL_MASTER_ELECTED) newMasterElected(m, (struct net_event_data *) &msg.k.event_data); - + // We receive network change notifications both through configd and through SYSPROTO_EVENT socket. // Configd may not generate network change events for manually configured interfaces (i.e., non-DHCP) // always during sleep/wakeup due to some race conditions (See radar:8666757). At the same time, if @@ -7784,10 +8680,18 @@ mDNSlocal void PowerOn(mDNS *const m) { long utc = mDNSPlatformUTC(); mDNSPowerRequest(-1,-1); // Need to explicitly clear any previous power requests -- they're not cleared automatically on wake - if (m->p->WakeAtUTC - utc > 30) LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc); - else if (utc - m->p->WakeAtUTC > 30) LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC); - else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is K66AP so not re-sleeping", utc - m->p->WakeAtUTC); - else if (!strncasecmp(HINFO_HWstring, "J33AP", 5)) LogSPS("PowerChanged PowerOn %d seconds late, device is J33AP so not re-sleeping", utc - m->p->WakeAtUTC); + if (m->p->WakeAtUTC - utc > 30) + { + LogSPS("PowerChanged PowerOn %d seconds early, assuming not maintenance wake", m->p->WakeAtUTC - utc); + } + else if (utc - m->p->WakeAtUTC > 30) + { + LogSPS("PowerChanged PowerOn %d seconds late, assuming not maintenance wake", utc - m->p->WakeAtUTC); + } + else if (IsAppleTV()) + { + LogSPS("PowerChanged PowerOn %d seconds late, device is an AppleTV running iOS so not re-sleeping", utc - m->p->WakeAtUTC); + } else { LogSPS("PowerChanged: Waking for network maintenance operations %d seconds early; re-sleeping in 20 seconds", m->p->WakeAtUTC - utc); @@ -7839,18 +8743,6 @@ mDNSlocal void PowerChanged(void *refcon, io_service_t service, natural_t messag case kIOMessageSystemWillRestart: LogSPS("PowerChanged kIOMessageSystemWillRestart (no action)"); break; // E0000310 case kIOMessageSystemWillPowerOn: LogSPS("PowerChanged kIOMessageSystemWillPowerOn"); // E0000320 - // On Leopard and earlier, on wake from sleep, instead of reporting link state down, Apple - // Ethernet drivers report "hardware incapable of detecting link state", which the kernel - // interprets as "should assume we have networking", which results in the first 4-5 seconds - // of packets vanishing into a black hole. To work around this, on wake from sleep, - // we block for five seconds to let Ethernet come up, and then resume normal operation. - if (OSXVers && OSXVers < OSXVers_10_6_SnowLeopard) - { - sleep(5); - LogMsg("Running on Mac OS X version 10.%d earlier than 10.6; " - "PowerChanged did sleep(5) to wait for Ethernet hardware", OSXVers - OSXVers_Base); - } - // Make sure our interface list is cleared to the empty state, then tell mDNSCore to wake if (m->SleepState != SleepState_Sleeping) { @@ -8600,7 +9492,7 @@ mDNSlocal void UpdateEtcHosts(mDNS *const m, void *context) { AuthHash *newhosts = (AuthHash *)context; - if (!m->mDNS_busy) LogMsg("UpdateEtcHosts: ERROR!! Lock not held"); + mDNS_CheckLock(m); //Delete old entries from the core if they are not present in the newhosts EtcHostsDeleteOldEntries(m, newhosts, mDNSfalse); @@ -8710,11 +9602,7 @@ CF_EXPORT const CFStringRef _kCFSystemVersionProductNameKey; CF_EXPORT const CFStringRef _kCFSystemVersionProductVersionKey; CF_EXPORT const CFStringRef _kCFSystemVersionBuildVersionKey; -// Major version 6 is 10.2.x (Jaguar) -// Major version 7 is 10.3.x (Panther) -// Major version 8 is 10.4.x (Tiger) -// Major version 9 is 10.5.x (Leopard) -// Major version 10 is 10.6.x (SnowLeopard) +// Major version 13 is 10.9.x mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) { int major = 0, minor = 0; @@ -8725,18 +9613,28 @@ mDNSexport void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring) CFStringRef cfprodname = CFDictionaryGetValue(vers, _kCFSystemVersionProductNameKey); CFStringRef cfprodvers = CFDictionaryGetValue(vers, _kCFSystemVersionProductVersionKey); CFStringRef cfbuildver = CFDictionaryGetValue(vers, _kCFSystemVersionBuildVersionKey); - if (cfprodname) CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); - if (cfprodvers) CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); + if (cfprodname) + CFStringGetCString(cfprodname, prodname, sizeof(prodname), kCFStringEncodingUTF8); + if (cfprodvers) + CFStringGetCString(cfprodvers, prodvers, sizeof(prodvers), kCFStringEncodingUTF8); if (cfbuildver && CFStringGetCString(cfbuildver, buildver, sizeof(buildver), kCFStringEncodingUTF8)) sscanf(buildver, "%d%c%d", &major, &letter, &minor); CFRelease(vers); } - if (!major) { major=8; LogMsg("Note: No Major Build Version number found; assuming 8"); } - if (HINFO_SWstring) mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion)); + if (!major) + { + major = 13; + LogMsg("Note: No Major Build Version number found; assuming 13"); + } + if (HINFO_SWstring) + mDNS_snprintf(HINFO_SWstring, 256, "%s %s (%s), %s", prodname, prodvers, buildver, STRINGIFY(mDNSResponderVersion)); //LogMsg("%s %s (%s), %d %c %d", prodname, prodvers, buildver, major, letter, minor); // If product name is "Mac OS X" (or similar) we set OSXVers, else we set iOSVers; - if ((prodname[0] & 0xDF) == 'M') OSXVers = major;else iOSVers = major; + if ((prodname[0] & 0xDF) == 'M') + OSXVers = major; + else + iOSVers = major; } // Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT. @@ -8843,6 +9741,34 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) char HINFO_SWstring[256] = ""; mDNSMacOSXSystemBuildNumber(HINFO_SWstring); + err = mDNSHelperInit(); + if (err) + return err; + + DynamicStoreQueue = dispatch_queue_create("com.apple.mDNSResponder.DynamicStoreQueue", NULL); + if (DynamicStoreQueue == NULL) + { + LogMsg("dispatch_queue_create: DynamicStoreQueue NULL!"); + return mStatus_NoMemoryErr; + } + + // Store mDNSResponder Platform + if (OSXVers) + { + m->mDNS_plat = platform_OSX; + } + else if (iOSVers) + { + if (IsAppleTV()) + m->mDNS_plat = platform_Atv; + else + m->mDNS_plat = platform_iOS; + } + else + { + m->mDNS_plat = platform_NonApple; + } + // In 10.4, mDNSResponder is launched very early in the boot process, while other subsystems are still in the process of starting up. // If we can't read the user's preferences, then we sleep a bit and try again, for up to five seconds before we give up. int i; @@ -8872,9 +9798,8 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) // For names of the form "N88AP" containg no comma, we use the entire string. HINFO_HWstring_prefixlen = strchr(HINFO_HWstring_buffer, ',') ? strcspn(HINFO_HWstring, "0123456789") : strlen(HINFO_HWstring); - if (OSXVers && OSXVers <= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LimitedIPv6; - if (OSXVers && OSXVers >= OSXVers_10_6_SnowLeopard) m->KnownBugs |= mDNS_KnownBug_LossySyslog; - if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue; + if (mDNSPlatformInit_CanReceiveUnicast()) + m->CanReceiveUnicastOn5353 = mDNStrue; mDNSu32 hlen = mDNSPlatformStrLen(HINFO_HWstring); mDNSu32 slen = mDNSPlatformStrLen(HINFO_SWstring); @@ -8892,23 +9817,21 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) m->p->permanentsockets.kqsv4.KQcallback = myKQSocketCallBack; m->p->permanentsockets.kqsv4.KQcontext = &m->p->permanentsockets; m->p->permanentsockets.kqsv4.KQtask = "UDP packet reception"; -#ifndef NO_IPV6 m->p->permanentsockets.sktv6 = -1; m->p->permanentsockets.kqsv6.KQcallback = myKQSocketCallBack; m->p->permanentsockets.kqsv6.KQcontext = &m->p->permanentsockets; m->p->permanentsockets.kqsv6.KQtask = "UDP packet reception"; -#endif err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET, mDNSNULL); -#ifndef NO_IPV6 err = SetupSocket(&m->p->permanentsockets, MulticastDNSPort, AF_INET6, mDNSNULL); -#endif struct sockaddr_in s4; socklen_t n4 = sizeof(s4); - if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); - else m->UnicastPort4.NotAnInteger = s4.sin_port; -#ifndef NO_IPV6 + if (getsockname(m->p->permanentsockets.sktv4, (struct sockaddr *)&s4, &n4) < 0) + LogMsg("getsockname v4 error %d (%s)", errno, strerror(errno)); + else + m->UnicastPort4.NotAnInteger = s4.sin_port; + if (m->p->permanentsockets.sktv6 >= 0) { struct sockaddr_in6 s6; @@ -8916,7 +9839,6 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) if (getsockname(m->p->permanentsockets.sktv6, (struct sockaddr *)&s6, &n6) < 0) LogMsg("getsockname v6 error %d (%s)", errno, strerror(errno)); else m->UnicastPort6.NotAnInteger = s6.sin6_port; } -#endif m->p->InterfaceList = mDNSNULL; m->p->userhostlabel.c[0] = 0; @@ -8929,6 +9851,13 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) m->p->KeyChainTimer = 0; m->p->WakeAtUTC = 0; m->p->RequestReSleep = 0; + // Assume that everything is good to begin with. If something is not working, + // we will detect that when we start sending questions. + m->p->v4answers = 1; + m->p->v6answers = 1; + m->p->DNSTrigger = 0; + m->p->LastConfigGeneration = 0; + #if APPLE_OSX_mDNSResponder uuid_generate(m->asl_uuid); #endif @@ -8940,7 +9869,8 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) NetworkChangedKey_Hostnames = SCDynamicStoreKeyCreateHostNames(NULL); NetworkChangedKey_Computername = SCDynamicStoreKeyCreateComputerName(NULL); NetworkChangedKey_DNS = SCDynamicStoreKeyCreateNetworkGlobalEntity(NULL, kSCDynamicStoreDomainState, kSCEntNetDNS); - if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS) + NetworkChangedKey_StateInterfacePrefix = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, kSCDynamicStoreDomainState, CFSTR(""), NULL); + if (!NetworkChangedKey_IPv4 || !NetworkChangedKey_IPv6 || !NetworkChangedKey_Hostnames || !NetworkChangedKey_Computername || !NetworkChangedKey_DNS || !NetworkChangedKey_StateInterfacePrefix) { LogMsg("SCDynamicStore string setup failed"); return(mStatus_NoMemoryErr); } err = WatchForNetworkChanges(m); @@ -9025,16 +9955,13 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) else if (!strncasecmp(HINFO_HWstring, "Macmini", 7)) { SPMetricPortability = 33 /* 5kg */; SPMetricMarginalPower = 73 /* 20W */; SPMetricTotalPower = 74 /* 25W */; } else if (!strncasecmp(HINFO_HWstring, "TimeCapsule", 11)) { SPMetricPortability = 34 /* 4kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 13W */; } else if (!strncasecmp(HINFO_HWstring, "AirPort", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 70 /* 12W */; } - else if (!strncasecmp(HINFO_HWstring, "AppleTV", 7)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 10 /* ~0W */; SPMetricTotalPower = 73 /* 20W */; } - else if (!strncasecmp(HINFO_HWstring, "K66AP", 5)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } - else if (!strncasecmp(HINFO_HWstring, "J33AP", 5)) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } + else if ( IsAppleTV() ) { SPMetricPortability = 35 /* 3kg */; SPMetricMarginalPower = 60 /* 1W */; SPMetricTotalPower = 63 /* 2W */; } else if (!strncasecmp(HINFO_HWstring, "MacBook", 7)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } else if (!strncasecmp(HINFO_HWstring, "PowerBook", 9)) { SPMetricPortability = 37 /* 2kg */; SPMetricMarginalPower = 71 /* 13W */; SPMetricTotalPower = 72 /* 15W */; } LogSPS("HW_MODEL: %.*s (%s) Portability %d Marginal Power %d Total Power %d Features %d", HINFO_HWstring_prefixlen, HINFO_HWstring, HINFO_HWstring, SPMetricPortability, SPMetricMarginalPower, SPMetricTotalPower, SPMetricFeatures); #endif // APPLE_OSX_mDNSResponder -#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM // Currently this is not defined. SSL code will eventually fix this. If it becomes // critical, we will define this to workaround the bug in SSL. #ifdef __SSL_NEEDS_SERIALIZATION__ @@ -9043,10 +9970,11 @@ mDNSlocal mStatus mDNSPlatformInit_setup(mDNS *const m) SSLqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); #endif if (SSLqueue == mDNSNULL) LogMsg("dispatch_queue_create: SSL queue NULL"); -#endif + mDNSMacOSXUpdateEtcHosts(m); SetupLocalHostRecords(m); - m->TrustAnchors = mDNSNULL; + CUPInit(m); + return(mStatus_NoError); } @@ -9262,9 +10190,15 @@ mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, co IOPMAssertionRelease(m->p->IOPMAssertion); m->p->IOPMAssertion = 0; } - else if (!allowSleep && m->p->IOPMAssertion == 0) + else if (!allowSleep) { #ifdef kIOPMAssertionTypeNoIdleSleep + if (m->p->IOPMAssertion) + { + IOPMAssertionRelease(m->p->IOPMAssertion); + m->p->IOPMAssertion = 0; + } + CFStringRef assertionName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s.%d %s"), getprogname(), getpid(), reason ? reason : ""); IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, assertionName ? assertionName : CFSTR("mDNSResponder"), &m->p->IOPMAssertion); if (assertionName) CFRelease(assertionName); @@ -9326,14 +10260,14 @@ mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const Ne // If it's an AWDL interface the record must be explicitly marked to include AWDL. if (intf->InterfaceID == AWDLInterfaceID) { - if (rr->ARType == AuthRecordAnyIncludeAWDL) + if (rr->ARType == AuthRecordAnyIncludeAWDL || rr->ARType == AuthRecordAnyIncludeAWDLandP2P) return mDNStrue; else return mDNSfalse; } - // Sent record if it is explicitly marked to include all other P2P type interfaces. - if (rr->ARType == AuthRecordAnyIncludeP2P) + // Send record if it is explicitly marked to include all other P2P type interfaces. + if (rr->ARType == AuthRecordAnyIncludeP2P || rr->ARType == AuthRecordAnyIncludeAWDLandP2P) return mDNStrue; // Don't send the record over this interface. @@ -9397,79 +10331,73 @@ mDNSexport void mDNSPlatformFormatTime(unsigned long te, mDNSu8 *buf, int bufsiz strftime((char *)buf, bufsize, "%Y%m%d%H%M%S", &tmTime); } -mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) +mDNSexport mDNSs32 mDNSPlatformGetPID() { - struct tcp_info ti; - struct info_tuple itpl; - int mib[4]; - unsigned int miblen; - size_t len; - size_t sz; - - itpl.itpl_proto = IPPROTO_TCP; - - if (laddr->type == mDNSAddrType_IPv4) - { - mDNSPlatformMemCopy(&itpl.itpl_local_sin.sin_addr, &laddr->ip.v4, sizeof(struct in_addr)); - mDNSPlatformMemCopy(&itpl.itpl_remote_sin.sin_addr, &raddr->ip.v4, sizeof(struct in_addr)); - itpl.itpl_local_sin.sin_port = lport->NotAnInteger; - itpl.itpl_remote_sin.sin_port = rport->NotAnInteger; - itpl.itpl_remote_sin.sin_family = AF_INET; - } - else - { - mDNSPlatformMemCopy(&itpl.itpl_local_sin6.sin6_addr, &laddr->ip.v6, sizeof(struct in6_addr)); - mDNSPlatformMemCopy(&itpl.itpl_remote_sin6.sin6_addr, &raddr->ip.v6, sizeof(struct in6_addr)); - itpl.itpl_local_sin6.sin6_port = lport->NotAnInteger; - itpl.itpl_remote_sin6.sin6_port = rport->NotAnInteger; - itpl.itpl_remote_sin6.sin6_family = AF_INET6; - } - + return getpid(); +} - sz = sizeof(mib)/sizeof(mib[0]); - if (sysctlnametomib("net.inet.tcp.info", mib, &sz) == -1) - { - LogMsg("mDNSPlatformRetrieveTCPInfo: sysctlnametomib failed %d, %s", errno, strerror(errno)); - return mStatus_UnknownErr; - } +// Schedule a function asynchronously on the main queue +mDNSexport void mDNSPlatformDispatchAsync(mDNS *const m, void *context, AsyncDispatchFunc func) +{ + // KQueueLock/Unlock is used for two purposes + // + // 1. We can't be running along with the KQueue thread and hence acquiring the lock + // serializes the access to the "core" + // + // 2. KQueueUnlock also sends a message wake up the KQueue thread which in turn wakes + // up and calls udsserver_idle which schedules the messages across the uds socket. + // If "func" delivers something to the uds socket from the dispatch thread, it will + // not be delivered immediately if not for the Unlock. + dispatch_async(dispatch_get_main_queue(), ^{ + KQueueLock(m); + func(m, context); + KQueueUnlock(m, "mDNSPlatformDispatchAsync"); +#ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM + // KQueueUnlock is a noop. Hence, we need to run kick off the idle loop + // to handle any message that "func" might deliver. + TriggerEventCompletion(); +#endif + }); +} - miblen = (unsigned int)sz; +// definitions for device-info record construction +#define DEVINFO_MODEL "model=" +#define DEVINFO_MODEL_LEN strlen(DEVINFO_MODEL) - len = sizeof(struct tcp_info); - if (sysctl(mib, miblen, &ti, &len, &itpl, sizeof(struct info_tuple)) == -1) - { - LogMsg("mDNSPlatformRetrieveTCPInfo: sysctl failed %d, %s", errno, strerror(errno)); - return mStatus_UnknownErr; - } - mti->seq = ti.tcpi_snd_nxt - 1; - mti->ack = ti.tcpi_rcv_nxt; - mti->window = ti.tcpi_rcv_space >> ti.tcpi_rcv_wscale; - mti->IntfId = mDNSPlatformInterfaceIDfromInterfaceIndex(m, ti.tcpi_last_outif); - if (mti->IntfId == mDNSInterface_LocalOnly || mti->IntfId == mDNSInterface_P2P || mti->IntfId == mDNSInterface_Any) - { - LogMsg("mDNSPlatformRetrieveTCPInfo: Bad InterfaceId %p", mti->IntfId); - return mStatus_BadParamErr; - } +#define OSX_VER "osxvers=" +#define OSX_VER_LEN strlen(OSX_VER) +#define VER_NUM_LEN 2 // 2 digits of version number added to base string - return mStatus_NoError; -} +// Bytes available in TXT record for model name after subtracting space for other +// fixed size strings and their length bytes. +#define MAX_MODEL_NAME_LEN (256 - (DEVINFO_MODEL_LEN + 1) - (OSX_VER_LEN + VER_NUM_LEN + 1)) -#if APPLE_OSX_mDNSResponder -mDNSexport void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt) +// Initialize device-info TXT record contents and return total length of record data. +mDNSexport mDNSu32 initializeDeviceInfoTXT(mDNS *m, mDNSu8 *ptr) { - NetworkInterfaceInfoOSX *intf; + mDNSu8 *bufferStart = ptr; + mDNSu8 len = m->HIHardware.c[0] < MAX_MODEL_NAME_LEN ? m->HIHardware.c[0] : MAX_MODEL_NAME_LEN; - LogInfo("%s called to %s advertisements", __func__, stopAdvt? "stop" : "start"); - for (intf = m->p->InterfaceList; intf; intf = intf->next) + *ptr = DEVINFO_MODEL_LEN + len; // total length of DEVINFO_MODEL string plus the hardware name string + ptr++; + mDNSPlatformMemCopy(ptr, DEVINFO_MODEL, DEVINFO_MODEL_LEN); + ptr += DEVINFO_MODEL_LEN; + mDNSPlatformMemCopy(ptr, m->HIHardware.c + 1, len); + ptr += len; + + // only include this string for OSX + if (OSXVers) { - // For now, the ioctl only supports suppression of IPv6 advertisements - if (intf->ifinfo.InterfaceActive && intf->ifinfo.McastTxRx) - { - if (mDNSInterfaceAdvtIoctl(intf->ifinfo.ifname, (stopAdvt? 0 : 1)) < 0) - { - LogMsg("%s: mDNSInterfaceAdvtIoctl failed"); - } - } + char ver_num[VER_NUM_LEN + 1]; // version digits + null written by snprintf + *ptr = OSX_VER_LEN + VER_NUM_LEN; // length byte + ptr++; + mDNSPlatformMemCopy(ptr, OSX_VER, OSX_VER_LEN); + ptr += OSX_VER_LEN; + // convert version number to ASCII, add 1 for terminating null byte written by snprintf() + snprintf(ver_num, VER_NUM_LEN + 1, "%d", OSXVers); + mDNSPlatformMemCopy(ptr, ver_num, VER_NUM_LEN); + ptr += VER_NUM_LEN; } + + return (ptr - bufferStart); } -#endif diff --git a/mDNSMacOSX/mDNSMacOSX.h b/mDNSMacOSX/mDNSMacOSX.h index 7c30207..00bfb87 100644 --- a/mDNSMacOSX/mDNSMacOSX.h +++ b/mDNSMacOSX/mDNSMacOSX.h @@ -15,8 +15,8 @@ * limitations under the License. */ -#ifndef __mDNSOSX_h -#define __mDNSOSX_h +#ifndef __mDNSMacOSX_h +#define __mDNSMacOSX_h #ifdef __cplusplus extern "C" { @@ -30,11 +30,43 @@ extern "C" { #include #include "mDNSEmbeddedAPI.h" // for domain name structure +#include + //#define MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM #include +#include +#endif + +#if TARGET_OS_EMBEDDED +#define NO_SECURITYFRAMEWORK 1 +#define NO_CFUSERNOTIFICATION 1 +#include // for IsAppleTV() +#include // for _scprefs_observer_watch() +extern mDNSBool GetmDNSManagedPref(CFStringRef key); +#endif + +#ifndef NO_SECURITYFRAMEWORK +#include +#include +#endif /* NO_SECURITYFRAMEWORK */ + +#if TARGET_OS_IPHONE +#include "cellular_usage_policy.h" #endif +#define kmDNSResponderServName "com.apple.mDNSResponder" + +enum mDNSDynamicStoreSetConfigKey +{ + kmDNSMulticastConfig = 1, + kmDNSDynamicConfig, + kmDNSPrivateConfig, + kmDNSBackToMyMacConfig, + kmDNSSleepProxyServersState, + kmDNSDebugState, +}; + typedef struct NetworkInterfaceInfoOSX_struct NetworkInterfaceInfoOSX; typedef void (*KQueueEventCallback)(int fd, short filter, void *context); @@ -42,7 +74,7 @@ typedef struct { KQueueEventCallback KQcallback; void *KQcontext; - const char const *KQtask; // For debugging messages + const char *KQtask; // For debugging messages #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM dispatch_source_t readSource; dispatch_source_t writeSource; @@ -57,11 +89,10 @@ typedef struct mDNS *m; int sktv4; KQueueEntry kqsv4; -#ifndef NO_IPV6 int sktv6; KQueueEntry kqsv6; -#endif int *closeFlag; + mDNSBool proxy; } KQSocketSet; struct UDPSocket_struct @@ -69,6 +100,36 @@ struct UDPSocket_struct KQSocketSet ss; // First field of KQSocketSet has to be mDNSIPPort -- mDNSCore requires every UDPSocket_struct to begin with mDNSIPPort port }; +// TCP socket support + +typedef enum +{ + handshake_required, + handshake_in_progress, + handshake_completed, + handshake_to_be_closed +} handshakeStatus; + +struct TCPSocket_struct +{ + TCPSocketFlags flags; // MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags + TCPConnectionCallback callback; + int fd; + KQueueEntry *kqEntry; + KQSocketSet ss; +#ifndef NO_SECURITYFRAMEWORK + SSLContextRef tlsContext; + pthread_t handshake_thread; +#endif /* NO_SECURITYFRAMEWORK */ + domainname hostname; + void *context; + mDNSBool setup; + mDNSBool connected; + handshakeStatus handshake; + mDNS *m; // So we can call KQueueLock from the SSLHandshake thread + mStatus err; +}; + struct NetworkInterfaceInfoOSX_struct { NetworkInterfaceInfo ifinfo; // MUST be the first element in this structure @@ -80,6 +141,7 @@ struct NetworkInterfaceInfoOSX_struct mDNSu8 Occulting; // Set if interface vanished for less than 60 seconds and then came back mDNSu8 D2DInterface; // IFEF_LOCALNET_PRIVATE flag indicates we should call // D2D plugin for operations over this interface + mDNSu8 DirectLink; // IFEF_DIRECTLINK flag is set for interface mDNSs32 AppearanceTime; // Time this interface appeared most recently in getifaddrs list // i.e. the first time an interface is seen, AppearanceTime is set. @@ -145,20 +207,23 @@ struct mDNS_PlatformSupport_struct #endif mDNSs32 BigMutexStartTime; int WakeKQueueLoopFD; + mDNSu8 v4answers; // non-zero if we are receiving answers + mDNSu8 v6answers; // for A/AAAA from external DNS servers + mDNSs32 DNSTrigger; // Time the DNSTrigger was given + uint64_t LastConfigGeneration; // DNS configuration generation number + UDPSocket UDPProxy; + TCPSocket TCPProxy; + ProxyCallback *UDPProxyCallback; + ProxyCallback *TCPProxyCallback; +#if TARGET_OS_IPHONE + cellular_usage_policy_client_t handle; +#endif }; extern int OfferSleepProxyService; extern int DisableSleepProxyClient; extern int UseInternalSleepProxy; extern int OSXVers, iOSVers; -#define OSXVers_Base 4 -#define OSXVers_10_0_Cheetah 4 -#define OSXVers_10_1_Puma 5 -#define OSXVers_10_2_Jaguar 6 -#define OSXVers_10_3_Panther 7 -#define OSXVers_10_4_Tiger 8 -#define OSXVers_10_5_Leopard 9 -#define OSXVers_10_6_SnowLeopard 10 extern int KQueueFD; @@ -168,6 +233,9 @@ extern void mDNSMacOSXNetworkChanged(mDNS *const m); extern void mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring); extern NetworkInterfaceInfoOSX *IfindexToInterfaceInfoOSX(const mDNS *const m, mDNSInterfaceID ifindex); extern void mDNSUpdatePacketFilter(const ResourceRecord *const excludeRecord); +extern void myKQSocketCallBack(int s1, short filter, void *context); +extern void mDNSDynamicStoreSetConfig(int key, const char *subkey, CFPropertyListRef value); +extern void UpdateDebugState(void); #ifdef MDNSRESPONDER_USES_LIB_DISPATCH_AS_PRIMARY_EVENT_LOOP_MECHANISM extern int KQueueSet(int fd, u_short flags, short filter, KQueueEntry *const entryRef); @@ -179,12 +247,15 @@ extern int KQueueSet(int fd, u_short flags, short filter, const KQueueEntry *con // When events are processed on the non-kqueue thread (i.e. CFRunLoop notifications like Sleep/Wake, // Interface changes, Keychain changes, etc.) they must use KQueueLock/KQueueUnlock to lock out the kqueue thread extern void KQueueLock(mDNS *const m); -extern void KQueueUnlock(mDNS *const m, const char const *task); +extern void KQueueUnlock(mDNS *const m, const char* task); extern void mDNSPlatformCloseFD(KQueueEntry *kq, int fd); +extern ssize_t myrecvfrom(const int s, void *const buffer, const size_t max, + struct sockaddr *const from, size_t *const fromlen, mDNSAddr *dstaddr, char *ifname, mDNSu8 *ttl); extern mDNSBool DictionaryIsEnabled(CFDictionaryRef dict); -extern void mDNSPlatformToggleInterfaceAdvt(mDNS *const m, mDNSBool stopAdvt); +extern void CUPInit(mDNS *const m); +extern const char *DNSScopeToString(mDNSu32 scope); // If any event takes more than WatchDogReportingThreshold milliseconds to be processed, we log a warning message // General event categories are: @@ -209,8 +280,12 @@ 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) <= 7000) ? 1 : -1]; - char sizecheck_mDNS_PlatformSupport [(sizeof(mDNS_PlatformSupport) <= 768) ? 1 : -1]; + + // Checks commented out when sizeof(DNSQuestion) change cascaded into having to change yet another + // set of hardcoded size values because these structures contain one or more DNSQuestion + // instances. +// char sizecheck_NetworkInterfaceInfoOSX[(sizeof(NetworkInterfaceInfoOSX) <= 7084) ? 1 : -1]; + char sizecheck_mDNS_PlatformSupport [(sizeof(mDNS_PlatformSupport) <= 1378) ? 1 : -1]; }; #ifdef __cplusplus diff --git a/mDNSMacOSX/mDNSResponder-entitlements.plist b/mDNSMacOSX/mDNSResponder-entitlements.plist index 6c42a2e..21b0810 100644 --- a/mDNSMacOSX/mDNSResponder-entitlements.plist +++ b/mDNSMacOSX/mDNSResponder-entitlements.plist @@ -4,5 +4,19 @@ com.apple.wifi.manager-access + com.apple.SystemConfiguration.trailing-edge-agent + + com.apple.private.network.socket-delegate + + com.apple.networkd.cellular_blocked.notify + + com.apple.private.SCNetworkConnection-proxy-user + + com.apple.private.network.reserved-port + + com.apple.SystemConfiguration.SCDynamicStore-write-access + + com.apple.private.snhelper + diff --git a/mDNSMacOSX/mDNSResponder.sb b/mDNSMacOSX/mDNSResponder.sb index 7fa2643..65b31ba 100644 --- a/mDNSMacOSX/mDNSResponder.sb +++ b/mDNSMacOSX/mDNSResponder.sb @@ -53,6 +53,8 @@ (global-name "com.apple.SecurityServer") (global-name "com.apple.SystemConfiguration.configd") (global-name "com.apple.SystemConfiguration.SCNetworkReachability") + (global-name "com.apple.SystemConfiguration.DNSConfiguration") + (global-name "com.apple.SystemConfiguration.NetworkInformation") (global-name "com.apple.system.notification_center") (global-name "com.apple.system.logger") (global-name "com.apple.webcontentfilter.dns") @@ -61,7 +63,12 @@ (global-name "com.apple.networkd") (global-name "com.apple.securityd") (global-name "com.apple.wifi.manager") - (global-name "com.apple.blued")) + (global-name "com.apple.commcenter.cupolicy.xpc") + (global-name "com.apple.blued") + (global-name "com.apple.snhelper")) + +(allow mach-register + (global-name "com.apple.d2d.ipc")) ; Networking, including Unix Domain Sockets (allow network*) @@ -99,6 +106,9 @@ (literal "/Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist") (literal "/private/var/preferences/SystemConfiguration/preferences.plist")) +; For MAC Address +(allow system-info (info-type "net.link.addr")) + ; We just need access to System.keychain. But we don't want errors logged if other keychains are ; accessed under /Library/Keychains. Other keychains may be accessed as part of setting up an SSL ; connection. Instead of adding access to it here (to things which we don't need), we disable any @@ -106,6 +116,13 @@ (deny file-read-data (regex #"^/Library/Keychains/") (with no-log)) (allow file-read-data (literal "/Library/Keychains/System.keychain")) +; Access to mDNSResponder Managed Preferences profile +; instead of using (mobile-preferences-read "com.apple.mDNSResponder") we use the lines below for OSX compatibility +(allow file-read* (literal "/private/var/Managed Preferences/mobile")) +(allow file-read* (literal "/private/var/Library/Preferences/")) +(allow file-read* (literal "/Library/Managed Preferences")) +(allow file-read* (literal "/private/var/Managed Preferences/mobile/com.apple.mDNSResponder.plist")) + ; Our Module Directory Services cache (allow file-read-data (subpath "/private/var/tmp/mds") @@ -128,4 +145,6 @@ (allow iokit-open (iokit-user-client-class "NVEthernetUserClientMDNS") (iokit-user-client-class "mDNSOffloadUserClient") - (iokit-user-client-class "RootDomainUserClient")))) + (iokit-user-client-class "wlDNSOffloadUserClient") + (iokit-user-client-class "RootDomainUserClient") + (iokit-user-client-class "AppleMobileFileIntegrityUserClient")))) diff --git a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj index 95f6f83..597478f 100644 --- a/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj +++ b/mDNSMacOSX/mDNSResponder.xcodeproj/project.pbxproj @@ -33,6 +33,8 @@ 03067D6A0C83A3890022BE1F /* PBXTargetDependency */, 03067D6C0C83A3920022BE1F /* PBXTargetDependency */, 03067D6E0C83A39C0022BE1F /* PBXTargetDependency */, + 84C5B3411665544B00C324A8 /* PBXTargetDependency */, + 72FB546A166D5FE40090B2D9 /* PBXTargetDependency */, ); name = "Build Some"; productName = "Build Some"; @@ -91,12 +93,22 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ + 21070E5F16486B9000A69507 /* DNSSECSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21070E5D16486B9000A69507 /* DNSSECSupport.c */; }; + 21070E6016486B9000A69507 /* DNSSECSupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21070E5D16486B9000A69507 /* DNSSECSupport.c */; }; + 21070E6116486B9000A69507 /* DNSSECSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21070E5E16486B9000A69507 /* DNSSECSupport.h */; }; + 21070E6216486B9000A69507 /* DNSSECSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21070E5E16486B9000A69507 /* DNSSECSupport.h */; }; + 2120ABD516B71614007089B6 /* CUPolicy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2120ABD416B71614007089B6 /* CUPolicy.c */; }; + 2120ABD616B71614007089B6 /* CUPolicy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2120ABD416B71614007089B6 /* CUPolicy.c */; }; 2124FA2C1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; }; 2124FA2D1471E98C0021D7BB /* nsec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2B1471E98C0021D7BB /* nsec.h */; }; 2124FA301471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; }; 2124FA311471E9B50021D7BB /* dnssec.h in Headers */ = {isa = PBXBuildFile; fileRef = 2124FA2F1471E9B50021D7BB /* dnssec.h */; }; 2124FA331471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; }; 2124FA341471E9DE0021D7BB /* nsec.c in Sources */ = {isa = PBXBuildFile; fileRef = 2124FA321471E9DE0021D7BB /* nsec.c */; }; + 2127A47715C3C7B900A857FC /* nsec3.c in Sources */ = {isa = PBXBuildFile; fileRef = 2127A47515C3C7B900A857FC /* nsec3.c */; }; + 2127A47815C3C7B900A857FC /* nsec3.c in Sources */ = {isa = PBXBuildFile; fileRef = 2127A47515C3C7B900A857FC /* nsec3.c */; }; + 2127A47915C3C7B900A857FC /* nsec3.h in Headers */ = {isa = PBXBuildFile; fileRef = 2127A47615C3C7B900A857FC /* nsec3.h */; }; + 2127A47A15C3C7B900A857FC /* nsec3.h in Headers */ = {isa = PBXBuildFile; fileRef = 2127A47615C3C7B900A857FC /* nsec3.h */; }; 213BDC6D147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; }; 213BDC6E147319F400000896 /* dnssec.c in Sources */ = {isa = PBXBuildFile; fileRef = 213BDC6C147319F400000896 /* dnssec.c */; }; 213FB23C12028C4A002B3A08 /* BonjourEvents.c in Sources */ = {isa = PBXBuildFile; fileRef = 213FB22C12028B53002B3A08 /* BonjourEvents.c */; }; @@ -119,6 +131,12 @@ 215FFAFD1240013400470DE1 /* DNSServiceDiscovery.c in Sources */ = {isa = PBXBuildFile; fileRef = FFA572480AF18FCC0055A0F1 /* DNSServiceDiscovery.c */; }; 215FFAFE1240013400470DE1 /* DNSServiceDiscoveryRequest.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */; }; 215FFAFF1240013400470DE1 /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Server, ); }; }; + 216D9ACE1720C9F5008066E1 /* VPNService.c in Sources */ = {isa = PBXBuildFile; fileRef = 216D9ACD1720C9F5008066E1 /* VPNService.c */; }; + 216D9ACF1720C9F5008066E1 /* VPNService.c in Sources */ = {isa = PBXBuildFile; fileRef = 216D9ACD1720C9F5008066E1 /* VPNService.c */; }; + 218E8E51156D8C0300720DA0 /* dnsproxy.c in Sources */ = {isa = PBXBuildFile; fileRef = 218E8E4F156D8C0300720DA0 /* dnsproxy.c */; }; + 218E8E52156D8C0300720DA0 /* dnsproxy.c in Sources */ = {isa = PBXBuildFile; fileRef = 218E8E4F156D8C0300720DA0 /* dnsproxy.c */; }; + 218E8E53156D8C0300720DA0 /* dnsproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 218E8E50156D8C0300720DA0 /* dnsproxy.h */; }; + 218E8E54156D8C0300720DA0 /* dnsproxy.h in Headers */ = {isa = PBXBuildFile; fileRef = 218E8E50156D8C0300720DA0 /* dnsproxy.h */; }; 219D5542149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; }; 219D5543149ED645004464AE /* libxml2.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 219D5541149ED645004464AE /* libxml2.2.dylib */; }; 21A57F4C145B2AE100939099 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; @@ -131,8 +149,14 @@ 21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 21A57F52145B2B1400939099 /* CryptoSupport.h */; }; 21DCD05C1461B23700702FC8 /* CryptoAlg.c in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4A145B2AE100939099 /* CryptoAlg.c */; }; 21DCD05D1461B23700702FC8 /* CryptoAlg.h in Sources */ = {isa = PBXBuildFile; fileRef = 21A57F4B145B2AE100939099 /* CryptoAlg.h */; }; + 21DD8FBF161E9A250033C8F8 /* anonymous.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8FBD161E9A250033C8F8 /* anonymous.c */; }; + 21DD8FC0161E9A250033C8F8 /* anonymous.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DD8FBD161E9A250033C8F8 /* anonymous.c */; }; + 21DD8FC1161E9A250033C8F8 /* anonymous.h in Headers */ = {isa = PBXBuildFile; fileRef = 21DD8FBE161E9A250033C8F8 /* anonymous.h */; }; + 21DD8FC2161E9A250033C8F8 /* anonymous.h in Headers */ = {isa = PBXBuildFile; fileRef = 21DD8FBE161E9A250033C8F8 /* anonymous.h */; }; + 21DED43515702C0F0060B6B9 /* DNSProxySupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */; }; + 21DED43615702C0F0060B6B9 /* DNSProxySupport.c in Sources */ = {isa = PBXBuildFile; fileRef = 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */; }; 2E0405F50C3195F700F13B59 /* helper.c in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405F40C3195F700F13B59 /* helper.c */; }; - 2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; settings = {ATTRIBUTES = (Server, Client, ); COMPILER_FLAGS = "-Wno-error"; }; }; + 2E0405F60C31961100F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; settings = {ATTRIBUTES = (Client, Server, ); COMPILER_FLAGS = "-Wno-error"; }; }; 2E0406150C3197CB00F13B59 /* libbsm.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E0406140C3197CB00F13B59 /* libbsm.dylib */; }; 2E04061F0C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; }; 2E0406200C3198B700F13B59 /* helpermsg.defs in Sources */ = {isa = PBXBuildFile; fileRef = 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */; }; @@ -167,6 +191,17 @@ 4AAE0C9A0C68EA81003882A5 /* mDNSResponderHelper.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4AAE0C7A0C68E97F003882A5 /* mDNSResponderHelper.8 */; }; 4BD2B63A134FE09F002B96D5 /* P2PPacketFilter.c in Sources */ = {isa = PBXBuildFile; fileRef = 4BD2B638134FE09F002B96D5 /* P2PPacketFilter.c */; }; 4BD2B63B134FE09F002B96D5 /* P2PPacketFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD2B639134FE09F002B96D5 /* P2PPacketFilter.h */; }; + 72FB5467166D5FCA0090B2D9 /* dnsctl.c in Sources */ = {isa = PBXBuildFile; fileRef = 72FB545A166D5F960090B2D9 /* dnsctl.c */; }; + 72FB5468166D5FD20090B2D9 /* libdns_services.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 84C5B3351665529800C324A8 /* libdns_services.dylib */; }; + 8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */; }; + 848DA5C7165477E000D2E8B4 /* xpc_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 848DA5C6165477E000D2E8B4 /* xpc_services.c */; }; + 848DA5C8165477E000D2E8B4 /* xpc_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 848DA5C6165477E000D2E8B4 /* xpc_services.c */; }; + 848DA5CA165477EB00D2E8B4 /* xpc_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5C9165477EB00D2E8B4 /* xpc_services.h */; }; + 848DA5CB165477EB00D2E8B4 /* xpc_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5C9165477EB00D2E8B4 /* xpc_services.h */; }; + 848DA5D616547F7200D2E8B4 /* dns_xpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5D516547F7200D2E8B4 /* dns_xpc.h */; }; + 848DA5D716547F7200D2E8B4 /* dns_xpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 848DA5D516547F7200D2E8B4 /* dns_xpc.h */; }; + 84C5B33C166553F100C324A8 /* dns_services.c in Sources */ = {isa = PBXBuildFile; fileRef = 84C5B339166553AF00C324A8 /* dns_services.c */; }; + 84C5B33D166553F900C324A8 /* dns_services.h in Headers */ = {isa = PBXBuildFile; fileRef = 84C5B338166553A000C324A8 /* dns_services.h */; settings = {ATTRIBUTES = (Private, ); }; }; D284BE530ADD80740027CCDF /* DNSServiceDiscoveryDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 6575FBFF022EAFBA00000109 /* DNSServiceDiscoveryDefines.h */; }; D284BE540ADD80740027CCDF /* dnssd_ipc.h in Headers */ = {isa = PBXBuildFile; fileRef = F5E11B5B04A28126019798ED /* dnssd_ipc.h */; }; D284BE560ADD80740027CCDF /* DNSServiceDiscoveryReply.defs in Sources */ = {isa = PBXBuildFile; fileRef = 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */; settings = {ATTRIBUTES = (Client, ); }; }; @@ -394,6 +429,20 @@ remoteGlobalIDString = 4AE471670EAFF81900A6C5AD; remoteInfo = dns_sd.jar; }; + 72FB5469166D5FE40090B2D9 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 72FB545E166D5FB00090B2D9; + remoteInfo = dnsctl; + }; + 84C5B3401665544B00C324A8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 84C5B3341665529800C324A8; + remoteInfo = dns_services; + }; D284BF2B0ADD815A0027CCDF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; @@ -483,6 +532,25 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 72FB545D166D5FB00090B2D9 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 12; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8418673D15AB8BFF00BB7F70 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /private/etc/asl/; + dstSubfolderSpec = 0; + files = ( + 8418673E15AB8C2D00BB7F70 /* com.apple.networking.mDNSResponder in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; D284BE6A0ADD80740027CCDF /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 8; @@ -529,9 +597,14 @@ 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mDNSMacOSX.h; sourceTree = ""; }; 00CA213D02786FC30CCA2C71 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; 09AB6884FE841BABC02AAC07 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + 21070E5D16486B9000A69507 /* DNSSECSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSSECSupport.c; sourceTree = ""; }; + 21070E5E16486B9000A69507 /* DNSSECSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DNSSECSupport.h; sourceTree = ""; }; + 2120ABD416B71614007089B6 /* CUPolicy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CUPolicy.c; sourceTree = ""; }; 2124FA2B1471E98C0021D7BB /* nsec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec.h; path = ../mDNSCore/nsec.h; sourceTree = ""; }; 2124FA2F1471E9B50021D7BB /* dnssec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssec.h; path = ../mDNSCore/dnssec.h; sourceTree = ""; }; 2124FA321471E9DE0021D7BB /* nsec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec.c; path = ../mDNSCore/nsec.c; sourceTree = ""; }; + 2127A47515C3C7B900A857FC /* nsec3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = nsec3.c; path = ../mDNSCore/nsec3.c; sourceTree = ""; }; + 2127A47615C3C7B900A857FC /* nsec3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nsec3.h; path = ../mDNSCore/nsec3.h; sourceTree = ""; }; 213BDC6C147319F400000896 /* dnssec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssec.c; path = ../mDNSCore/dnssec.c; sourceTree = ""; }; 213FB21812028A7A002B3A08 /* BonjourEvents.plugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BonjourEvents.plugin; sourceTree = BUILT_PRODUCTS_DIR; }; 213FB22C12028B53002B3A08 /* BonjourEvents.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = BonjourEvents.c; sourceTree = ""; }; @@ -539,11 +612,17 @@ 2141DD1D123FFCDB0086D23E /* libdns_sd.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd.a; sourceTree = BUILT_PRODUCTS_DIR; }; 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_debug.a; sourceTree = BUILT_PRODUCTS_DIR; }; 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdns_sd_profile.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 216D9ACD1720C9F5008066E1 /* VPNService.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = VPNService.c; sourceTree = ""; }; + 218E8E4F156D8C0300720DA0 /* dnsproxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnsproxy.c; path = ../mDNSCore/dnsproxy.c; sourceTree = ""; }; + 218E8E50156D8C0300720DA0 /* dnsproxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnsproxy.h; path = ../mDNSCore/dnsproxy.h; sourceTree = ""; }; 219D5541149ED645004464AE /* libxml2.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.2.dylib; path = SDKs/MacOSX10.8.sdk/usr/lib/libxml2.2.dylib; sourceTree = DEVELOPER_DIR; }; 21A57F4A145B2AE100939099 /* CryptoAlg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = CryptoAlg.c; path = ../mDNSCore/CryptoAlg.c; sourceTree = ""; }; 21A57F4B145B2AE100939099 /* CryptoAlg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CryptoAlg.h; path = ../mDNSCore/CryptoAlg.h; sourceTree = ""; }; 21A57F51145B2B1400939099 /* CryptoSupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = CryptoSupport.c; sourceTree = ""; }; 21A57F52145B2B1400939099 /* CryptoSupport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoSupport.h; sourceTree = ""; }; + 21DD8FBD161E9A250033C8F8 /* anonymous.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = anonymous.c; path = ../mDNSCore/anonymous.c; sourceTree = ""; }; + 21DD8FBE161E9A250033C8F8 /* anonymous.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = anonymous.h; path = ../mDNSCore/anonymous.h; sourceTree = ""; }; + 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DNSProxySupport.c; sourceTree = ""; }; 21F432971134AA6800581B69 /* WebFilterDNS.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebFilterDNS.framework; path = /System/Library/PrivateFrameworks/WebFilterDNS.framework; sourceTree = ""; }; 2E0405EB0C3190DC00F13B59 /* helpermsg.defs */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.mig; path = helpermsg.defs; sourceTree = ""; }; 2E0405F00C31955500F13B59 /* mDNSResponderHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponderHelper; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -577,11 +656,21 @@ 6575FC00022EAFBA00000109 /* DNSServiceDiscoveryReply.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryReply.defs; sourceTree = ""; }; 6575FC01022EAFBA00000109 /* DNSServiceDiscoveryRequest.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = DNSServiceDiscoveryRequest.defs; sourceTree = ""; }; 6575FC20022EB7AA00000109 /* SamplemDNSClient.c */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.c; path = SamplemDNSClient.c; sourceTree = SOURCE_ROOT; tabWidth = 4; usesTabs = 0; }; + 72FB545A166D5F960090B2D9 /* dnsctl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dnsctl.c; path = ../Clients/dnsctl.c; sourceTree = ""; }; + 72FB545F166D5FB00090B2D9 /* dnsctl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dnsctl; sourceTree = BUILT_PRODUCTS_DIR; }; 7F18A9F60587CEF6001880B3 /* DNSCommon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSCommon.c; path = ../mDNSCore/DNSCommon.c; sourceTree = SOURCE_ROOT; }; 7F18A9F70587CEF6001880B3 /* uDNS.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uDNS.c; path = ../mDNSCore/uDNS.c; sourceTree = SOURCE_ROOT; }; 7F461DB5062DBF2900672BF3 /* DNSDigest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = DNSDigest.c; path = ../mDNSCore/DNSDigest.c; sourceTree = SOURCE_ROOT; }; 7F869685066EE02400D2A2DC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = ""; }; 7FC8F9D406D14E66007E879D /* LegacyNATTraversal.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = LegacyNATTraversal.c; sourceTree = SOURCE_ROOT; }; + 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */ = {isa = PBXFileReference; lastKnownFileType = text; path = com.apple.networking.mDNSResponder; sourceTree = ""; }; + 8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = mDNSResponderLogging.mobileconfig; sourceTree = ""; }; + 848DA5C6165477E000D2E8B4 /* xpc_services.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xpc_services.c; path = Private/xpc_services.c; sourceTree = ""; }; + 848DA5C9165477EB00D2E8B4 /* xpc_services.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xpc_services.h; path = Private/xpc_services.h; sourceTree = ""; }; + 848DA5D516547F7200D2E8B4 /* dns_xpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dns_xpc.h; path = Private/dns_xpc.h; sourceTree = ""; }; + 84C5B3351665529800C324A8 /* libdns_services.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libdns_services.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + 84C5B338166553A000C324A8 /* dns_services.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = dns_services.h; path = Private/dns_services.h; sourceTree = ""; }; + 84C5B339166553AF00C324A8 /* dns_services.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = dns_services.c; path = Private/dns_services.c; sourceTree = ""; }; D284BE730ADD80740027CCDF /* mDNSResponder */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder; sourceTree = BUILT_PRODUCTS_DIR; }; D284BE950ADD80800027CCDF /* mDNSResponder.debug */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mDNSResponder.debug; sourceTree = BUILT_PRODUCTS_DIR; }; D284BEB00ADD80920027CCDF /* dns-sd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dns-sd"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -610,7 +699,6 @@ F5E11B5A04A28126019798ED /* dnssd_ipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dnssd_ipc.c; path = ../mDNSShared/dnssd_ipc.c; sourceTree = SOURCE_ROOT; }; F5E11B5B04A28126019798ED /* dnssd_ipc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dnssd_ipc.h; path = ../mDNSShared/dnssd_ipc.h; sourceTree = SOURCE_ROOT; }; FF08480607CEB8E800AE6769 /* inprogress.tiff */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; name = inprogress.tiff; path = PreferencePane/Artwork/inprogress.tiff; sourceTree = SOURCE_ROOT; }; - FF0E0B5D065ADC7600FE4D9C /* mDNS.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = mDNS.1; path = ../mDNSShared/mDNS.1; sourceTree = SOURCE_ROOT; }; FF13FFEA0A5DA44A00897C81 /* dnsextd_lexer.l */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.lex; name = dnsextd_lexer.l; path = ../mDNSShared/dnsextd_lexer.l; sourceTree = SOURCE_ROOT; }; FF13FFEC0A5DA45500897C81 /* dnsextd_parser.y */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.yacc; name = dnsextd_parser.y; path = ../mDNSShared/dnsextd_parser.y; sourceTree = SOURCE_ROOT; }; FF1C919D07021D77001048AB /* dns-sd.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; name = "dns-sd.1"; path = "../mDNSShared/dns-sd.1"; sourceTree = SOURCE_ROOT; }; @@ -698,6 +786,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 72FB545C166D5FB00090B2D9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 72FB5468166D5FD20090B2D9 /* libdns_services.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84C5B3321665529800C324A8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D284BE640ADD80740027CCDF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -814,6 +917,25 @@ 08FB7795FE84155DC02AAC07 /* mDNS Server Sources */ = { isa = PBXGroup; children = ( + 216D9ACD1720C9F5008066E1 /* VPNService.c */, + 2120ABD416B71614007089B6 /* CUPolicy.c */, + 84C5B338166553A000C324A8 /* dns_services.h */, + 72FB545A166D5F960090B2D9 /* dnsctl.c */, + 84C5B339166553AF00C324A8 /* dns_services.c */, + 848DA5D516547F7200D2E8B4 /* dns_xpc.h */, + 848DA5C9165477EB00D2E8B4 /* xpc_services.h */, + 848DA5C6165477E000D2E8B4 /* xpc_services.c */, + 21070E5D16486B9000A69507 /* DNSSECSupport.c */, + 21070E5E16486B9000A69507 /* DNSSECSupport.h */, + 21DD8FBD161E9A250033C8F8 /* anonymous.c */, + 21DD8FBE161E9A250033C8F8 /* anonymous.h */, + 2127A47515C3C7B900A857FC /* nsec3.c */, + 2127A47615C3C7B900A857FC /* nsec3.h */, + 8418673C15AB8B8000BB7F70 /* mDNSResponderLogging.mobileconfig */, + 8418673A15AB8B6900BB7F70 /* com.apple.networking.mDNSResponder */, + 21DED43415702C0F0060B6B9 /* DNSProxySupport.c */, + 218E8E4F156D8C0300720DA0 /* dnsproxy.c */, + 218E8E50156D8C0300720DA0 /* dnsproxy.h */, 213BDC6C147319F400000896 /* dnssec.c */, 2124FA321471E9DE0021D7BB /* nsec.c */, 2124FA2F1471E9B50021D7BB /* dnssec.h */, @@ -849,7 +971,6 @@ 000753D303367C1C0CCA2C71 /* mDNSMacOSX.h */, DBAAFE2C057E8F660085CAD0 /* GenLinkedList.c */, FFCB6D73075D539900B8AF62 /* PlatformCommon.c */, - FF0E0B5D065ADC7600FE4D9C /* mDNS.1 */, FF1C919D07021D77001048AB /* dns-sd.1 */, FF485D5105632E0000130380 /* mDNSResponder.8 */, FFF4F63A06CFE4DD00459EFD /* dnsextd.8 */, @@ -912,6 +1033,8 @@ 2141DD1D123FFCDB0086D23E /* libdns_sd.a */, 2141DD24123FFD0F0086D23E /* libdns_sd_debug.a */, 2141DD2A123FFD2C0086D23E /* libdns_sd_profile.a */, + 84C5B3351665529800C324A8 /* libdns_services.dylib */, + 72FB545F166D5FB00090B2D9 /* dnsctl */, ); name = Products; sourceTree = ""; @@ -1041,6 +1164,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 84C5B3331665529800C324A8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 84C5B33D166553F900C324A8 /* dns_services.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D284BE520ADD80740027CCDF /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -1055,6 +1186,12 @@ 21A57F55145B2B1400939099 /* CryptoSupport.h in Headers */, 2124FA2C1471E98C0021D7BB /* nsec.h in Headers */, 2124FA301471E9B50021D7BB /* dnssec.h in Headers */, + 218E8E53156D8C0300720DA0 /* dnsproxy.h in Headers */, + 2127A47915C3C7B900A857FC /* nsec3.h in Headers */, + 21DD8FC1161E9A250033C8F8 /* anonymous.h in Headers */, + 21070E6116486B9000A69507 /* DNSSECSupport.h in Headers */, + 848DA5CA165477EB00D2E8B4 /* xpc_services.h in Headers */, + 848DA5D616547F7200D2E8B4 /* dns_xpc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1075,6 +1212,12 @@ 21A57F56145B2B1400939099 /* CryptoSupport.h in Headers */, 2124FA2D1471E98C0021D7BB /* nsec.h in Headers */, 2124FA311471E9B50021D7BB /* dnssec.h in Headers */, + 218E8E54156D8C0300720DA0 /* dnsproxy.h in Headers */, + 2127A47A15C3C7B900A857FC /* nsec3.h in Headers */, + 21DD8FC2161E9A250033C8F8 /* anonymous.h in Headers */, + 21070E6216486B9000A69507 /* DNSSECSupport.h in Headers */, + 848DA5CB165477EB00D2E8B4 /* xpc_services.h in Headers */, + 848DA5D716547F7200D2E8B4 /* dns_xpc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1245,6 +1388,40 @@ productReference = 2E0405F00C31955500F13B59 /* mDNSResponderHelper */; productType = "com.apple.product-type.tool"; }; + 72FB545E166D5FB00090B2D9 /* dnsctl */ = { + isa = PBXNativeTarget; + buildConfigurationList = 72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */; + buildPhases = ( + 72FB545B166D5FB00090B2D9 /* Sources */, + 72FB545C166D5FB00090B2D9 /* Frameworks */, + 72FB545D166D5FB00090B2D9 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dnsctl; + productName = dnsctl; + productReference = 72FB545F166D5FB00090B2D9 /* dnsctl */; + productType = "com.apple.product-type.tool"; + }; + 84C5B3341665529800C324A8 /* dns_services */ = { + isa = PBXNativeTarget; + buildConfigurationList = 84C5B3361665529800C324A8 /* Build configuration list for PBXNativeTarget "dns_services" */; + buildPhases = ( + 84C5B3311665529800C324A8 /* Sources */, + 84C5B3321665529800C324A8 /* Frameworks */, + 84C5B3331665529800C324A8 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dns_services; + productName = dns_services; + productReference = 84C5B3351665529800C324A8 /* libdns_services.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; D284BE500ADD80740027CCDF /* mDNSResponder */ = { isa = PBXNativeTarget; buildConfigurationList = D284BE6D0ADD80740027CCDF /* Build configuration list for PBXNativeTarget "mDNSResponder" */; @@ -1258,6 +1435,7 @@ 4A7B9E7F14FDA21B00B84CC1 /* CopyFiles */, 4A7B9E8114FDA25500B84CC1 /* CopyFiles */, D284BE6C0ADD80740027CCDF /* ShellScript */, + 8418673D15AB8BFF00BB7F70 /* CopyFiles */, ); buildRules = ( ); @@ -1450,6 +1628,8 @@ /* Begin PBXProject section */ 08FB7793FE84155DC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = D284BE2B0ADD78180027CCDF /* Build configuration list for PBXProject "mDNSResponder" */; compatibilityVersion = "Xcode 3.1"; developmentRegion = English; @@ -1486,6 +1666,8 @@ 2141DD1C123FFCDB0086D23E /* libdns_sd_static */, 2141DD23123FFD0F0086D23E /* libdns_sd_debug_static */, 2141DD29123FFD2C0086D23E /* libdns_sd_profile_static */, + 84C5B3341665529800C324A8 /* dns_services */, + 72FB545E166D5FB00090B2D9 /* dnsctl */, ); }; /* End PBXProject section */ @@ -1604,7 +1786,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include/DNSServiceDiscovery\"\ncp $SRCROOT/DNSServiceDiscovery.h $DSTROOT/usr/include/DNSServiceDiscovery\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; + shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include\"\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; }; 21DE714D115831CB00DD4BD1 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -1617,7 +1799,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include/DNSServiceDiscovery\"\ncp $SRCROOT/DNSServiceDiscovery.h $DSTROOT/usr/include/DNSServiceDiscovery\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; + shellScript = "#if we are building for simulator, change the installation path\nif [ \"${RC_ProjectName%_Sim}\" != \"${RC_ProjectName}\" ] ; then\n\tDSTROOT=${DSTROOT}${SDKROOT}\nfi\nmkdir -p \"$DSTROOT/usr/include\"\nsed 's/\\(^#define _DNS_SD_LIBDISPATCH \\)0$/\\1 1/' \"$SRCROOT/../mDNSShared/dns_sd.h\" > \"$DSTROOT/usr/include/dns_sd.h\""; }; 4A4EE3A413CB8E82005C624B /* Build yacc file into derived source files */ = { isa = PBXShellScriptBuildPhase; @@ -1747,6 +1929,22 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 72FB545B166D5FB00090B2D9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72FB5467166D5FCA0090B2D9 /* dnsctl.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 84C5B3311665529800C324A8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 84C5B33C166553F100C324A8 /* dns_services.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D284BE550ADD80740027CCDF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1770,6 +1968,14 @@ 21A57F53145B2B1400939099 /* CryptoSupport.c in Sources */, 2124FA331471E9DE0021D7BB /* nsec.c in Sources */, 213BDC6D147319F400000896 /* dnssec.c in Sources */, + 218E8E51156D8C0300720DA0 /* dnsproxy.c in Sources */, + 21DED43515702C0F0060B6B9 /* DNSProxySupport.c in Sources */, + 216D9ACE1720C9F5008066E1 /* VPNService.c in Sources */, + 2127A47715C3C7B900A857FC /* nsec3.c in Sources */, + 21DD8FBF161E9A250033C8F8 /* anonymous.c in Sources */, + 21070E5F16486B9000A69507 /* DNSSECSupport.c in Sources */, + 848DA5C7165477E000D2E8B4 /* xpc_services.c in Sources */, + 2120ABD516B71614007089B6 /* CUPolicy.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1796,6 +2002,14 @@ 21A57F54145B2B1400939099 /* CryptoSupport.c in Sources */, 2124FA341471E9DE0021D7BB /* nsec.c in Sources */, 213BDC6E147319F400000896 /* dnssec.c in Sources */, + 218E8E52156D8C0300720DA0 /* dnsproxy.c in Sources */, + 21DED43615702C0F0060B6B9 /* DNSProxySupport.c in Sources */, + 216D9ACF1720C9F5008066E1 /* VPNService.c in Sources */, + 2127A47815C3C7B900A857FC /* nsec3.c in Sources */, + 21DD8FC0161E9A250033C8F8 /* anonymous.c in Sources */, + 21070E6016486B9000A69507 /* DNSSECSupport.c in Sources */, + 848DA5C8165477E000D2E8B4 /* xpc_services.c in Sources */, + 2120ABD616B71614007089B6 /* CUPolicy.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1962,6 +2176,16 @@ target = 4AE471670EAFF81900A6C5AD /* dns_sd.jar */; targetProxy = 4AE471690EAFF83800A6C5AD /* PBXContainerItemProxy */; }; + 72FB546A166D5FE40090B2D9 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 72FB545E166D5FB00090B2D9 /* dnsctl */; + targetProxy = 72FB5469166D5FE40090B2D9 /* PBXContainerItemProxy */; + }; + 84C5B3411665544B00C324A8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 84C5B3341665529800C324A8 /* dns_services */; + targetProxy = 84C5B3401665544B00C324A8 /* PBXContainerItemProxy */; + }; D284BF2C0ADD815A0027CCDF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D284BEBF0ADD80A20027CCDF /* dnsextd */; @@ -2042,6 +2266,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; BUNDLE_LOADER = /usr/libexec/UserEventAgent; + CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_MODEL_TUNING = G5; @@ -2049,6 +2274,7 @@ INSTALL_PATH = /System/Library/UserEventPlugins/; PREBINDING = NO; PRODUCT_NAME = BonjourEvents; + PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = plugin; }; name = Development; @@ -2150,10 +2376,7 @@ LD_MAP_FILE_PATH = "$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt"; LIBRARY_SEARCH_PATHS = "\"${CONFIGURATION_TEMP_DIR}\""; MACOSX_DEPLOYMENT_TARGET = 10.5; - "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( - "$(inherited)", - "-mthumb", - ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = "$(inherited)"; OTHER_LDFLAGS = ( "$(inherited)", "-lipsec", @@ -2179,6 +2402,77 @@ }; name = Development; }; + 72FB5466166D5FB00090B2D9 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "dnsctl-entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_STRICT_ALIASING = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SDKROOT = macosx; + }; + name = Development; + }; + 84C5B3371665529800C324A8 /* Development */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + EXECUTABLE_PREFIX = lib; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/lib; + MACOSX_DEPLOYMENT_TARGET = 10.8; + ONLY_ACTIVE_ARCH = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Development; + }; D284BE290ADD78180027CCDF /* Development */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2214,6 +2508,7 @@ "-DUSE_SYSTEMCONFIGURATION_PRIVATE_HEADERS", "-fwrapv", ); + "OTHER_LDFLAGS[sdk=macosx*]" = ""; PREBINDING = NO; STRIP_STYLE = debugging; WARNING_CFLAGS = ( @@ -2231,7 +2526,7 @@ D284BE6E0ADD80740027CCDF /* Development */ = { isa = XCBuildConfiguration; buildSettings = { - "CODE_SIGN_ENTITLEMENTS[sdk=iphoneos*]" = "mDNSResponder-entitlements.plist"; + "CODE_SIGN_ENTITLEMENTS[sdk=*]" = "mDNSResponder-entitlements.plist"; CODE_SIGN_IDENTITY = "-"; CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; @@ -2257,15 +2552,14 @@ "$(inherited)", "-no-cpp-precomp", ); - "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = ( - "$(inherited)", - "-mthumb", - ); + "OTHER_CFLAGS[sdk=iphoneos*][arch=*]" = "$(inherited)"; OTHER_LDFLAGS = ""; "OTHER_LDFLAGS[sdk=iphoneos*][arch=*]" = ( "-Wl,-pie", "-weak_framework", DeviceToDeviceManager, + "-lMobileGestalt", + "-lcupolicy", ); "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( "-Wl,-pie", @@ -2318,6 +2612,8 @@ "-Wl,-pie", "-weak_framework", DeviceToDeviceManager, + "-lMobileGestalt", + "-lcupolicy", ); "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = ( "-Wl,-pie", @@ -2348,7 +2644,10 @@ CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; - HEADER_SEARCH_PATHS = ../mDNSShared; + HEADER_SEARCH_PATHS = ( + ../mDNSShared, + "${SYSTEM_LIBRARY_DIR}/Frameworks/System.framework/PrivateHeaders", + ); INSTALL_PATH = /usr/bin; OTHER_CFLAGS = "-no-cpp-precomp"; OTHER_LDFLAGS = ""; @@ -2428,6 +2727,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.5; OTHER_CFLAGS = ""; OTHER_LDFLAGS = ""; + "OTHER_LDFLAGS[sdk=macosx*]" = "-Wl,-pie"; OTHER_REZFLAGS = ""; PRODUCT_NAME = ddnswriteconfig; REZ_EXECUTABLE = YES; @@ -2464,6 +2764,7 @@ CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; COPY_PHASE_STRIP = NO; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; EXECUTABLE_EXTENSION = dylib; GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -2474,11 +2775,32 @@ HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; INSTALL_PATH = /usr/lib/system; "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system"; + LINK_WITH_STANDARD_LIBRARIES = NO; OTHER_LDFLAGS = ( - "-umbrella", - System, + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_platform", + "-lsystem_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld_sim", + "-lcompiler_rt_sim", + "-lsystem_sim_c", + "-lsystem_sim_blocks", + "-ldispatch", + "-Wl,-upward-lSystem", ); - "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = ""; PRODUCT_NAME = libsystem_dnssd_debug; "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_debug; }; @@ -2490,6 +2812,7 @@ CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; COPY_PHASE_STRIP = NO; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; EXECUTABLE_EXTENSION = dylib; GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; @@ -2501,11 +2824,32 @@ HEADER_SEARCH_PATHS = "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders/"; INSTALL_PATH = /usr/lib/system; "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system"; + LINK_WITH_STANDARD_LIBRARIES = NO; OTHER_LDFLAGS = ( - "-umbrella", - System, + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_platform", + "-lsystem_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld_sim", + "-lcompiler_rt_sim", + "-lsystem_sim_c", + "-lsystem_sim_blocks", + "-ldispatch", + "-Wl,-upward-lSystem", ); - "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = ""; PRODUCT_NAME = libsystem_dnssd_profile; "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd_profile; }; @@ -2539,6 +2883,7 @@ CONFIGURATION_BUILD_DIR = "${BUILD_DIR}"; CONFIGURATION_TEMP_DIR = "${BUILD_DIR}/mDNSResponder.build"; COPY_PHASE_STRIP = NO; + DYLIB_CURRENT_VERSION = "$(RC_ProjectSourceVersion)"; EXECUTABLE_EXTENSION = dylib; GCC_DYNAMIC_NO_PIC = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; @@ -2551,11 +2896,32 @@ INSTALLHDRS_SCRIPT_PHASE = YES; INSTALL_PATH = /usr/lib/system; "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system"; + LINK_WITH_STANDARD_LIBRARIES = NO; OTHER_LDFLAGS = ( - "-umbrella", - System, + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_platform", + "-lsystem_pthread", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-ldispatch", + "-llaunch", + "-lsystem_asl", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld_sim", + "-lcompiler_rt_sim", + "-lsystem_sim_c", + "-lsystem_sim_blocks", + "-ldispatch", + "-Wl,-upward-lSystem", ); - "OTHER_LDFLAGS[sdk=iphonesimulator*][arch=*]" = ""; PRODUCT_NAME = libsystem_dnssd; "PRODUCT_NAME[sdk=iphonesimulator*]" = libsystem_sim_dnssd; }; @@ -2636,6 +3002,22 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Development; }; + 72FB5465166D5FB00090B2D9 /* Build configuration list for PBXNativeTarget "dnsctl" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 72FB5466166D5FB00090B2D9 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; + 84C5B3361665529800C324A8 /* Build configuration list for PBXNativeTarget "dns_services" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 84C5B3371665529800C324A8 /* Development */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Development; + }; D284BE280ADD78180027CCDF /* Build configuration list for PBXAggregateTarget "Build More" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/mDNSMacOSX/mDNSResponderLogging.mobileconfig b/mDNSMacOSX/mDNSResponderLogging.mobileconfig new file mode 100644 index 0000000..34ec0d9 --- /dev/null +++ b/mDNSMacOSX/mDNSResponderLogging.mobileconfig @@ -0,0 +1,57 @@ + + + + + PayloadIdentifier + com.apple.mDNSResponder + PayloadUUID + 6D0962E1-558A-44FB-8FE0-1F4F0BD157F4 + PayloadDescription + Turns on mDNSResponder Debug Logging + PayloadDisplayName + mDNSResponder Debug Logging + PayloadOrganization + Apple, Inc + PayloadType + Configuration + PayloadVersion + 2 + + ConsentText + + en + English consent text + jp + Japanese consent text + default + Default consent text - used if none of the other languages match + + + PayloadContent + + + PayloadUUID + 6D0962E1-558A-44FB-8FE0-1F4F0BD157F4 + PayloadIdentifier + com.apple.defaults.1 + PayloadType + com.apple.defaults.managed + PayloadVersion + 1 + PayloadContent + + + DefaultsDomainName + com.apple.mDNSResponder + DefaultsData + + EnableLogging + + + + + + + + + diff --git a/mDNSPosix/Client.c b/mDNSPosix/Client.c index 1b43c07..c0badf4 100755 --- a/mDNSPosix/Client.c +++ b/mDNSPosix/Client.c @@ -196,7 +196,7 @@ int main(int argc, char **argv) MakeDomainNameFromDNSNameString(&type, gServiceType); MakeDomainNameFromDNSNameString(&domain, gServiceDomain); - status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, BrowseCallback, NULL); + status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSNULL, mDNSInterface_Any, 0, mDNSfalse, mDNSfalse, BrowseCallback, NULL); // Run the platform main event loop until the user types ^C. // The BrowseCallback routine is responsible for printing diff --git a/mDNSPosix/Identify.c b/mDNSPosix/Identify.c index 745ff21..003ac63 100644 --- a/mDNSPosix/Identify.c +++ b/mDNSPosix/Identify.c @@ -217,8 +217,11 @@ mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const m q->ValidationRequired = 0; q->ValidatingResponse = 0; q->WakeOnResolve = 0; - q->UseBrackgroundTrafficClass = mDNSfalse; + q->UseBackgroundTrafficClass = mDNSfalse; + q->ProxyQuestion = 0; q->qnameOrig = mDNSNULL; + q->AnonInfo = mDNSNULL; + q->pid = mDNSPlatformGetPID(); q->QuestionCallback = callback; q->QuestionContext = NULL; diff --git a/mDNSPosix/Makefile b/mDNSPosix/Makefile index b9ec10b..d095a0f 100755 --- a/mDNSPosix/Makefile +++ b/mDNSPosix/Makefile @@ -235,7 +235,7 @@ clean: DAEMONOBJS = $(OBJDIR)/PosixDaemon.c.o $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNS.c.o \ $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/uds_daemon.c.o \ $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o \ - $(OBJDIR)/CryptoAlg.c.o + $(OBJDIR)/CryptoAlg.c.o $(OBJDIR)/anonymous.c.o # dnsextd target build dnsextd DNSEXTDOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o \ @@ -446,7 +446,7 @@ JavaDoc: Java setup # The following targets build embedded example programs SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \ $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o \ - $(OBJDIR)/CryptoAlg.c.o + $(OBJDIR)/CryptoAlg.c.o $(OBJDIR)/anonymous.c.o COMMONOBJ = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o APPOBJ = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o diff --git a/mDNSPosix/NetMonitor.c b/mDNSPosix/NetMonitor.c index 7b23fc3..1354f1f 100644 --- a/mDNSPosix/NetMonitor.c +++ b/mDNSPosix/NetMonitor.c @@ -725,8 +725,9 @@ mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const const mDNSu8 *ep = ptr; ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt); if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; } - mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n", - srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c); + if (pkt.r.resrec.rrtype != kDNSType_NSEC3) + mprintf("%#-16a (?) **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n", + srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c); } for (i=0; ih.numAdditionals; i++) diff --git a/mDNSPosix/mDNSPosix.c b/mDNSPosix/mDNSPosix.c index d2869d4..9af8ebc 100755 --- a/mDNSPosix/mDNSPosix.c +++ b/mDNSPosix/mDNSPosix.c @@ -309,6 +309,13 @@ mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int s &senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID); } +mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src) +{ + (void)m; // unused + (void)src; // unused + return mDNSfalse; +} + mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port, mDNSBool useBackgroundTrafficClass) { (void)m; // Unused @@ -432,7 +439,8 @@ mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result #pragma mark ***** DDNS Config Platform Functions #endif -mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains) +mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, + DNameListElem **BrowseDomains, mDNSBool ackConfig) { (void) m; (void) setservers; @@ -440,6 +448,9 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN (void) setsearch; (void) RegDomains; (void) BrowseDomains; + (void) ackConfig; + + return mDNStrue; } mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router) @@ -499,7 +510,7 @@ mDNSexport int ParseDNSServers(mDNS *m, const char *filePath) mDNSAddr DNSAddr; DNSAddr.type = mDNSAddrType_IPv4; DNSAddr.ip.v4.NotAnInteger = ina.s_addr; - mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse, 0, mDNSfalse, 0); + mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, 0, &DNSAddr, UnicastDNSPort, kScopeNone, 0, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse); numOfServers++; } } @@ -1362,6 +1373,38 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C return mDNSfalse; } +mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) +{ + (void)m; + (void)action; + (void)type; + (void)value; +} + +// Proxy stub functions +mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) +{ + (void) q; + (void) h; + (void) msg; + (void) ptr; + (void) limit; + + return ptr; +} + +mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[], mDNSu32 OpIf) +{ + (void) m; + (void) IpIfArr; + (void) OpIf; +} + +mDNSexport void DNSProxyTerminate(mDNS *const m) +{ + (void) m; +} + // mDNS core calls this routine to clear blocks of memory. // On the Posix platform this is a simple wrapper around ANSI C memset. mDNSexport void mDNSPlatformMemZero(void *dst, mDNSu32 len) @@ -1462,6 +1505,62 @@ mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, m return mStatus_NoError; } +mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNS *const m, mDNSAddr *raddr) +{ + (void) raddr; // Unused + (void) m; // Unused + + return mStatus_NoError; +} + +mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) +{ + (void) spsaddr; // Unused + (void) ifname; // Unused + + return mStatus_NoError; +} + +mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) +{ + (void) sock; // unused + + return (mDNSu16)-1; +} + +mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) +{ + (void) InterfaceID; // unused + + return mDNSfalse; +} + +mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) +{ + (void) m; + (void) q; + return mDNStrue; +} + +mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q) +{ + (void) m; + (void) q; + return 0; +} + +mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) +{ + (void) src; + (void) dst; + (void) q; +} + +mDNSexport mDNSs32 mDNSPlatformGetPID() +{ + return 0; +} + mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s) { if (*nfds < s + 1) *nfds = s + 1; diff --git a/mDNSShared/PlatformCommon.c b/mDNSShared/PlatformCommon.c index f650c10..d86a755 100644 --- a/mDNSShared/PlatformCommon.c +++ b/mDNSShared/PlatformCommon.c @@ -190,7 +190,10 @@ mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, m if (ident && ident[0] && mDNSPlatformClockDivisor) syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer); else -#endif +#elif APPLE_OSX_mDNSResponder + mDNSPlatformLogToFile(syslog_level, buffer); +#else syslog(syslog_level, "%s", buffer); +#endif } } diff --git a/mDNSShared/dns_sd.h b/mDNSShared/dns_sd.h index cfcdff9..bc42582 100644 --- a/mDNSShared/dns_sd.h +++ b/mDNSShared/dns_sd.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2013 Apple Computer, 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: @@ -77,7 +77,7 @@ */ #ifndef _DNS_SD_H -#define _DNS_SD_H 3793801 +#define _DNS_SD_H 5220111 #ifdef __cplusplus extern "C" { @@ -167,6 +167,12 @@ struct sockaddr; * The reliable way to test whether a particular bit is set is not with an equality test, * but with a bitwise mask: * if (flags & kDNSServiceFlagsAdd) ... + * With the exception of kDNSServiceFlagsValidate, each flag can be valid(be set) + * EITHER only as an input to one of the DNSService*() APIs OR only as an output + * (provide status) through any of the callbacks used. For example, kDNSServiceFlagsAdd + * can be set only as an output in the callback, whereas the kDNSServiceFlagsIncludeP2P + * can be set only as an input to the DNSService*() APIs. See comments on kDNSServiceFlagsValidate + * defined in enum below. */ enum { @@ -231,14 +237,13 @@ enum * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS. */ - kDNSServiceFlagsForce = 0x800, - /* Flag for signifying a "stronger" variant of an operation. - * Currently defined only for DNSServiceReconfirmRecord(), where it forces a record to - * be removed from the cache immediately, instead of querying for a few seconds before - * concluding that the record is no longer valid and then removing it. This flag should - * be used with caution because if a service browsing PTR record is indeed still valid - * on the network, forcing its removal will result in a user-interface flap -- the - * discovered service instance will disappear, and then re-appear moments later. + kDNSServiceFlagsForce = 0x800, // This flag is deprecated. + + kDNSServiceFlagsKnownUnique = 0x800, + /* + * Client guarantees that record names are unique, so we can skip sending out initial + * probe messages. Standard name conflict resolution is still done if a conflict is discovered. + * Currently only valid for a DNSServiceRegister call. */ kDNSServiceFlagsReturnIntermediates = 0x1000, @@ -380,19 +385,130 @@ enum * class for packets that service the request. */ - kDNSServiceFlagsIncludeAWDL = 0x100000 - /* - * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified. + kDNSServiceFlagsIncludeAWDL = 0x100000, + /* + * Include AWDL interface when kDNSServiceInterfaceIndexAny is specified. + */ + + kDNSServiceFlagsValidate = 0x200000, + /* + * This flag is meaningful in DNSServiceGetAddrInfo and DNSServiceQueryRecord. This is the ONLY flag to be valid + * as an input to the APIs and also an output through the callbacks in the APIs. + * + * When this flag is passed to DNSServiceQueryRecord and DNSServiceGetAddrInfo to resolve unicast names, + * the response will be validated using DNSSEC. The validation results are delivered using the flags field in + * the callback and kDNSServiceFlagsValidate is marked in the flags to indicate that DNSSEC status is also available. + * When the callback is called to deliver the query results, the validation results may or may not be available. + * If it is not delivered along with the results, the validation status is delivered when the validation completes. + * + * When the validation results are delivered in the callback, it is indicated by marking the flags with + * kDNSServiceFlagsValidate and kDNSServiceFlagsAdd along with the DNSSEC status flags (described below) and a NULL + * sockaddr will be returned for DNSServiceGetAddrInfo and zero length rdata will be returned for DNSServiceQueryRecord. + * DNSSEC validation results are for the whole RRSet and not just individual records delivered in the callback. When + * kDNSServiceFlagsAdd is not set in the flags, applications should implicitly assume that the DNSSEC status of the + * RRSet that has been delivered up until that point is not valid anymore, till another callback is called with + * kDNSServiceFlagsAdd and kDNSServiceFlagsValidate. + * + * The following four flags indicate the status of the DNSSEC validation and marked in the flags field of the callback. + * When any of the four flags is set, kDNSServiceFlagsValidate will also be set. To check the validation status, the + * other applicable output flags should be masked. See kDNSServiceOutputFlags below. + */ + + kDNSServiceFlagsSecure = 0x200010, + /* + * The response has been validated by verifying all the signaures in the response and was able to + * build a successful authentication chain starting from a known trust anchor. + */ + + kDNSServiceFlagsInsecure = 0x200020, + /* + * A chain of trust cannot be built starting from a known trust anchor to the response. + */ + + kDNSServiceFlagsBogus = 0x200040, + /* + * If the response cannot be verified to be secure due to expired signatures, missing signatures etc., + * then the results are considered to be bogus. + */ + + kDNSServiceFlagsIndeterminate = 0x200080, + /* + * There is no valid trust anchor that can be used to determine whether a response is secure or not. + */ + + kDNSServiceFlagsUnicastResponse = 0x400000, + /* + * Request unicast response to query. + */ + kDNSServiceFlagsValidateOptional = 0x800000, + + /* + * This flag is identical to kDNSServiceFlagsValidate except for the case where the response + * cannot be validated. If this flag is set in DNSServiceQueryRecord or DNSServiceGetAddrInfo, + * the DNSSEC records will be requested for validation. If they cannot be received for some reason + * during the validation (e.g., zone is not signed, zone is signed but cannot be traced back to + * root, recursive server does not understand DNSSEC etc.), then this will fallback to the default + * behavior where the validation will not be performed and no DNSSEC results will be provided. + * + * If the zone is signed and there is a valid path to a known trust anchor configured in the system + * and the application requires DNSSEC validation irrespective of the DNSSEC awareness in the current + * network, then this option MUST not be used. This is only intended to be used during the transition + * period where the different nodes participating in the DNS resolution may not understand DNSSEC or + * managed properly (e.g. missing DS record) but still want to be able to resolve DNS successfully. + */ + + kDNSServiceFlagsWakeOnlyService = 0x1000000, + /* + * This flag is meaningful only in DNSServiceRegister. When set, the service will not be registered + * with sleep proxy server during sleep. + */ + + kDNSServiceFlagsThresholdOne = 0x2000000, + kDNSServiceFlagsThresholdFinder = 0x4000000, + kDNSServiceFlagsThresholdReached = kDNSServiceFlagsThresholdOne, + /* + * kDNSServiceFlagsThresholdOne is meaningful only in DNSServiceBrowse. When set, + * the system will stop issuing browse queries on the network once the number + * of answers returned is one or more. It will issue queries on the network + * again if the number of answers drops to zero. + * This flag is for Apple internal use only. Third party developers + * should not rely on this behavior being supported in any given software release. + * + * kDNSServiceFlagsThresholdFinder is meaningful only in DNSServiceBrowse. When set, + * the system will stop issuing browse queries on the network once the number + * of answers has reached the threshold set for Finder. + * It will issue queries on the network again if the number of answers drops below + * this threshold. + * This flag is for Apple internal use only. Third party developers + * should not rely on this behavior being supported in any given software release. + * + * When kDNSServiceFlagsThresholdReached is set in the client callback add or remove event, + * it indicates that the browse answer threshold has been reached and no + * browse requests will be generated on the network until the number of answers falls + * below the threshold value. Add and remove events can still occur based + * on incoming Bonjour traffic observed by the system. + * The set of services return to the client is not guaranteed to represent the + * entire set of services present on the network once the threshold has been reached. + * + * Note, while kDNSServiceFlagsThresholdReached and kDNSServiceFlagsThresholdOne + * have the same value, there isn't a conflict because kDNSServiceFlagsThresholdReached + * is only set in the callbacks and kDNSServiceFlagsThresholdOne is only set on + * input to a DNSServiceBrowse call. */ }; -/* Possible protocols for DNSServiceNATPortMappingCreate(). */ +#define kDNSServiceOutputFlags (kDNSServiceFlagsValidate | kDNSServiceFlagsValidateOptional | kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault) + /* All the output flags excluding the DNSSEC Status flags. Typically used to check DNSSEC Status */ + +/* Possible protocol values */ enum { + /* for DNSServiceGetAddrInfo() */ kDNSServiceProtocol_IPv4 = 0x01, kDNSServiceProtocol_IPv6 = 0x02, /* 0x04 and 0x08 reserved for future internetwork protocols */ + /* for DNSServiceNATPortMappingCreate() */ kDNSServiceProtocol_UDP = 0x10, kDNSServiceProtocol_TCP = 0x20 /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960] @@ -518,8 +634,8 @@ enum kDNSServiceErr_BadKey = -65561, kDNSServiceErr_Transient = -65562, kDNSServiceErr_ServiceNotRunning = -65563, /* Background daemon not running */ - kDNSServiceErr_NATPortMappingUnsupported = -65564, /* NAT doesn't support NAT-PMP or UPnP */ - kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports NAT-PMP or UPnP but it's disabled by the administrator */ + kDNSServiceErr_NATPortMappingUnsupported = -65564, /* NAT doesn't support PCP, NAT-PMP or UPnP */ + kDNSServiceErr_NATPortMappingDisabled = -65565, /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */ kDNSServiceErr_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */ kDNSServiceErr_PollingMode = -65567, kDNSServiceErr_Timeout = -65568 @@ -620,12 +736,16 @@ enum * to their DNSServiceBrowseReply() callback function, and discarding those * where the interface index is not kDNSServiceInterfaceIndexLocalOnly. * - * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, + * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord, Register, * and Resolve operations. It should not be used in other DNSService APIs. * * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or * DNSServiceQueryRecord, it restricts the operation to P2P. * + * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceRegister, it is + * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P + * set. + * * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is * mapped internally to kDNSServiceInterfaceIndexAny with the kDNSServiceFlagsIncludeP2P * set, because resolving a P2P service may create and/or enable an interface whose @@ -705,6 +825,28 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty #define kDNSServiceProperty_DaemonVersion "DaemonVersion" +// Map the source port of the local UDP socket that was opened for sending the DNS query +// to the process ID of the application that triggered the DNS resolution. +// +/* DNSServiceGetPID() Parameters: + * + * srcport: Source port (in network byte order) of the UDP socket that was created by + * mDNSResponder to send the DNS query on the wire. + * + * pid: Process ID of the application that started the name resolution which triggered + * mDNSResponder to send the query on the wire. The value can be -1 if the srcport + * cannot be mapped. + * + * return value: Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning + * if the daemon is not running. The value of the pid is undefined if the return + * value has error. + */ +DNSServiceErrorType DNSSD_API DNSServiceGetPID +( + uint16_t srcport, + int32_t *pid +); + /********************************************************************************************* * * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions @@ -777,9 +919,7 @@ DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef); * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent * functions. * - * Note: This call is to be used only with the DNSServiceRef defined by this API. It is - * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based - * DNSServiceDiscovery.h API. + * Note: This call is to be used only with the DNSServiceRef defined by this API. * * sdRef: A DNSServiceRef initialized by any of the DNSService calls. * @@ -990,6 +1130,31 @@ typedef void (DNSSD_API *DNSServiceRegisterReply) * * % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123 * + * When a service is registered, all the clients browsing for the registered + * type ("regtype") will discover it. If the discovery should be + * restricted to a smaller set of well known peers, the service can be + * registered with additional data (group identifier) that is known + * only to a smaller set of peers. The group identifier should follow primary + * service type using a colon (":") as a delimeter. If subtypes are also present, + * it should be given before the subtype as shown below. + * + * % dns-sd -R _test1 _http._tcp:mygroup1 local 1001 + * % dns-sd -R _test2 _http._tcp:mygroup2 local 1001 + * % dns-sd -R _test3 _http._tcp:mygroup3,HasFeatureA local 1001 + * + * Now: + * % dns-sd -B _http._tcp:"mygroup1" # will discover only test1 + * % dns-sd -B _http._tcp:"mygroup2" # will discover only test2 + * % dns-sd -B _http._tcp:"mygroup3",HasFeatureA # will discover only test3 + * + * By specifying the group information, only the members of that group are + * discovered. + * + * The group identifier itself is not sent in clear. Only a hash of the group + * identifier is sent and the clients discover them anonymously. The group identifier + * may be up to 256 bytes long and may contain any eight bit values except comma which + * should be escaped. + * * domain: If non-NULL, specifies the domain on which to advertise the service. * Most applications will not specify a domain, instead automatically * registering in the default domain(s). @@ -1246,7 +1411,10 @@ typedef void (DNSSD_API *DNSServiceBrowseReply) * A client may optionally specify a single subtype to perform filtered browsing: * e.g. browsing for "_primarytype._tcp,_subtype" will discover only those * instances of "_primarytype._tcp" that were registered specifying "_subtype" - * in their list of registered subtypes. + * in their list of registered subtypes. Additionally, a group identifier may + * also be specified before the subtype e.g., _primarytype._tcp:GroupID, which + * will discover only the members that register the service with GroupID. See + * DNSServiceRegister for more details. * * domain: If non-NULL, specifies the domain on which to browse for services. * Most applications will not specify a domain, instead browsing on the @@ -1650,7 +1818,6 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef); - /* DNSServiceRegisterRecord * * Register an individual resource record on a connected DNSServiceRef. @@ -1759,8 +1926,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord * * Parameters: * - * flags: Pass kDNSServiceFlagsForce to force immediate deletion of record, - * instead of after some number of reconfirmation queries have gone unanswered. + * flags: Not currently used. * * interfaceIndex: Specifies the interface of the record in question. * The caller must specify the interface. @@ -1802,8 +1968,12 @@ DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord /* DNSServiceNATPortMappingCreate * * Request a port mapping in the NAT gateway, which maps a port on the local machine - * to an external port on the NAT. The NAT should support either the NAT-PMP or the UPnP IGD - * protocol for this API to create a successful mapping. + * to an external port on the NAT. The NAT should support either PCP, NAT-PMP or the + * UPnP/IGD protocol for this API to create a successful mapping. Note that this API + * currently supports IPv4 addresses/mappings only. If the NAT gateway supports PCP and + * returns an IPv6 address (incorrectly, since this API specifically requests IPv4 + * addresses), the DNSServiceNATPortMappingReply callback will be invoked with errorCode + * kDNSServiceErr_NATPortMappingUnsupported. * * The port mapping will be renewed indefinitely until the client process exits, or * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate(). @@ -2433,6 +2603,34 @@ DNSServiceErrorType DNSSD_API DNSServiceSleepKeepalive ); #endif +#ifdef APPLE_OSX_mDNSResponder +/* DNSServiceCreateDelegateConnection() + * + * Create a delegate connection to the daemon allowing efficient registration of + * multiple individual records. + * + * Parameters: + * + * sdRef: A pointer to an uninitialized DNSServiceRef. Deallocating + * the reference (via DNSServiceRefDeallocate()) severs the + * connection and deregisters all records registered on this connection. + * + * pid : Process ID of the delegate + * + * uuid: UUID of the delegate + * + * Note that only one of the two arguments (pid or uuid) can be specified. If pid + * is zero, uuid will be assumed to be a valid value; otherwise pid will be used. + * + * return value: Returns kDNSServiceErr_NoError on success, otherwise returns + * an error code indicating the specific failure that occurred (in which + * case the DNSServiceRef is not initialized). kDNSServiceErr_NotAuth is + * returned to indicate that the calling process does not have entitlements + * to use this API. + */ +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid); +#endif + #ifdef __APPLE_API_PRIVATE #define kDNSServiceCompPrivateDNS "PrivateDNS" diff --git a/mDNSShared/dnsextd.c b/mDNSShared/dnsextd.c index a5ee500..aa06650 100644 --- a/mDNSShared/dnsextd.c +++ b/mDNSShared/dnsextd.c @@ -3104,8 +3104,10 @@ void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID iid) { ( void ) m; ( void ) msg; ( void ) end; ( void ) srcaddr; ( void ) srcport; ( void ) dstaddr; ( void ) dstport; ( void ) iid; } -DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID) -{ ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; (void) cellIntf; (void) resGroupID; return(NULL); } +DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const int serviceID, const mDNSAddr *addr, const mDNSIPPort port, + mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA, mDNSBool reqAAAA, mDNSBool reqDO) +{ ( void ) m; ( void ) d; ( void ) interface; ( void ) serviceID; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; (void) cellIntf; + (void) resGroupID; (void) reqA; (void) reqAAAA; (void) reqDO; return(NULL); } void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) { (void)domain; (void) InterfaceID;} void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext) { ( void ) m; ( void ) fqdn; ( void ) StatusCallback; ( void ) StatusContext; } @@ -3132,6 +3134,8 @@ mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; } void TriggerEventCompletion(void); void TriggerEventCompletion() {} +int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); +int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q) { ( void ) rr; ( void ) q; return 1;} mDNS mDNSStorage; diff --git a/mDNSShared/dnssd_clientshim.c b/mDNSShared/dnssd_clientshim.c index 0815b8d..cb14310 100644 --- a/mDNSShared/dnssd_clientshim.c +++ b/mDNSShared/dnssd_clientshim.c @@ -412,7 +412,7 @@ DNSServiceErrorType DNSServiceBrowse x->q.QuestionContext = x; // Do the operation - err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, flags, (flags & kDNSServiceFlagsForceMulticast) != 0, (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, x); + err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSNULL, mDNSInterface_Any, flags, (flags & kDNSServiceFlagsForceMulticast) != 0, (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, x); if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; } // Succeeded: Wrap up and return @@ -516,10 +516,13 @@ DNSServiceErrorType DNSServiceResolve x->qSRV.RetryWithSearchDomains = mDNSfalse; x->qSRV.TimeoutQuestion = 0; x->qSRV.WakeOnResolve = 0; - x->qSRV.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + x->qSRV.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; x->qSRV.ValidationRequired = 0; x->qSRV.ValidatingResponse = 0; + x->qSRV.ProxyQuestion = 0; x->qSRV.qnameOrig = mDNSNULL; + x->qSRV.AnonInfo = mDNSNULL; + x->qSRV.pid = mDNSPlatformGetPID(); x->qSRV.QuestionCallback = FoundServiceInfo; x->qSRV.QuestionContext = x; @@ -540,10 +543,13 @@ DNSServiceErrorType DNSServiceResolve x->qTXT.RetryWithSearchDomains = mDNSfalse; x->qTXT.TimeoutQuestion = 0; x->qTXT.WakeOnResolve = 0; - x->qTXT.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + x->qTXT.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; x->qTXT.ValidationRequired = 0; x->qTXT.ValidatingResponse = 0; + x->qTXT.ProxyQuestion = 0; x->qTXT.qnameOrig = mDNSNULL; + x->qTXT.AnonInfo = mDNSNULL; + x->qTXT.pid = mDNSPlatformGetPID(); x->qTXT.QuestionCallback = FoundServiceInfo; x->qTXT.QuestionContext = x; @@ -672,9 +678,15 @@ DNSServiceErrorType DNSServiceQueryRecord x->q.SearchListIndex = 0; x->q.AppendSearchDomains = 0; x->q.RetryWithSearchDomains = mDNSfalse; + x->q.TimeoutQuestion = 0; x->q.WakeOnResolve = 0; - x->q.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + x->q.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + x->q.ValidationRequired = 0; + x->q.ValidatingResponse = 0; + x->q.ProxyQuestion = 0; x->q.qnameOrig = mDNSNULL; + x->q.AnonInfo = mDNSNULL; + x->q.pid = mDNSPlatformGetPID(); x->q.QuestionCallback = DNSServiceQueryRecordResponse; x->q.QuestionContext = x; diff --git a/mDNSShared/dnssd_clientstub.c b/mDNSShared/dnssd_clientstub.c index f5e382d..d21658f 100644 --- a/mDNSShared/dnssd_clientstub.c +++ b/mDNSShared/dnssd_clientstub.c @@ -31,6 +31,8 @@ #if APPLE_OSX_mDNSResponder #include +#include +#include #endif #include "dnssd_ipc.h" @@ -96,7 +98,11 @@ static void syslog( int priority, const char * message, ...) // Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp) //#define USE_NAMED_ERROR_RETURN_SOCKET 1 -#define DNSSD_CLIENT_TIMEOUT 10 // In seconds +// If the UDS client has not received a response from the daemon in 60 secs, it is unlikely to get one +// Note: Timeout of 3 secs should be sufficient in normal scenarios, but 60 secs is chosen as a safeguard since +// some clients may come up before mDNSResponder itself after a BOOT and on rare ocassions IOPM/Keychain/D2D calls +// in mDNSResponder's INIT may take a much longer time to return +#define DNSSD_CLIENT_TIMEOUT 60 #ifndef CTL_PATH_PREFIX #define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket." @@ -218,7 +224,11 @@ static int read_all(dnssd_sock_t sd, char *buf, int len) ssize_t num_read = recv(sd, buf, len, 0); // It is valid to get an interrupted system call error e.g., somebody attaching // in a debugger, retry without failing - if ((num_read < 0) && (errno == EINTR)) { syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); continue; } + if ((num_read < 0) && (errno == EINTR)) + { + syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); + continue; + } if ((num_read == 0) || (num_read < 0) || (num_read > len)) { int printWarn = 0; @@ -277,29 +287,29 @@ static int more_bytes(dnssd_sock_t sd) int nfdbits = sizeof (int) * 8; int nints = (sd/nfdbits) + 1; fs = (fd_set *)calloc(nints, sizeof(int)); - if (fs == NULL) { syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); return 0; } + if (fs == NULL) + { + syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); + return 0; + } } FD_SET(sd, fs); ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv); - if (fs != &readfds) free(fs); + if (fs != &readfds) + free(fs); return (ret > 0); } -// Wait for daemon to write to socket -static int wait_for_daemon(dnssd_sock_t sock, int timeout) +// set_waitlimit() implements a timeout using select. It is called from deliver_request() before recv() OR accept() +// to ensure the UDS clients are not blocked in these system calls indefinitely. +// Note: Ideally one should never be blocked here, because it indicates either mDNSResponder daemon is not yet up/hung/ +// superbusy/crashed or some other OS bug. For eg: On Windows which suffers from 3rd party software +// (primarily 3rd party firewall software) interfering with proper functioning of the TCP protocol stack it is possible +// the next operation on this socket(recv/accept) is blocked since we depend on TCP to communicate with the system service. +static int set_waitlimit(dnssd_sock_t sock, int timeout) { -#ifndef WIN32 - // At this point the next operation (accept() or read()) on this socket may block for a few milliseconds waiting - // for the daemon to respond, but that's okay -- the daemon is a trusted service and we know if won't take more - // than a few milliseconds to respond. So we'll forego checking for readability of the socket. - (void) sock; - (void) timeout; -#else - // Windows on the other hand suffers from 3rd party software (primarily 3rd party firewall software) that - // interferes with proper functioning of the TCP protocol stack. Because of this and because we depend on TCP - // to communicate with the system service, we want to make sure that the next operation on this socket (accept() or - // read()) doesn't block indefinitely. - if (!gDaemonErr) + // To prevent stack corruption since select does not work with timeout if fds > FD_SETSIZE(1024) + if (!gDaemonErr && sock < FD_SETSIZE) { struct timeval tv; fd_set set; @@ -310,11 +320,11 @@ static int wait_for_daemon(dnssd_sock_t sock, int timeout) tv.tv_usec = 0; if (!select((int)(sock + 1), &set, NULL, NULL, &tv)) { - syslog(LOG_WARNING, "dnssd_clientstub wait_for_daemon timed out"); + // Ideally one should never hit this case: See comments before set_waitlimit() + syslog(LOG_WARNING, "dnssd_clientstub set_waitlimit:_daemon timed out (%d secs) without any response: Socket %d", timeout, sock); gDaemonErr = kDNSServiceErr_Timeout; } } -#endif return gDaemonErr; } @@ -430,16 +440,16 @@ static void FreeDNSServiceOp(DNSServiceOp *x) // Return a connected service ref (deallocate with DNSServiceRefDeallocate) static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext) { - #if APPLE_OSX_mDNSResponder - int NumTries = DNSSD_CLIENT_MAXTRIES; - #else int NumTries = 0; - #endif dnssd_sockaddr_t saddr; DNSServiceOp *sdr; - if (!ref) { syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } + if (!ref) + { + syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef"); + return kDNSServiceErr_BadParam; + } if (flags & kDNSServiceFlagsShareConnection) { @@ -448,10 +458,10 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; } - if (!DNSServiceRefValid(*ref) || (*ref)->op != connection_request || (*ref)->primary) + if (!DNSServiceRefValid(*ref) || ((*ref)->op != connection_request && (*ref)->op != connection_delegate_request) || (*ref)->primary) { - syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X", - (*ref), (*ref)->sockfd, (*ref)->validator); + syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X op %d", + (*ref), (*ref)->sockfd, (*ref)->validator, (*ref)->op); *ref = NULL; return kDNSServiceErr_BadReference; } @@ -465,11 +475,17 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; } } // If the system service is disabled, we only want to try to connect once - if (IsSystemServiceDisabled()) NumTries = DNSSD_CLIENT_MAXTRIES; + if (IsSystemServiceDisabled()) + NumTries = DNSSD_CLIENT_MAXTRIES; #endif sdr = malloc(sizeof(DNSServiceOp)); - if (!sdr) { syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed"); *ref = NULL; return kDNSServiceErr_NoMemory; } + if (!sdr) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed"); + *ref = NULL; + return kDNSServiceErr_NoMemory; + } sdr->next = NULL; sdr->primary = NULL; sdr->sockfd = dnssd_InvalidSocket; @@ -493,10 +509,12 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f if (flags & kDNSServiceFlagsShareConnection) { DNSServiceOp **p = &(*ref)->next; // Append ourselves to end of primary's list - while (*p) p = &(*p)->next; + while (*p) + p = &(*p)->next; *p = sdr; // Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear - if (++(*ref)->uid.u32[0] == 0) ++(*ref)->uid.u32[1]; // In parent DNSServiceOp increment UID counter + if (++(*ref)->uid.u32[0] == 0) + ++(*ref)->uid.u32[1]; // In parent DNSServiceOp increment UID counter sdr->primary = *ref; // Set our primary pointer sdr->sockfd = (*ref)->sockfd; // Inherit primary's socket sdr->validator = (*ref)->validator; @@ -537,18 +555,30 @@ static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags f } #endif #endif - + while (1) { int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); - if (!err) break; // If we succeeded, return sdr + if (!err) + break; // If we succeeded, return sdr // If we failed, then it may be because the daemon is still launching. // This can happen for processes that launch early in the boot process, while the - // daemon is still coming up. Rather than fail here, we'll wait a bit and try again. - // If, after four seconds, we still can't connect to the daemon, + // daemon is still coming up. Rather than fail here, we wait 1 sec and try again. + // If, after DNSSD_CLIENT_MAXTRIES, we still can't connect to the daemon, // then we give up and return a failure code. - if (++NumTries < DNSSD_CLIENT_MAXTRIES) sleep(1); // Sleep a bit, then try again - else { dnssd_close(sdr->sockfd); FreeDNSServiceOp(sdr); return kDNSServiceErr_ServiceNotRunning; } + if (++NumTries < DNSSD_CLIENT_MAXTRIES) + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect()-> No of tries: %d", NumTries); + sleep(1); // Sleep a bit, then try again + } + else + { + syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: connect() failed Socket:%d Err:%d Errno:%d %s", + sdr->sockfd, err, dnssd_errno, dnssd_strerror(dnssd_errno)); + dnssd_close(sdr->sockfd); + FreeDNSServiceOp(sdr); + return kDNSServiceErr_ServiceNotRunning; + } } //printf("ConnectToServer opened socket %d\n", sdr->sockfd); } @@ -580,11 +610,17 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) if (!DNSServiceRefValid(sdr)) { + if (hdr) + free(hdr); syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator); return kDNSServiceErr_BadReference; } - if (!hdr) { syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); return kDNSServiceErr_Unknown; } + if (!hdr) + { + syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); + return kDNSServiceErr_Unknown; + } if (MakeSeparateReturnSocket) { @@ -654,7 +690,8 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) // any associated data does not work reliably -- e.g. one particular issue we ran // into is that if the receiving program is in a kqueue loop waiting to be notified // of the received message, it doesn't get woken up when the control message arrives. - if (MakeSeparateReturnSocket || sdr->op == send_bpf) datalen--; // Okay to use sdr->op when checking for op == send_bpf + if (MakeSeparateReturnSocket || sdr->op == send_bpf) + datalen--; // Okay to use sdr->op when checking for op == send_bpf #endif // At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to @@ -682,51 +719,58 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) } #endif - if (!MakeSeparateReturnSocket) errsd = sdr->sockfd; + if (!MakeSeparateReturnSocket) + errsd = sdr->sockfd; if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf { #if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET) - // At this point we may block in accept for a few milliseconds waiting for the daemon to connect back to us, - // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond. + // At this point we may wait in accept for a few milliseconds waiting for the daemon to connect back to us, + // but that's okay -- the daemon should not take more than a few milliseconds to respond. + // set_waitlimit() ensures we do not block indefinitely just in case something is wrong dnssd_sockaddr_t daddr; dnssd_socklen_t len = sizeof(daddr); - if ((err = wait_for_daemon(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) goto cleanup; + if ((err = set_waitlimit(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) + goto cleanup; errsd = accept(listenfd, (struct sockaddr *)&daddr, &len); - if (!dnssd_SocketValid(errsd)) deliver_request_bailout("accept"); + if (!dnssd_SocketValid(errsd)) + deliver_request_bailout("accept"); #else struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS struct msghdr msg; struct cmsghdr *cmsg; - char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))]; - - if (sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf - { - int i; - char p[12]; // Room for "/dev/bpf999" with terminating null - for (i=0; i<100; i++) - { - snprintf(p, sizeof(p), "/dev/bpf%d", i); - listenfd = open(p, O_RDWR, 0); - //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p); - if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY) - syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno)); - if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break; - } - } + char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))]; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &vec; msg.msg_iovlen = 1; - msg.msg_control = cbuf; - msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t)); msg.msg_flags = 0; - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof(dnssd_sock_t)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd; + if (MakeSeparateReturnSocket || sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf + { + if (sdr->op == send_bpf) + { + int i; + char p[12]; // Room for "/dev/bpf999" with terminating null + for (i=0; i<100; i++) + { + snprintf(p, sizeof(p), "/dev/bpf%d", i); + listenfd = open(p, O_RDWR, 0); + //if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p); + if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY) + syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno)); + if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break; + } + } + msg.msg_control = cbuf; + msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(dnssd_sock_t)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd; + } #if TEST_KQUEUE_CONTROL_MESSAGE_BUG sleep(1); @@ -753,25 +797,27 @@ static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr) #endif // DEBUG_64BIT_SCM_RIGHTS #endif - // Close our end of the socketpair *before* blocking in read_all to get the four-byte error code. - // Otherwise, if the daemon closes our socket (or crashes), we block in read_all() forever - // because the socket is not closed (we still have an open reference to it ourselves). + // Close our end of the socketpair *before* calling read_all() to get the four-byte error code. + // Otherwise, if the daemon closes our socket (or crashes), we will have to wait for a timeout + // in read_all() because the socket is not closed (we still have an open reference to it) + // Note: listenfd is overwritten in the case of send_bpf above and that will be closed here + // for send_bpf operation. dnssd_close(listenfd); - listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below + listenfd = dnssd_InvalidSocket; // Make sure we don't close it a second time in the cleanup handling below } - // At this point we may block in read_all for a few milliseconds waiting for the daemon to send us the error code, - // but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond. + // At this point we may wait in read_all for a few milliseconds waiting for the daemon to send us the error code, + // but that's okay -- the daemon should not take more than a few milliseconds to respond. + // set_waitlimit() ensures we do not block indefinitely just in case something is wrong if (sdr->op == send_bpf) // Okay to use sdr->op when checking for op == send_bpf err = kDNSServiceErr_NoError; - else if ((err = wait_for_daemon(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError) + else if ((err = set_waitlimit(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError) { if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0) err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us else err = ntohl(err); } - //syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err); cleanup: @@ -847,6 +893,7 @@ static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error) if (sdr->AppCallback) ((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL, sdr->AppContext); break; case connection_request: + case connection_delegate_request: // This means Register Record, walk the list of DNSRecords to do the callback rec = sdr->rec; while (rec) @@ -1121,6 +1168,37 @@ DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void * return kDNSServiceErr_NoError; } +DNSServiceErrorType DNSSD_API DNSServiceGetPID(const uint16_t srcport, int32_t *pid) +{ + char *ptr; + ipc_msg_hdr *hdr; + DNSServiceOp *tmp; + size_t len = sizeof(int32_t); + + DNSServiceErrorType err = ConnectToServer(&tmp, 0, getpid_request, NULL, NULL, NULL); + if (err) + return err; + + hdr = create_hdr(getpid_request, &len, &ptr, 0, tmp); + if (!hdr) + { + DNSServiceRefDeallocate(tmp); + return kDNSServiceErr_NoMemory; + } + + put_uint16(srcport, &ptr); + err = deliver_request(hdr, tmp); // Will free hdr for us + + if (read_all(tmp->sockfd, (char*)pid, sizeof(int32_t)) < 0) + { + DNSServiceRefDeallocate(tmp); + return kDNSServiceErr_ServiceNotRunning; + } + + DNSServiceRefDeallocate(tmp); + return kDNSServiceErr_NoError; +} + static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end) { char fullname[kDNSServiceMaxDomainName]; @@ -1344,7 +1422,16 @@ static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHead if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface; } } - ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext); + // Validation results are always delivered separately from the actual results of the + // DNSServiceGetAddrInfo. Set the "addr" to NULL as per the documentation. + // + // Note: If we deliver validation results along with the "addr" in the future, we need + // a way to differentiate the negative response from validation-only response as both + // has zero address. + if (!(cbh->cb_flags & kDNSServiceFlagsValidate)) + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext); + else + ((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, NULL, 0, sdr->AppContext); // MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function } } @@ -1368,7 +1455,10 @@ DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo if (!hostname) return kDNSServiceErr_BadParam; err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, callBack, context); - if (err) return err; // On error ConnectToServer leaves *sdRef set to NULL + if (err) + { + return err; // On error ConnectToServer leaves *sdRef set to NULL + } // Calculate total message length len = sizeof(flags); @@ -1625,7 +1715,7 @@ static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *co return; } - if (sdr->op == connection_request) + if (sdr->op == connection_request || sdr->op == connection_delegate_request) { rec->AppCallback(rec->sdr, rec, cbh->cb_flags, cbh->cb_err, rec->AppContext); } @@ -1654,6 +1744,72 @@ DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef) return err; } +#if APPLE_OSX_mDNSResponder && !TARGET_IPHONE_SIMULATOR +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) +{ + char *ptr; + size_t len = 0; + ipc_msg_hdr *hdr; + + DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_delegate_request, ConnectionResponse, NULL, NULL); + if (err) + { + return err; // On error ConnectToServer leaves *sdRef set to NULL + } + + // Only one of the two options can be set. If pid is zero, uuid is used. + // If both are specified only pid will be used. We send across the pid + // so that the daemon knows what to read from the socket. + + len += sizeof(int32_t); + + hdr = create_hdr(connection_delegate_request, &len, &ptr, 0, *sdRef); + if (!hdr) + { + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoMemory; + } + + if (pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED, &pid, sizeof(pid)) == -1) + { + // Free the hdr in case we return before calling deliver_request() + if (hdr) + free(hdr); + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoAuth; + } + + if (!pid && setsockopt((*sdRef)->sockfd, SOL_SOCKET, SO_DELEGATED_UUID, uuid, sizeof(uuid_t)) == -1) + { + // Free the hdr in case we return before calling deliver_request() + if (hdr) + free(hdr); + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + return kDNSServiceErr_NoAuth; + } + + put_uint32(pid, &ptr); + + err = deliver_request(hdr, *sdRef); // Will free hdr for us + if (err) + { + DNSServiceRefDeallocate(*sdRef); + *sdRef = NULL; + } + return err; +} +#elif TARGET_IPHONE_SIMULATOR // This hack is for Simulator platform only +DNSServiceErrorType DNSSD_API DNSServiceCreateDelegateConnection(DNSServiceRef *sdRef, int32_t pid, uuid_t uuid) +{ + (void) pid; + (void) uuid; + return DNSServiceCreateConnection(sdRef); +} +#endif + DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord ( DNSServiceRef sdRef, @@ -1690,7 +1846,7 @@ DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord return kDNSServiceErr_BadReference; } - if (sdRef->op != connection_request) + if (sdRef->op != connection_request && sdRef->op != connection_delegate_request) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op); return kDNSServiceErr_BadReference; diff --git a/mDNSShared/dnssd_ipc.h b/mDNSShared/dnssd_ipc.h index bb3b0df..360d703 100644 --- a/mDNSShared/dnssd_ipc.h +++ b/mDNSShared/dnssd_ipc.h @@ -135,7 +135,9 @@ typedef enum port_mapping_request, // New in Leopard and B4W 2.0 addrinfo_request, send_bpf, // New in SL + getpid_request, release_request, + connection_delegate_request, cancel_request = 63 } request_op_t; diff --git a/mDNSShared/mDNSDebug.c b/mDNSShared/mDNSDebug.c index 2af79e3..cb4da6e 100644 --- a/mDNSShared/mDNSDebug.c +++ b/mDNSShared/mDNSDebug.c @@ -37,8 +37,10 @@ #include "mDNSEmbeddedAPI.h" -mDNSexport int mDNS_LoggingEnabled = 0; +mDNSexport int mDNS_LoggingEnabled = 0; mDNSexport int mDNS_PacketLoggingEnabled = 0; +mDNSexport int mDNS_McastLoggingEnabled = 0; +mDNSexport int mDNS_McastTracingEnabled = 0; #if MDNS_DEBUGMSGS mDNSexport int mDNS_DebugMode = mDNStrue; diff --git a/mDNSShared/uds_daemon.c b/mDNSShared/uds_daemon.c index 22b4159..e9b3a27 100644 --- a/mDNSShared/uds_daemon.c +++ b/mDNSShared/uds_daemon.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2003-2012 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2003-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,6 +54,8 @@ mDNSBool AlwaysAppendSearchDomains = mDNSfalse; #include // for struct proc_bsdshortinfo #include // for proc_pidinfo() #endif //LOCAL_PEERPID +//upto 16 characters of process name (defined in but we do not want to include that file) +#define MAXCOMLEN 16 #if APPLE_OSX_mDNSResponder #include @@ -76,6 +78,8 @@ int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid // User IDs for real user accounts start at 501 and count up from there #define SystemUID(X) ((X) <= 500) +#define MAX_ANONYMOUS_DATA 256 + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -130,12 +134,21 @@ typedef struct browser_t DNSQuestion q; } browser_t; +#ifdef _WIN32 + typedef unsigned int pid_t; + typedef unsigned int socklen_t; +#endif + struct request_state { request_state *next; request_state *primary; // If this operation is on a shared socket, pointer to primary // request_state for the original DNSServiceCreateConnection() operation dnssd_sock_t sd; + pid_t process_id; // Client's PID value + char pid_name[MAXCOMLEN]; // Client's process name + char uuid[UUID_SIZE]; + mDNSBool validUUID; dnssd_sock_t errsd; mDNSu32 uid; void * platform_data; @@ -169,6 +182,7 @@ struct request_state mDNSBool ForceMCast; domainname regtype; browser_t *browsers; + const mDNSu8 *AnonData; } browser; struct { @@ -185,6 +199,7 @@ struct request_state mDNSBool autorename; // Set if this client wants us to automatically rename on conflict mDNSBool allowremotequery; // Respond to unicast queries from outside the local link? int num_subtypes; + mDNSBool AnonData; service_instance *instances; } servicereg; struct @@ -193,9 +208,11 @@ struct request_state mDNSu32 flags; mDNSu32 protocol; DNSQuestion q4; - DNSQuestion *q42; + DNSQuestion *q42; DNSQuestion q6; - DNSQuestion *q62; + DNSQuestion *q62; + mDNSu8 v4ans; + mDNSu8 v6ans; } addrinfo; struct { @@ -204,9 +221,7 @@ struct request_state } pm; struct { -#if 0 DNSServiceFlags flags; -#endif DNSQuestion q_all; DNSQuestion q_default; } enumeration; @@ -214,6 +229,7 @@ struct request_state { DNSQuestion q; DNSQuestion *q2; + mDNSu8 ans; } queryrecord; struct { @@ -259,9 +275,13 @@ static request_state *all_requests = NULL; #ifdef LOCAL_PEERPID struct proc_bsdshortinfo proc; #endif //LOCAL_PEERPID -//upto 16 characters of process name (defined in but we do not want to include that file) -#define MAXCOMLEN 16 -char pid_name[MAXCOMLEN]; +mDNSlocal void set_peer_pid(request_state *request); +mDNSlocal void LogMcastClientInfo(request_state *req); +mDNSlocal void GetMcastClients(request_state *req); +static mDNSu32 mcount; // tracks the current active mcast operations for McastLogging +static mDNSu32 i_mcount; // sets mcount when McastLogging is enabled(PROF signal is sent) +static mDNSu32 n_mrecords; // tracks the current active mcast records for McastLogging +static mDNSu32 n_mquests; // tracks the current active mcast questions for McastLogging // Note asymmetry here between registration and browsing. // For service registrations we only automatically register in domains that explicitly appear in local configuration data @@ -287,6 +307,8 @@ mDNSexport DNameListElem *AutoBrowseDomains; // List created from those l #define PID_FILE "/var/run/mDNSResponder.pid" #endif +mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen); + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - @@ -315,7 +337,7 @@ mDNSlocal void my_perror(char *errmsg) LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno)); } -//Throttled version of my_perror: Logs once every 250 msgs +// Throttled version of my_perror: Logs once every 250 msgs mDNSlocal void my_throttled_perror(char *err_msg) { static int uds_throttle_count = 0; @@ -323,6 +345,128 @@ mDNSlocal void my_throttled_perror(char *err_msg) my_perror(err_msg); } +// LogMcastQuestion/LogMcastQ should be called after the DNSQuestion struct is initialized(especially for q->TargetQID) +// Hence all calls are made after mDNS_StartQuery()/mDNS_StopQuery()/mDNS_StopBrowse() is called. +mDNSlocal void LogMcastQuestion(mDNS *const m, const DNSQuestion *const q, request_state *req, q_state status) +{ + if (mDNSOpaque16IsZero(q->TargetQID)) // Check for Mcast Query + { + mDNSBool mflag = mDNSfalse; + if (status == q_start) + { + if (++mcount == 1) + mflag = mDNStrue; + } + else + { + mcount--; + } + LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Question" : "-Question", q->qname.c, DNSTypeName(q->qtype), + q->InterfaceID == mDNSInterface_LocalOnly ? "lo" : q->InterfaceID == mDNSInterface_P2P ? "p2p" : + q->InterfaceID == mDNSInterface_Any ? "any" : InterfaceNameForID(m, q->InterfaceID), + req->process_id, req->pid_name); + LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse); + } + return; +} + +// LogMcastService/LogMcastS should be called after the AuthRecord struct is initialized +// Hence all calls are made after mDNS_Register()/ just before mDNS_Deregister() +mDNSlocal void LogMcastService(mDNS *const m, const AuthRecord *const ar, request_state *req, reg_state status) +{ + if (!AuthRecord_uDNS(ar)) // Check for Mcast Service + { + mDNSBool mflag = mDNSfalse; + if (status == reg_start) + { + if (++mcount == 1) + mflag = mDNStrue; + } + else + { + mcount--; + } + LogMcast("%s: %##s (%s) (%s) Client(%d)[%s]", status ? "+Service" : "-Service", ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), + ar->resrec.InterfaceID == mDNSInterface_LocalOnly ? "lo" : ar->resrec.InterfaceID == mDNSInterface_P2P ? "p2p" : + ar->resrec.InterfaceID == mDNSInterface_Any ? "all" : InterfaceNameForID(m, ar->resrec.InterfaceID), + req->process_id, req->pid_name); + LogMcastStateInfo(m, mflag, mDNSfalse, mDNSfalse); + } + return; +} + +// For complete Mcast State Log, pass mDNStrue to mstatelog in LogMcastStateInfo() +mDNSexport void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog) +{ + if (!mstatelog) + { + if (!all_requests) + { + LogMcastNoIdent(""); + } + else + { + request_state *req, *r; + for (req = all_requests; req; req=req->next) + { + if (req->primary) // If this is a subbordinate operation, check that the parent is in the list + { + for (r = all_requests; r && r != req; r=r->next) + if (r == req->primary) + goto foundpar; + } + // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info + GetMcastClients(req); + foundpar:; + } + LogMcastNoIdent("--- MCAST RECORDS COUNT[%d] MCAST QUESTIONS COUNT[%d] ---", n_mrecords, n_mquests); + n_mrecords = n_mquests = 0; // Reset the values + } + } + else + { + static mDNSu32 i_mpktnum; + i_mcount = 0; + if (start) + mcount = 0; + // mcount is initialized to 0 when the PROF signal is sent since mcount could have + // wrong value if MulticastLogging is disabled and then re-enabled + LogMcastNoIdent("--- START MCAST STATE LOG ---"); + if (!all_requests) + { + mcount = 0; + LogMcastNoIdent(""); + } + else + { + request_state *req, *r; + for (req = all_requests; req; req=req->next) + { + if (req->primary) // If this is a subbordinate operation, check that the parent is in the list + { + for (r = all_requests; r && r != req; r=r->next) + if (r == req->primary) + goto foundparent; + LogMcastNoIdent("%3d: Orphan operation; parent not found in request list", req->sd); + } + // For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info + LogMcastClientInfo(req); + foundparent:; + } + if(!mcount) // To initially set mcount + mcount = i_mcount; + } + if (mcount == 0) + { + i_mpktnum = m->MPktNum; + LogMcastNoIdent("--- MCOUNT[%d]: IMPKTNUM[%d] ---", mcount, i_mpktnum); + } + if (mflag) + LogMcastNoIdent("--- MCOUNT[%d]: CMPKTNUM[%d] - IMPKTNUM[%d] = [%d]PKTS ---", mcount, m->MPktNum, i_mpktnum, (m->MPktNum - i_mpktnum)); + LogMcastNoIdent("--- END MCAST STATE LOG ---"); + } +} + mDNSlocal void abort_request(request_state *req) { if (req->terminate == (req_termination_fn) ~0) @@ -539,6 +683,9 @@ mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, i artype = AuthRecordLocalOnly; else if (InterfaceID == mDNSInterface_P2P) artype = AuthRecordP2P; + else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P) + && (flags & kDNSServiceFlagsIncludeAWDL)) + artype = AuthRecordAnyIncludeAWDLandP2P; else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P)) artype = AuthRecordAnyIncludeP2P; else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeAWDL)) @@ -744,7 +891,16 @@ mDNSlocal void unlink_and_free_service_instance(service_instance *srv) if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage) freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata); - if (srv->subtypes) { freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; } + if (srv->subtypes) + { + freeL("ServiceSubTypes", srv->subtypes); + srv->subtypes = NULL; + } + if (srv->srs.AnonData) + { + freeL("Anonymous", (void *)srv->srs.AnonData); + srv->srs.AnonData = NULL; + } freeL("service_instance", srv); } @@ -944,13 +1100,21 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) if (result) { - // unlink from list, free memory - registered_record_entry **ptr = &request->u.reg_recs; - while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; - if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } - *ptr = (*ptr)->next; - freeL("registered_record_entry AuthRecord regrecord_callback", re->rr); - freeL("registered_record_entry regrecord_callback", re); + // If this is a callback to a keepalive record, do not free it. + if (result == mStatus_BadStateErr) + { + LogInfo("regrecord_callback: Callback with error code mStatus_BadStateErr - not freeing the record."); + } + else + { + // unlink from list, free memory + registered_record_entry **ptr = &request->u.reg_recs; + while (*ptr && (*ptr) != re) ptr = &(*ptr)->next; + if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; } + *ptr = (*ptr)->next; + freeL("registered_record_entry AuthRecord regrecord_callback", re->rr); + freeL("registered_record_entry regrecord_callback", re); + } } else { @@ -966,28 +1130,30 @@ mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result) } } -mDNSlocal pid_t get_peer_pid(int sock, char *pid_name_local) +// set_peer_pid() is called after mem is allocated for each new request in NewRequest() +// This accounts for 2 places (connect_callback, request_callback) +mDNSlocal void set_peer_pid(request_state *request) { - pid_t p = (pid_t) -1; - socklen_t len = sizeof(p); - pid_name_local[0] = '\0'; -#ifdef LOCAL_PEERPID - if (sock < 0) - return -1; + pid_t p = (pid_t) -1; + socklen_t len = sizeof(p); + request->pid_name[0] = '\0'; + request->process_id = -1; +#ifdef LOCAL_PEERPID + if (request->sd < 0) + return; // to extract the pid value - if (getsockopt(sock, SOL_LOCAL, LOCAL_PEERPID, &p, &len) != 0) - return -1; + if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEERPID, &p, &len) != 0) + return; // to extract the process name from the pid value if (proc_pidinfo(p, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) - return -1; - mDNSPlatformStrCopy(pid_name_local, proc.pbsi_comm); - return p; + return; + mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm); + request->process_id = p; #else // !LOCAL_PEERPID len = 0; - if (sock < 0) - return -1; - LogInfo("get_peer_pid: Not Supported on this version of OS"); - return -1; + if (request->sd < 0) + return; + LogInfo("set_peer_pid: Not Supported on this version of OS"); #endif // LOCAL_PEERPID } @@ -997,7 +1163,7 @@ mDNSlocal void connection_termination(request_state *request) // and terminate any subbordinate operations sharing this file descriptor request_state **req = &all_requests; - LogOperation("%3d: DNSServiceCreateConnection STOP PID[%d](%s)", request->sd, get_peer_pid(request->sd, pid_name), pid_name); + LogOperation("%3d: DNSServiceCreateConnection STOP PID[%d](%s)", request->sd, request->process_id, request->pid_name); while (*req) { @@ -1018,7 +1184,7 @@ mDNSlocal void connection_termination(request_state *request) while (request->u.reg_recs) { registered_record_entry *ptr = request->u.reg_recs; - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), get_peer_pid(request->sd, pid_name), pid_name); + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP PID[%d](%s)", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec), request->process_id, request->pid_name); request->u.reg_recs = request->u.reg_recs->next; ptr->rr->RecordContext = NULL; if (ptr->external_advertise) @@ -1026,6 +1192,7 @@ mDNSlocal void connection_termination(request_state *request) ptr->external_advertise = mDNSfalse; external_stop_advertising_service(&ptr->rr->resrec, request->flags); } + LogMcastS(&mDNSStorage, ptr->rr, request, reg_stop); mDNS_Deregister(&mDNSStorage, ptr->rr); // Will free ptr->rr for us freeL("registered_record_entry/connection_termination", ptr); } @@ -1070,7 +1237,8 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) } // allocate registration entry, link into list re = mallocL("registered_record_entry", sizeof(registered_record_entry)); - if (!re) FatalError("ERROR: malloc"); + if (!re) + FatalError("ERROR: malloc"); re->key = request->hdr.reg_index; re->rr = rr; re->regrec_client_context = request->hdr.client_context; @@ -1080,16 +1248,18 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) rr->RecordCallback = regrecord_callback; re->origInterfaceID = rr->resrec.InterfaceID; - if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any; + if (rr->resrec.InterfaceID == mDNSInterface_P2P) + rr->resrec.InterfaceID = mDNSInterface_Any; #if 0 if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains)) return (mStatus_NoError); #endif if (rr->resrec.rroriginalttl == 0) rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype); - LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", - request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), get_peer_pid(request->sd, pid_name), pid_name); - err = mDNS_Register(&mDNSStorage, rr); + LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START PID[%d](%s)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), + request->process_id, request->pid_name); + + err = mDNS_Register(&mDNSStorage, rr); if (err) { LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err); @@ -1098,6 +1268,7 @@ mDNSlocal mStatus handle_regrecord_request(request_state *request) } else { + LogMcastS(&mDNSStorage, rr, request, reg_start); re->next = request->u.reg_recs; request->u.reg_recs = re; } @@ -1109,14 +1280,18 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m); mDNSlocal void regservice_termination_callback(request_state *request) { - if (!request) { LogMsg("regservice_termination_callback context is NULL"); return; } + if (!request) + { + LogMsg("regservice_termination_callback context is NULL"); + return; + } while (request->u.servicereg.instances) { service_instance *p = request->u.servicereg.instances; request->u.servicereg.instances = request->u.servicereg.instances->next; // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p) - LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", - request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), get_peer_pid(request->sd, pid_name), pid_name); + LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP PID[%d](%s)", request->sd, p->srs.RR_SRV.resrec.name->c, + mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port), request->process_id, request->pid_name); external_stop_advertising_helper(p); @@ -1126,11 +1301,18 @@ mDNSlocal void regservice_termination_callback(request_state *request) // We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance // because by then we might have already freed p p->request = NULL; - if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) unlink_and_free_service_instance(p); - // Don't touch service_instance *p after this -- it's likely to have been freed already + LogMcastS(&mDNSStorage, &p->srs.RR_SRV, request, reg_stop); + if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) + { + unlink_and_free_service_instance(p); + // Don't touch service_instance *p after this -- it's likely to have been freed already + } } if (request->u.servicereg.txtdata) - { freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; } + { + freeL("service_info txtdata", request->u.servicereg.txtdata); + request->u.servicereg.txtdata = NULL; + } if (request->u.servicereg.autoname) { // Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations @@ -1172,8 +1354,13 @@ mDNSlocal mStatus add_record_to_service(request_state *request, service_instance coreFlags |= coreFlagIncludeAWDL; result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl, coreFlags); - if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; } - + if (result) + { + freeL("ExtraResourceRecord/add_record_to_service", extra); + return result; + } + LogMcastS(&mDNSStorage, &srs->RR_PTR, request, reg_start); + extra->ClientID = request->hdr.reg_index; if ( instance->external_advertise && callExternalHelpers(request->u.servicereg.InterfaceID, &instance->domain, request->flags)) @@ -1381,13 +1568,13 @@ mDNSlocal mStatus remove_record(request_state *request) external_stop_advertising_service(&e->rr->resrec, request->flags); e->external_advertise = mDNSfalse; } + LogMcastS(&mDNSStorage, e->rr, request, reg_stop); err = mDNS_Deregister(&mDNSStorage, e->rr); // Will free e->rr for us; we're responsible for freeing e if (err) { LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err); freeL("registered_record_entry AuthRecord remove_record", e->rr); } - freeL("registered_record_entry remove_record", e); return err; } @@ -1445,13 +1632,28 @@ mDNSlocal mStatus handle_removerecord_request(request_state *request) // If there's a comma followed by another character, // FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character. // Otherwise, it returns a pointer to the final nul at the end of the string -mDNSlocal char *FindFirstSubType(char *p) +mDNSlocal char *FindFirstSubType(char *p, char **AnonData) { while (*p) { - if (p[0] == '\\' && p[1]) p += 2; - else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); } - else p++; + if (p[0] == '\\' && p[1]) + { + p += 2; + } + else if (p[0] == ',' && p[1]) + { + *p++ = 0; + return(p); + } + else if (p[0] == ':' && p[1]) + { + *p++ = 0; + *AnonData = p; + } + else + { + p++; + } } return(p); } @@ -1479,10 +1681,10 @@ mDNSlocal char *FindNextSubType(char *p) } // Returns -1 if illegal subtype found -mDNSexport mDNSs32 ChopSubTypes(char *regtype) +mDNSexport mDNSs32 ChopSubTypes(char *regtype, char **AnonData) { mDNSs32 NumSubTypes = 0; - char *stp = FindFirstSubType(regtype); + char *stp = FindFirstSubType(regtype, AnonData); while (stp && *stp) // If we found a comma... { if (*stp == ',') return(-1); @@ -1493,9 +1695,42 @@ mDNSexport mDNSs32 ChopSubTypes(char *regtype) return(NumSubTypes); } -mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) +mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData) { AuthRecord *st = mDNSNULL; + // + // "p" is pointing at the regtype e.g., _http._tcp followed by ":" indicated + // by AnonData being non-NULL which is in turn follwed by "," indicated by + // NumSubTypes being non-zero. We need to skip the initial regtype to get to the actual + // data that we want. When we come here, ChopSubTypes has null terminated like this e.g., + // + // _http._tcp etc. + // + // 1. If we have Anonymous data and subtypes, skip the regtype (e.g., "_http._tcp") + // to get the AnonData and then skip the AnonData to get to the SubType. + // + // 2. If we have only SubTypes, skip the regtype to get to the SubType data. + // + // 3. If we have only AnonData, skip the regtype to get to the AnonData. + // + // 4. If we don't have AnonData or NumStypes, it is a noop. + // + if (AnonData) + { + int len; + + // Skip the regtype + while (*p) p++; + p++; + + len = strlen(p) + 1; + *AnonData = mallocL("Anonymous", len); + if (!(*AnonData)) + { + return (mDNSNULL); + } + mDNSPlatformMemCopy(*AnonData, p, len); + } if (NumSubTypes) { mDNSs32 i; @@ -1504,12 +1739,21 @@ mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p) for (i = 0; i < NumSubTypes; i++) { mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL); + // First time through we skip the regtype or AnonData. Subsequently, the + // previous subtype. while (*p) p++; p++; if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p)) - { freeL("ServiceSubTypes", st); return(mDNSNULL); } + { + freeL("ServiceSubTypes", st); + if (*AnonData) + freeL("AnonymousData", *AnonData); + return(mDNSNULL); + } } } + // If NumSubTypes is zero and AnonData is non-NULL, we still return NULL but AnonData has been + // initialized. The caller knows how to handle this. return(st); } @@ -1527,11 +1771,14 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain if (request->flags & kDNSServiceFlagsIncludeAWDL) coreFlags |= coreFlagIncludeAWDL; - // client guarantees that record names are unique - // we reuse this deprecated flag for his fucntion - if (request->flags & kDNSServiceFlagsForce) + // Client guarantees that record names are unique, so we can skip sending out initial + // probe messages. Standard name conflict resolution is still done if a conflict is discovered. + if (request->flags & kDNSServiceFlagsKnownUnique) coreFlags |= coreFlagKnownUnique; + if (request->flags & kDNSServiceFlagsWakeOnlyService) + coreFlags |= coreFlagWakeOnly; + // If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS) // registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast // registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface. @@ -1549,32 +1796,36 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain } } - if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6) - { - // Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains, - // because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not. - // BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6 - if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp")) - { - DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain); - if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported); - } - } - instance = mallocL("service_instance", sizeof(*instance) + extra_size); if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; } instance->next = mDNSNULL; instance->request = request; - instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string); instance->renameonmemfree = 0; instance->clientnotified = mDNSfalse; instance->default_local = (request->u.servicereg.default_domain && DomainIsLocal); instance->external_advertise = mDNSfalse; AssignDomainName(&instance->domain, domain); + instance->srs.AnonData = mDNSNULL; + if (!request->u.servicereg.AnonData) + { + instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, mDNSNULL); + } + else + { + char *AnonData = mDNSNULL; + instance->subtypes = AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string, &AnonData); + if (AnonData) + instance->srs.AnonData = (const mDNSu8 *)AnonData; + } + if (request->u.servicereg.num_subtypes && !instance->subtypes) - { unlink_and_free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); } + { + unlink_and_free_service_instance(instance); + instance = NULL; + FatalError("ERROR: malloc"); + } result = mDNS_RegisterService(&mDNSStorage, &instance->srs, &request->u.servicereg.name, &request->u.servicereg.type, domain, @@ -1587,8 +1838,9 @@ mDNSlocal mStatus register_service_instance(request_state *request, const domain if (!result) { *ptr = instance; // Append this to the end of our request->u.servicereg.instances list - LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", - instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port)); + LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED", instance->request->sd, + instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port)); + LogMcastS(&mDNSStorage, &instance->srs.RR_SRV, request, reg_start); } else { @@ -1663,6 +1915,55 @@ mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d } } +// Don't allow normal and anonymous registration to coexist. +mDNSlocal mDNSBool CheckForMixedRegistrations(domainname *regtype, domainname *domain, mDNSBool AnonData) +{ + request_state *request; + + // We only care about local domains where the anonymous extension is + // implemented. + if (!SameDomainName(domain, (const domainname *) "\x5" "local")) + { + return mDNStrue; + } + + for (request = all_requests; request; request = request->next) + { + service_instance *ptr; + + if (request->terminate != regservice_termination_callback) continue; + for (ptr = request->u.servicereg.instances; ptr ; ptr = ptr->next) + { + if (!SameDomainName(&ptr->domain, (const domainname *)"\x5" "local") || + !SameDomainName(&request->u.servicereg.type, regtype)) + { + continue; + } + + // If we are about to register a anonymous registraion, we dont't want to + // allow the regular ones and vice versa. + if (AnonData) + { + if (!ptr->srs.AnonData) + { + LogMsg("CheckForMixedRegistrations: Normal registration already exists for %##s", regtype->c); + return mDNSfalse; + } + } + else + { + // Allow multiple regular registrations + if (ptr->srs.AnonData) + { + LogMsg("CheckForMixedRegistrations: Anonymous registration already exists for %##s", regtype->c); + return mDNSfalse; + } + } + } + } + return mDNStrue; +} + mDNSlocal mStatus handle_regservice_request(request_state *request) { char name[256]; // Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes @@ -1670,6 +1971,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) char type_as_string[MAX_ESCAPED_DOMAIN_NAME]; domainname d, srv; mStatus err; + char *AnonData = mDNSNULL; DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); @@ -1719,9 +2021,26 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) if (!request->msgptr) { LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } // Check for sub-types after the service type - request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string); // Note: Modifies regtype string to remove trailing subtypes + request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string, &AnonData); // Note: Modifies regtype string to remove trailing subtypes if (request->u.servicereg.num_subtypes < 0) - { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); } + { + LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); + return(mStatus_BadParamErr); + } + if (AnonData) + { + int AnonDataLen = strlen(AnonData); + if (AnonDataLen > MAX_ANONYMOUS_DATA) + { + LogMsg("ERROR: handle_regservice_request: AnonDataLen %d", AnonDataLen); + return(mStatus_BadParamErr); + } + request->u.servicereg.AnonData = mDNStrue; + } + else + { + request->u.servicereg.AnonData = mDNSfalse; + } // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string)) @@ -1757,6 +2076,12 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) MakeDomainNameFromDNSNameString(&d, "local."); } + // We don't allow the anonymous and the regular ones to coexist + if (!CheckForMixedRegistrations(&request->u.servicereg.type, &d, request->u.servicereg.AnonData)) + { + return(mStatus_BadParamErr); + } + if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d)) { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", @@ -1775,13 +2100,13 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) { int count = CountExistingRegistrations(&srv, request->u.servicereg.port); if (count) - LogMsg("Client application registered %d identical instances of service %##s port %u.", - count+1, srv.c, mDNSVal16(request->u.servicereg.port)); + LogMsg("Client application[%d](%s) registered %d identical instances of service %##s port %u.", request->process_id, + request->pid_name, count+1, srv.c, mDNSVal16(request->u.servicereg.port)); } LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START PID[%d](%s)", - request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, - mDNSVal16(request->u.servicereg.port), get_peer_pid(request->sd, pid_name), pid_name); + request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, + mDNSVal16(request->u.servicereg.port), request->process_id, request->pid_name); // We need to unconditionally set request->terminate, because even if we didn't successfully // start any registrations right now, subsequent configuration changes may cause successful @@ -1821,7 +2146,7 @@ mDNSlocal mStatus handle_regservice_request(request_state *request) mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) { - const DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0; + DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0; request_state *req = question->QuestionContext; reply_state *rep; (void)m; // Unused @@ -1829,6 +2154,11 @@ mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const Resourc if (answer->rrtype != kDNSType_PTR) { LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; } + if (mDNSOpaque16IsZero(question->TargetQID) && (question->BrowseThreshold > 0) && (question->CurrentAnswers >= question->BrowseThreshold)) + { + flags |= kDNSServiceFlagsThresholdReached; + } + if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError) { if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp")) @@ -1867,8 +2197,8 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d b = mallocL("browser_t", sizeof(*b)); if (!b) return mStatus_NoMemoryErr; AssignDomainName(&b->domain, d); - err = mDNS_StartBrowse(&mDNSStorage, &b->q, - &info->u.browser.regtype, d, info->u.browser.interface_id, info->flags, info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info); + err = mDNS_StartBrowse(&mDNSStorage, &b->q, &info->u.browser.regtype, d, info->u.browser.AnonData, info->u.browser.interface_id, info->flags, + info->u.browser.ForceMCast, (info->flags & kDNSServiceFlagsBackgroundTrafficClass) != 0, FoundInstance, info); if (err) { LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c); @@ -1878,8 +2208,9 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d { b->next = info->u.browser.browsers; info->u.browser.browsers = b; - LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, get_peer_pid(info->sd, pid_name), pid_name); - + LogOperation("%3d: DNSServiceBrowse(%##s) START PID[%d](%s)", info->sd, b->q.qname.c, info->process_id, + info->pid_name); + LogMcastQ(&mDNSStorage, &b->q, info, q_start); if (callExternalHelpers(info->u.browser.interface_id, &b->domain, info->flags)) { domainname tmp; @@ -1893,6 +2224,14 @@ mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d mDNSlocal void browse_termination_callback(request_state *info) { + if (info->u.browser.default_domain) + { + // Stop the domain enumeration queries to discover the WAB legacy browse domains + LogInfo("%3d: DNSServiceBrowse Cancel WAB PID[%d](%s)", info->sd, info->process_id, info->pid_name); + uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY); + } + if (info->u.browser.AnonData) + freeL("Anonymous", (void *)info->u.browser.AnonData); while (info->u.browser.browsers) { browser_t *ptr = info->u.browser.browsers; @@ -1906,8 +2245,9 @@ mDNSlocal void browse_termination_callback(request_state *info) } info->u.browser.browsers = ptr->next; - LogOperation("%3d: DNSServiceBrowse(%##s) STOP PID[%d](%s)", info->sd, ptr->q.qname.c, get_peer_pid(info->sd, pid_name), pid_name); + LogOperation("%3d: DNSServiceBrowse(%##s) STOP PID[%d](%s)", info->sd, ptr->q.qname.c, info->process_id, info->pid_name); mDNS_StopBrowse(&mDNSStorage, &ptr->q); // no need to error-check result + LogMcastQ(&mDNSStorage, &ptr->q, info, q_stop); freeL("browser_t/browse_termination_callback", ptr); } } @@ -2077,6 +2417,8 @@ mDNSlocal void SetPrefsBrowseDomains(mDNS *m, DNameListElem *browseDomains, mDNS } } +#if APPLE_OSX_mDNSResponder + mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m) { int num_autoname = 0; @@ -2097,17 +2439,19 @@ mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m) if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered) if (num_autoname > 0) { - mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6; mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL); ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain); - mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6); - mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len); - m->DeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string - m->DeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string + m->DeviceInfo.resrec.rdlength = initializeDeviceInfoTXT(m, m->DeviceInfo.resrec.rdata->u.data); LogOperation("UpdateDeviceInfoRecord Register %##s", m->DeviceInfo.resrec.name); mDNS_Register(m, &m->DeviceInfo); } } +#else // APPLE_OSX_mDNSResponder +mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m) +{ + (void)m; // unused +} +#endif // APPLE_OSX_mDNSResponder mDNSexport void udsserver_handle_configchange(mDNS *const m) { @@ -2137,7 +2481,7 @@ mDNSexport void udsserver_handle_configchange(mDNS *const m) // Let the platform layer get the current DNS information mDNS_Lock(m); - mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains); + mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains, mDNSfalse); mDNS_Unlock(m); // Any automatic registration domains are also implicitly automatic browsing domains @@ -2211,7 +2555,9 @@ mDNSlocal mStatus handle_browse_request(request_state *request) char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME]; domainname typedn, d, temp; mDNSs32 NumSubTypes; + char *AnonData = mDNSNULL; mStatus err = mStatus_NoError; + int AnonDataLen; DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend); mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend); @@ -2223,13 +2569,28 @@ mDNSlocal mStatus handle_browse_request(request_state *request) if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY); - request->flags = flags; typedn.c[0] = 0; - NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes - if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr); - if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr); + NumSubTypes = ChopSubTypes(regtype, &AnonData); // Note: Modifies regtype string to remove trailing subtypes + if (NumSubTypes < 0 || NumSubTypes > 1) + return(mStatus_BadParamErr); + AnonDataLen = 0; + if (AnonData) + { + AnonDataLen = strlen(AnonData); + if (AnonDataLen > MAX_ANONYMOUS_DATA) + { + LogMsg("handle_browse_request: AnonDataLen %d", AnonDataLen); + return(mStatus_BadParamErr); + } + // Account for the null byte + AnonDataLen += 1; + } + if (NumSubTypes == 1) + { + if (!AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1 + AnonDataLen)) + return(mStatus_BadParamErr); + } if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr); @@ -2245,8 +2606,24 @@ mDNSlocal mStatus handle_browse_request(request_state *request) request->u.browser.browsers = NULL; LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START PID[%d](%s)", - request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, get_peer_pid(request->sd, pid_name), pid_name); + request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain, request->process_id, request->pid_name); + if (request->u.browser.default_domain) + { + // Start the domain enumeration queries to discover the WAB browse domains + LogInfo("%3d: DNSServiceBrowse Start WAB PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_LBROWSE_QUERY); + } + request->u.browser.AnonData = mDNSNULL; + if (AnonData) + { + int len = strlen(AnonData) + 1; + request->u.browser.AnonData = mallocL("Anonymous", len); + if (!request->u.browser.AnonData) + return mStatus_NoMemoryErr; + else + mDNSPlatformMemCopy((void *)request->u.browser.AnonData, AnonData, len); + } // We need to unconditionally set request->terminate, because even if we didn't successfully // start any browses right now, subsequent configuration changes may cause successful // browses to be added, and we'll need to cancel them before freeing this memory. @@ -2338,10 +2715,12 @@ mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, con mDNSlocal void resolve_termination_callback(request_state *request) { - LogOperation("%3d: DNSServiceResolve(%##s) STOP PID[%d](%s)", request->sd, request->u.resolve.qtxt.qname.c, get_peer_pid(request->sd, pid_name), pid_name); + LogOperation("%3d: DNSServiceResolve(%##s) STOP PID[%d](%s)", request->sd, request->u.resolve.qtxt.qname.c, request->process_id, request->pid_name); mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt); mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); - if (request->u.resolve.external_advertise) external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags); + LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_stop); + if (request->u.resolve.external_advertise) + external_stop_resolving_service(request->u.resolve.qsrv.InterfaceID, &request->u.resolve.qsrv.qname, request->flags); } mDNSlocal mStatus handle_resolve_request(request_state *request) @@ -2399,10 +2778,13 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse; request->u.resolve.qsrv.TimeoutQuestion = 0; request->u.resolve.qsrv.WakeOnResolve = (flags & kDNSServiceFlagsWakeOnResolve) != 0; - request->u.resolve.qsrv.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + request->u.resolve.qsrv.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; request->u.resolve.qsrv.ValidationRequired = 0; request->u.resolve.qsrv.ValidatingResponse = 0; + request->u.resolve.qsrv.ProxyQuestion = 0; request->u.resolve.qsrv.qnameOrig = mDNSNULL; + request->u.resolve.qsrv.AnonInfo = mDNSNULL; + request->u.resolve.qsrv.pid = request->process_id; request->u.resolve.qsrv.QuestionCallback = resolve_result_callback; request->u.resolve.qsrv.QuestionContext = request; @@ -2422,10 +2804,13 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse; request->u.resolve.qtxt.TimeoutQuestion = 0; request->u.resolve.qtxt.WakeOnResolve = 0; - request->u.resolve.qtxt.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; + request->u.resolve.qtxt.UseBackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; request->u.resolve.qtxt.ValidationRequired = 0; request->u.resolve.qtxt.ValidatingResponse = 0; + request->u.resolve.qtxt.ProxyQuestion = 0; request->u.resolve.qtxt.qnameOrig = mDNSNULL; + request->u.resolve.qtxt.AnonInfo = mDNSNULL; + request->u.resolve.qtxt.pid = request->process_id; request->u.resolve.qtxt.QuestionCallback = resolve_result_callback; request->u.resolve.qtxt.QuestionContext = request; @@ -2439,16 +2824,20 @@ mDNSlocal mStatus handle_resolve_request(request_state *request) // ask the questions LogOperation("%3d: DNSServiceResolve(%X %d %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, - request->u.resolve.qsrv.qname.c, get_peer_pid(request->sd, pid_name), pid_name); + request->u.resolve.qsrv.qname.c, request->process_id, request->pid_name); err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv); + if (!err) { err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt); - if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + if (err) + { + mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv); + } else { request->terminate = resolve_termination_callback; - + LogMcastQ(&mDNSStorage, &request->u.resolve.qsrv, request, q_start); if (callExternalHelpers(InterfaceID, &fqdn, flags)) { request->u.resolve.external_advertise = mDNStrue; @@ -2616,6 +3005,7 @@ mDNSlocal mDNSBool ShouldDeliverNegativeResponse(mDNS *const m, DNSQuestion *que // top-level domain mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err) { +#ifndef UNICAST_DISABLED extern domainname ActiveDirectoryPrimaryDomain; DNSQuestion **question2; #define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp")) @@ -2704,17 +3094,26 @@ mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mS q2->RetryWithSearchDomains = mDNSfalse; q2->SearchListIndex = 0; q2->TimeoutQuestion = 0; + q2->AnonInfo = mDNSNULL; + q2->pid = request->process_id; } LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype)); err = mDNS_StartQuery(&mDNSStorage, q2); if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err); } return(err); +#else // !UNICAST_DISABLED + (void) q; + (void) request; + (void) err; + + return mStatus_NoError; +#endif // !UNICAST_DISABLED } #endif // APPLE_OSX_mDNSResponder // This function tries to append a search domain if valid and possible. If so, returns true. -mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req) +mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req, QC_result AddRecord) { int result; // RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no @@ -2725,7 +3124,7 @@ mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *qu // If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and // is a valid question for appending search domains, retry by appending domains - if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains) + if ((AddRecord != QC_suppressed) && question->SearchListIndex != -1 && question->AppendSearchDomains) { question->RetryWithSearchDomains = 0; result = AppendNewSearchDomain(m, question); @@ -2757,67 +3156,237 @@ mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *qu } else { - LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains); + LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, AddRecord, question->SearchListIndex, question->AppendSearchDomains); } return mDNSfalse; } -mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +mDNSlocal void queryrecord_result_reply(mDNS *const m, request_state *req, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord, + DNSServiceErrorType error) { char name[MAX_ESCAPED_DOMAIN_NAME]; - request_state *req = question->QuestionContext; + size_t len; + DNSServiceFlags flags = 0; reply_state *rep; char *data; - size_t len; - DNSServiceErrorType error = kDNSServiceErr_NoError; - DNSQuestion *q = mDNSNULL; -#if APPLE_OSX_mDNSResponder - { - // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not - // get any callbacks from the core after this. - if (!req) - { - LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - return; - } - if (req->hdr.op == query_request && question == req->u.queryrecord.q2) - q = &req->u.queryrecord.q; - else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42) - q = &req->u.addrinfo.q4; - else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62) - q = &req->u.addrinfo.q6; + ConvertDomainNameToCString(answer->name, name); - if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname)) - { - mStatus err; - domainname *orig = question->qnameOrig; + LogOperation("%3d: %s(%##s, %s) %s %s", req->sd, + req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo", + question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); - LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c); - mDNS_StopQuery(m, question); - question->QuestionContext = mDNSNULL; + len = sizeof(DNSServiceFlags); // calculate reply data length + len += sizeof(mDNSu32); // interface index + len += sizeof(DNSServiceErrorType); + len += strlen(name) + 1; + len += 3 * sizeof(mDNSu16); // type, class, rdlen + len += answer->rdlength; + len += sizeof(mDNSu32); // TTL - // We got a negative response for the SOA record indicating that .local does not exist. - // But we might have other search domains (that does not end in .local) that can be - // appended to this question. In that case, we want to retry the question. Otherwise, - // we don't want to try this question as unicast. - if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains) + rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req); + + if (AddRecord) + flags |= kDNSServiceFlagsAdd; + if (question->ValidationStatus != 0) + { + error = kDNSServiceErr_NoError; + if (question->ValidationRequired && question->ValidationState == DNSSECValDone) + { + switch (question->ValidationStatus) //Set the dnssec flags to be passed on to the Apps here { - LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c); - return; + case DNSSEC_Secure: + flags |= kDNSServiceFlagsSecure; + break; + case DNSSEC_Insecure: + flags |= kDNSServiceFlagsInsecure; + break; + case DNSSEC_Indeterminate: + flags |= kDNSServiceFlagsIndeterminate; + break; + case DNSSEC_Bogus: + flags |= kDNSServiceFlagsBogus; + break; + default: + LogMsg("queryrecord_result_reply unknown status %d for %##s", question->ValidationStatus, question->qname.c); } + } + } + + rep->rhdr->flags = dnssd_htonl(flags); + // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the + // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions + // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we + // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the + // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in + // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords + // should not have existed to answer this question if the corresponding interface is not valid. + rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue)); + rep->rhdr->error = dnssd_htonl(error); + + data = (char *)&rep->rhdr[1]; + + put_string(name, &data); + put_uint16(answer->rrtype, &data); + put_uint16(answer->rrclass, &data); + put_uint16(answer->rdlength, &data); + // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata + // function just does a blind memory copy without regard to structures that may have holes in them. + if (answer->rdlength) + if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer)) + LogMsg("queryrecord_result_reply putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data); + data += answer->rdlength; + put_uint32(AddRecord ? answer->rroriginalttl : 0, &data); + + append_reply(req, rep); + // Stop the question, if we just timed out + if (error == kDNSServiceErr_Timeout) + { + mDNS_StopQuery(m, question); + // Reset the pointers so that we don't call stop on termination + question->QuestionContext = mDNSNULL; + } + else if ((AddRecord == QC_add) && req->hdr.op == addrinfo_request) + { + // Note: We count all answers including LocalOnly e.g., /etc/hosts. If we + // exclude that, v4ans/v6ans will be zero and we would wrongly think that + // we did not answer questions and setup the status to deliver triggers. + if (question->qtype == kDNSType_A) + req->u.addrinfo.v4ans = 1; + if (question->qtype == kDNSType_AAAA) + req->u.addrinfo.v6ans = 1; + } + else if ((AddRecord == QC_add) && req->hdr.op == query_request) + { + if (question->qtype == kDNSType_A || question->qtype == kDNSType_AAAA) + req->u.queryrecord.ans = 1; + } + +#if APPLE_OSX_mDNSResponder +#if !NO_WCF + CHECK_WCF_FUNCTION(WCFIsServerRunning) + { + struct xucred x; + socklen_t xucredlen = sizeof(x); + + if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0) + { + if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && + (x.cr_version == XUCRED_VERSION)) + { + struct sockaddr_storage addr; + const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data; + addr.ss_len = 0; + if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) + { + if (answer->rrtype == kDNSType_A) + { + struct sockaddr_in *sin = (struct sockaddr_in *)&addr; + sin->sin_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer)) + LogMsg("queryrecord_result_reply: WCF AF_INET putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in); + addr.ss_family = AF_INET; + } + } + else if (answer->rrtype == kDNSType_AAAA) + { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; + sin6->sin6_port = 0; + if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer)) + LogMsg("queryrecord_result_reply: WCF AF_INET6 putRData failed"); + else + { + addr.ss_len = sizeof (struct sockaddr_in6); + addr.ss_family = AF_INET6; + } + } + if (addr.ss_len) + { + debugf("queryrecord_result_reply: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid); + } + } + } + else if (answer->rrtype == kDNSType_CNAME) + { + domainname cname; + char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; + if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer)) + LogMsg("queryrecord_result_reply: WCF CNAME putRData failed"); + else + { + ConvertDomainNameToCString(&cname, cname_cstr); + CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) + { + WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid); + } + } + } + } + else my_perror("queryrecord_result_reply: ERROR: getsockopt LOCAL_PEERCRED"); + } + } +#endif +#endif +} + +mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) +{ + request_state *req = question->QuestionContext; + DNSServiceErrorType error = kDNSServiceErr_NoError; + DNSQuestion *q = mDNSNULL; + +#if APPLE_OSX_mDNSResponder + { + // Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not + // get any callbacks from the core after this. + if (!req) + { + LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); + return; + } + if (req->hdr.op == query_request && question == req->u.queryrecord.q2) + q = &req->u.queryrecord.q; + else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42) + q = &req->u.addrinfo.q4; + else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62) + q = &req->u.addrinfo.q6; + + if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname)) + { + mStatus err; + domainname *orig = question->qnameOrig; + + LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c); + mDNS_StopQuery(m, question); + question->QuestionContext = mDNSNULL; + + // We got a negative response for the SOA record indicating that .local does not exist. + // But we might have other search domains (that does not end in .local) that can be + // appended to this question. In that case, we want to retry the question. Otherwise, + // we don't want to try this question as unicast. + if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains) + { + LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c); + return; + } + + // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query + // + // Note: When we copy the original question, we copy everything including the AppendSearchDomains, + // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is + // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in + // SendAdditionalQuery as to how qnameOrig gets initialized. + *question = *q; + question->InterfaceID = mDNSInterface_Unicast; + question->ExpectUnique = mDNStrue; + question->qnameOrig = orig; - // If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query - // - // Note: When we copy the original question, we copy everything including the AppendSearchDomains, - // RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is - // e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in - // SendAdditionalQuery as to how qnameOrig gets initialized. - *question = *q; - question->InterfaceID = mDNSInterface_Unicast; - question->ExpectUnique = mDNStrue; - question->qnameOrig = orig; - LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext); // If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above. @@ -2839,14 +3408,14 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, question->AppendLocalSearchDomains = 0; } - if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength) + if (q && AddRecord && AddRecord != QC_dnssec && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength) { // If we get a negative response to the unicast query that we sent above, retry after appending search domains // Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here. // As we ignore negative unicast answers below, we would never reach the code where the search domains are appended. // To keep things simple, we handle unicast ".local" separately here. LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype)); - if (RetryQuestionWithSearchDomains(m, question, req)) + if (RetryQuestionWithSearchDomains(m, question, req, AddRecord)) return; if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname)) { @@ -2863,6 +3432,18 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, } #endif // APPLE_OSX_mDNSResponder + // If a query is being suppressed for some reason, we don't have to do any other + // processing. + // + // Note: We don't check for "SuppressQuery" and instead use QC_suppressed because + // 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)); + queryrecord_result_reply(m, req, question, answer, AddRecord, kDNSServiceErr_NoSuchRecord); + return; + } + if (answer->RecordType == kDNSRecordTypePacketNegative) { // If this question needs to be timed out and we have reached the stop time, mark @@ -2893,26 +3474,30 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, // Sanity check: "q" will be set only if "question" is the .local unicast query. if (!q) { - LogMsg("queryrecord_result_callback: ERROR!! answering multicast question with unicast cache record"); + LogMsg("queryrecord_result_callback: ERROR!! answering multicast question %s with unicast cache record", + RRDisplayString(m, answer)); return; } +#if APPLE_OSX_mDNSResponder if (!ShouldDeliverNegativeResponse(m, question)) { return; } - LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response (found positive record)", question->qname.c, DNSTypeName(question->qtype)); +#endif // APPLE_OSX_mDNSResponder + LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with negative unicast response", question->qname.c, + DNSTypeName(question->qtype)); } error = kDNSServiceErr_NoSuchRecord; } - AddRecord = mDNStrue; } // If we get a negative answer, try appending search domains. Don't append search domains // - if we are timing out this question // - if the negative response was received as a result of a multicast query // - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below) + // - if this response is forced e.g., dnssec validation result if (error != kDNSServiceErr_Timeout) { - if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord) + if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord && AddRecord != QC_dnssec) { // If the original question did not end in .local, we did not send an SOA query // to figure out whether we should send an additional unicast query or not. If we just @@ -2920,7 +3505,7 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, // 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)); - if (RetryQuestionWithSearchDomains(m, question, req)) + if (RetryQuestionWithSearchDomains(m, question, req, AddRecord)) { // Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could // be anywhere in the search domain list. @@ -2933,136 +3518,17 @@ mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, } } } - - ConvertDomainNameToCString(answer->name, name); - - LogOperation("%3d: %s(%##s, %s) %s %s", req->sd, - req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo", - question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer)); - - len = sizeof(DNSServiceFlags); // calculate reply data length - len += sizeof(mDNSu32); // interface index - len += sizeof(DNSServiceErrorType); - len += strlen(name) + 1; - len += 3 * sizeof(mDNSu16); // type, class, rdlen - len += answer->rdlength; - len += sizeof(mDNSu32); // TTL - - rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req); - - rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0); - // Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the - // InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions - // to be stopped and started including *this* one. Normally the InterfaceID is valid. But when we - // are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the - // mDNS core . Eventually, we should remove the calls to "NetworkChanged" in - // mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords - // should not have existed to answer this question if the corresponding interface is not valid. - rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue)); - rep->rhdr->error = dnssd_htonl(error); - - data = (char *)&rep->rhdr[1]; - - put_string(name, &data); - put_uint16(answer->rrtype, &data); - put_uint16(answer->rrclass, &data); - put_uint16(answer->rdlength, &data); - // We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata - // function just does a blind memory copy without regard to structures that may have holes in them. - if (answer->rdlength) - if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer)) - LogMsg("queryrecord_result_callback putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data); - data += answer->rdlength; - put_uint32(AddRecord ? answer->rroriginalttl : 0, &data); - - append_reply(req, rep); - // Stop the question, if we just timed out - if (error == kDNSServiceErr_Timeout) - { - mDNS_StopQuery(m, question); - // Reset the pointers so that we don't call stop on termination - question->QuestionContext = mDNSNULL; - } -#if APPLE_OSX_mDNSResponder -#if !NO_WCF - CHECK_WCF_FUNCTION(WCFIsServerRunning) - { - struct xucred x; - socklen_t xucredlen = sizeof(x); - - if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0) - { - if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && - (x.cr_version == XUCRED_VERSION)) - { - struct sockaddr_storage addr; - const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data; - addr.ss_len = 0; - if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA) - { - if (answer->rrtype == kDNSType_A) - { - struct sockaddr_in *sin = (struct sockaddr_in *)&addr; - sin->sin_port = 0; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer)) - LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed"); - else - { - addr.ss_len = sizeof (struct sockaddr_in); - addr.ss_family = AF_INET; - } - } - else if (answer->rrtype == kDNSType_AAAA) - { - struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr; - sin6->sin6_port = 0; - if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer)) - LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed"); - else - { - addr.ss_len = sizeof (struct sockaddr_in6); - addr.ss_family = AF_INET6; - } - } - if (addr.ss_len) - { - debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len); - CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) - { - WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid); - } - } - } - else if (answer->rrtype == kDNSType_CNAME) - { - domainname cname; - char cname_cstr[MAX_ESCAPED_DOMAIN_NAME]; - if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer)) - LogMsg("queryrecord_result_callback: WCF CNAME putRData failed"); - else - { - ConvertDomainNameToCString(&cname, cname_cstr); - CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr) - { - WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid); - } - } - } - } - else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED"); - } - } -#endif -#endif + queryrecord_result_reply(m, req, question, answer, AddRecord, error); } mDNSlocal void queryrecord_termination_callback(request_state *request) { LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP PID[%d](%s)", - request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), get_peer_pid(request->sd, pid_name), pid_name); + request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype), request->process_id, request->pid_name); if (request->u.queryrecord.q.QuestionContext) { mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q); // no need to error check + LogMcastQ(&mDNSStorage, &request->u.queryrecord.q, request, q_stop); request->u.queryrecord.q.QuestionContext = mDNSNULL; } else @@ -3088,6 +3554,7 @@ mDNSlocal void queryrecord_termination_callback(request_state *request) { LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c); mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2); + LogMcastQ(&mDNSStorage, request->u.queryrecord.q2, request, q_stop); } else { @@ -3103,6 +3570,50 @@ mDNSlocal void queryrecord_termination_callback(request_state *request) freeL("queryrecord Q2", request->u.queryrecord.q2); request->u.queryrecord.q2 = mDNSNULL; } +#if APPLE_OSX_mDNSResponder + { + if (request->u.queryrecord.ans) + { + DNSQuestion *v4q, *v6q; + // If we are receiving poisitive answers, provide the hint to the + // upper layer. + v4q = v6q = mDNSNULL; + if (request->u.queryrecord.q.qtype == kDNSType_A) + v4q = &request->u.queryrecord.q; + else if (request->u.queryrecord.q.qtype == kDNSType_AAAA) + v6q = &request->u.queryrecord.q; + mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q); + } + } +#endif // APPLE_OSX_mDNSResponder +} + +mDNSlocal void SetQuestionPolicy(DNSQuestion *q, request_state *req) +{ + int i; + + // The policy is either based on pid or UUID. Pass a zero pid + // to the "core" if the UUID is valid. If we always pass the pid, + // then the "core" needs to determine whether the uuid is valid + // by examining all the 16 bytes at the time of the policy + // check and also when setting the delegate socket option. Also, it + // requires that we zero out the uuid wherever the question is + // initialized to make sure that it is not interpreted as valid. + // To prevent these intrusive changes, just pass a zero pid to indicate + // that pid is not valid when uuid is valid. In future if we need the + // pid in the question, we will reevaluate this strategy. + if (req->validUUID) + { + for (i = 0; i < UUID_SIZE; i++) + { + q->uuid[i] = req->uuid[i]; + } + q->pid = 0; + } + else + { + q->pid = req->process_id; + } } mDNSlocal mStatus handle_queryrecord_request(request_state *request) @@ -3143,24 +3654,37 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable ) != 0; q->TimeoutQuestion = (flags & kDNSServiceFlagsTimeout ) != 0; q->WakeOnResolve = 0; - q->UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - q->ValidationRequired = 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->AnonInfo = mDNSNULL; q->QuestionCallback = queryrecord_result_callback; q->QuestionContext = request; q->SearchListIndex = 0; + q->DNSSECAuthInfo = mDNSNULL; + q->DAIFreeCallback = mDNSNULL; + + //Turn off dnssec validation for local domains and Question Types: RRSIG/ANY(ANY Type is not supported yet) + if ((IsLocalDomain(&q->qname)) || (q->qtype == kDNSServiceType_RRSIG) || (q->qtype == kDNSServiceType_ANY)) + q->ValidationRequired = 0; + // Don't append search domains for fully qualified domain names including queries // such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally // we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should // append search domains or not. So, we record that information in AppendSearchDomains. // - // We append search domains only for queries that are a single label. If overriden using - // command line argument "AlwaysAppendSearchDomains", then we do it for any query which - // is not fully qualified. + // We append search domains only for queries that are a single label. If overriden using command line + // argument "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. + // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. - if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' && - (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) + if ((!(q->ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(q->ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && (rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' && + (AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1)) { q->AppendSearchDomains = 1; q->AppendLocalSearchDomains = 1; @@ -3177,14 +3701,18 @@ mDNSlocal mStatus handle_queryrecord_request(request_state *request) // the cache q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0; q->qnameOrig = mDNSNULL; + SetQuestionPolicy(q, request); LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START PID[%d](%s)", - request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), get_peer_pid(request->sd, pid_name), pid_name); + request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype), request->process_id, request->pid_name); err = mDNS_StartQuery(&mDNSStorage, q); - if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); + + if (err) + LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err); else { request->terminate = queryrecord_termination_callback; + LogMcastQ(&mDNSStorage, q, request, q_start); if (callExternalHelpers(q->InterfaceID, &q->qname, flags)) { LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()"); @@ -3228,6 +3756,17 @@ mDNSlocal reply_state *format_enumeration_reply(request_state *request, mDNSlocal void enum_termination_callback(request_state *request) { + // Stop the domain enumeration queries to discover the WAB Browse/Registration domains + if (request->u.enumeration.flags & kDNSServiceFlagsRegistrationDomains) + { + LogInfo("%3d: DNSServiceEnumeration Cancel WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY); + } + else + { + LogInfo("%3d: DNSServiceEnumeration Cancel WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StopWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY); + } mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default); } @@ -3283,13 +3822,8 @@ mDNSlocal mStatus handle_enum_request(request_state *request) if (!request->msgptr) { LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); } - // allocate context structures - uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY); - -#if 0 - // mark which kind of enumeration we're doing so we can (de)authorize certain domains + // mark which kind of enumeration we're doing so that we know what domain enumeration queries to stop request->u.enumeration.flags = reg; -#endif // enumeration requires multiple questions, so we must link all the context pointers so that // necessary context can be reached from the callbacks @@ -3310,6 +3844,20 @@ mDNSlocal mStatus handle_enum_request(request_state *request) if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all); else request->terminate = enum_termination_callback; } + if (!err) + { + // Start the domain enumeration queries to discover the WAB Browse/Registration domains + if (reg) + { + LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Registration PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_REG_QUERY); + } + else + { + LogInfo("%3d: DNSServiceEnumerateDomains Start WAB Browse PID[%d](%s)", request->sd, request->process_id, request->pid_name); + uDNS_StartWABQueries(&mDNSStorage, UDNS_WAB_BROWSE_QUERY); + } + } return(err); } @@ -3370,7 +3918,7 @@ mDNSlocal mStatus handle_release_request(request_state *request) } LogOperation("%3d: PeerConnectionRelease(%X %##s) START PID[%d](%s)", - request->sd, flags, instance.c, get_peer_pid(request->sd, pid_name), pid_name); + request->sd, flags, instance.c, request->process_id, request->pid_name); external_connection_release(&instance); return(err); @@ -3413,17 +3961,121 @@ mDNSlocal void handle_getproperty_request(request_state *request) char prop[256]; if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0) { - LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop); - if (!strcmp(prop, kDNSServiceProperty_DaemonVersion)) + LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop); + if (!strcmp(prop, kDNSServiceProperty_DaemonVersion)) + { + DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) }; + send_all(request->sd, (const char *)&x, sizeof(x)); + return; + } + } + + // If we didn't recogize the requested property name, return BadParamErr + send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr)); +} + +#ifdef APPLE_OSX_mDNSResponder +// The caller can specify either the pid or the uuid. If the pid is not specified, +// update the effective uuid. Don't overwrite the pid which is used for debugging +// purposes and initialized when the socket is opened. +mDNSlocal void handle_connection_delegate_request(request_state *request) +{ + mDNSs32 pid; + socklen_t len; + + len = 0; + pid = get_uint32(&request->msgptr, request->msgend); +#ifdef LOCAL_PEEREPID + if (pid) + { + len = sizeof(pid); + if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREPID, &request->process_id, &len) != 0) + return; + // to extract the process name from the pid value + if (proc_pidinfo(request->process_id, PROC_PIDT_SHORTBSDINFO, 1, &proc, PROC_PIDT_SHORTBSDINFO_SIZE) == 0) + return; + mDNSPlatformStrCopy(request->pid_name, proc.pbsi_comm); + //LogMsg("handle_connection_delegate_request: process id %d, name %s", request->process_id, request->pid_name); + } +#endif +#ifdef LOCAL_PEEREUUID + if (!pid) + { + len = UUID_SIZE; + if (getsockopt(request->sd, SOL_LOCAL, LOCAL_PEEREUUID, request->uuid, &len) != 0) + return; + request->validUUID = mDNStrue; + } +#endif +} +#else +mDNSlocal void handle_connection_delegate_request(request_state *request) +{ + (void) request; +} +#endif + +typedef packedstruct +{ + mStatus err; + mDNSs32 pid; +} PIDInfo; + +mDNSlocal void handle_getpid_request(request_state *request) +{ + const request_state *req; + mDNSs32 pid = -1; + mDNSu16 srcport = get_uint16(&request->msgptr, request->msgend); + const DNSQuestion *q = NULL; + PIDInfo pi; + + LogOperation("%3d: DNSServiceGetPID START", request->sd); + + for (req = all_requests; req; req=req->next) + { + if (req->hdr.op == query_request) + q = &req->u.queryrecord.q; + else if (req->hdr.op == addrinfo_request) + q = &req->u.addrinfo.q4; + else if (req->hdr.op == addrinfo_request) + q = &req->u.addrinfo.q6; + + if (q && q->LocalSocket != NULL) + { + mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket); + if (port == srcport) + { + pid = req->process_id; + LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s] question %##s", htons(srcport), pid, req->pid_name, q->qname.c); + break; + } + } + } + // If we cannot find in the client requests, look to see if this was + // started by mDNSResponder. + if (pid == -1) + { + for (q = mDNSStorage.Questions; q; q = q->next) { - DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) }; - send_all(request->sd, (const char *)&x, sizeof(x)); - return; + if (q && q->LocalSocket != NULL) + { + mDNSu16 port = mDNSPlatformGetUDPPort(q->LocalSocket); + if (port == srcport) + { +#if APPLE_OSX_mDNSResponder + pid = getpid(); +#endif // APPLE_OSX_mDNSResponder + LogInfo("DNSServiceGetPID: srcport %d, pid %d [%s], question %##s", htons(srcport), pid, "_mDNSResponder", q->qname.c); + break; + } + } } } - - // If we didn't recogize the requested property name, return BadParamErr - send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr)); + + pi.err = 0; + pi.pid = pid; + send_all(request->sd, (const char *)&pi, sizeof(PIDInfo)); + LogOperation("%3d: DNSServiceGetPID STOP", request->sd); } // *************************************************************************** @@ -3439,11 +4091,11 @@ mDNSlocal void port_mapping_termination_callback(request_state *request) LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP PID[%d](%s)", request->sd, DNSServiceProtocol(request->u.pm.NATinfo.Protocol), mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - get_peer_pid(request->sd, pid_name), pid_name); + request->process_id, request->pid_name); mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo); } -// Called via function pointer when we get a NAT-PMP address request or port mapping response +// Called via function pointer when we get a NAT Traversal (address request or port mapping) response mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n) { request_state *request = (request_state *)n->clientContext; @@ -3530,7 +4182,7 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START PID[%d](%s)", request->sd, protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease, - get_peer_pid(request->sd, pid_name), pid_name); + request->process_id, request->pid_name); err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo); if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err); else request->terminate = port_mapping_termination_callback; @@ -3547,11 +4199,12 @@ mDNSlocal mStatus handle_port_mapping_request(request_state *request) mDNSlocal void addrinfo_termination_callback(request_state *request) { LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP PID[%d](%s)", request->sd, request->u.addrinfo.q4.qname.c, - get_peer_pid(request->sd, pid_name), pid_name); + request->process_id, request->pid_name); if (request->u.addrinfo.q4.QuestionContext) { mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_stop); request->u.addrinfo.q4.QuestionContext = mDNSNULL; } if (request->u.addrinfo.q4.qnameOrig) @@ -3565,6 +4218,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) { LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c); mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42); + LogMcastQ(&mDNSStorage, request->u.addrinfo.q42, request, q_stop); } if (request->u.addrinfo.q42->qnameOrig) { @@ -3579,6 +4233,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) if (request->u.addrinfo.q6.QuestionContext) { mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_stop); request->u.addrinfo.q6.QuestionContext = mDNSNULL; } if (request->u.addrinfo.q6.qnameOrig) @@ -3592,6 +4247,7 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) { LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c); mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62); + LogMcastQ(&mDNSStorage, request->u.addrinfo.q62, request, q_stop); } if (request->u.addrinfo.q62->qnameOrig) { @@ -3602,6 +4258,36 @@ mDNSlocal void addrinfo_termination_callback(request_state *request) freeL("addrinfo Q62", request->u.addrinfo.q62); request->u.addrinfo.q62 = mDNSNULL; } +#if APPLE_OSX_mDNSResponder + { + DNSQuestion *v4q, *v6q; + v4q = v6q = mDNSNULL; + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) + { + // If we are not delivering answers, we may be timing out prematurely. + // Note down the current state so that we know to retry when we see a + // valid response again. + if (request->u.addrinfo.q4.TimeoutQuestion && !request->u.addrinfo.v4ans) + { + mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q4); + } + // If we have a v4 answer and if we timed out prematurely before, provide + // a trigger to the upper layer so that it can retry questions if needed. + if (request->u.addrinfo.v4ans) + v4q = &request->u.addrinfo.q4; + } + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) + { + if (request->u.addrinfo.q6.TimeoutQuestion && !request->u.addrinfo.v6ans) + { + mDNSPlatformUpdateDNSStatus(&mDNSStorage, &request->u.addrinfo.q6); + } + if (request->u.addrinfo.v6ans) + v6q = &request->u.addrinfo.q6; + } + mDNSPlatformTriggerDNSRetry(&mDNSStorage, v4q, v6q); + } +#endif // APPLE_OSX_mDNSResponder } mDNSlocal mStatus handle_addrinfo_request(request_state *request) @@ -3650,48 +4336,33 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) 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.UseBrackgroundTrafficClass = request->u.addrinfo.q6.UseBrackgroundTrafficClass = (flags & kDNSServiceFlagsBackgroundTrafficClass) != 0; - request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 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; + else if ((flags & kDNSServiceFlagsValidateOptional) != 0) + request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = DNSSEC_VALIDATION_SECURE_OPTIONAL; request->u.addrinfo.q4.ValidatingResponse = request->u.addrinfo.q6.ValidatingResponse = 0; - request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL; + request->u.addrinfo.q4.ProxyQuestion = request->u.addrinfo.q6.ProxyQuestion = 0; + request->u.addrinfo.q4.qnameOrig = request->u.addrinfo.q6.qnameOrig = mDNSNULL; + request->u.addrinfo.q4.AnonInfo = request->u.addrinfo.q6.AnonInfo = mDNSNULL; - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) - { - request->u.addrinfo.q4.qtype = kDNSServiceType_A; - request->u.addrinfo.q4.SearchListIndex = 0; + SetQuestionPolicy(&request->u.addrinfo.q4, request); + SetQuestionPolicy(&request->u.addrinfo.q6, request); - // We append search domains only for queries that are a single label. If overriden using - // command line argument "AlwaysAppendSearchDomains", then we do it for any query which - // is not fully qualified. - if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) - { - request->u.addrinfo.q4.AppendSearchDomains = 1; - request->u.addrinfo.q4.AppendLocalSearchDomains = 1; - } - else - { - request->u.addrinfo.q4.AppendSearchDomains = 0; - request->u.addrinfo.q4.AppendLocalSearchDomains = 0; - } - request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0); - request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback; - request->u.addrinfo.q4.QuestionContext = request; - err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4); - if (err != mStatus_NoError) - { - LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); - request->u.addrinfo.q4.QuestionContext = mDNSNULL; - } - #if APPLE_OSX_mDNSResponder - err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err); - #endif // APPLE_OSX_mDNSResponder - } + request->u.addrinfo.q4.DNSSECAuthInfo = request->u.addrinfo.q6.DNSSECAuthInfo = mDNSNULL; + request->u.addrinfo.q4.DAIFreeCallback = request->u.addrinfo.q6.DAIFreeCallback = mDNSNULL; + + //Turn off dnssec validation for local domains + if (IsLocalDomain(&d)) + request->u.addrinfo.q4.ValidationRequired = request->u.addrinfo.q6.ValidationRequired = 0; - if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6)) + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) { request->u.addrinfo.q6.qtype = kDNSServiceType_AAAA; request->u.addrinfo.q6.SearchListIndex = 0; - if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) + // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set + if ((!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q6.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) { request->u.addrinfo.q6.AppendSearchDomains = 1; request->u.addrinfo.q6.AppendLocalSearchDomains = 1; @@ -3709,23 +4380,64 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) { LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); request->u.addrinfo.q6.QuestionContext = mDNSNULL; - if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4) - { - // If we started a query for IPv4, we need to cancel it - mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4); - request->u.addrinfo.q4.QuestionContext = mDNSNULL; - } } #if APPLE_OSX_mDNSResponder err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err); #endif // APPLE_OSX_mDNSResponder + if (!err) + { + request->terminate = addrinfo_termination_callback; + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q6, request, q_start); + } } - LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", - request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c, get_peer_pid(request->sd, pid_name), pid_name); + if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)) + { + request->u.addrinfo.q4.qtype = kDNSServiceType_A; + request->u.addrinfo.q4.SearchListIndex = 0; + + // We append search domains only for queries that are a single label. If overriden using cmd line arg + // "AlwaysAppendSearchDomains", then we do it for any query which is not fully qualified. + // For DNSSEC questions, append search domains only if kDNSServiceFlagsValidateOptional is set. - if (!err) request->terminate = addrinfo_termination_callback; + if ((!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_SECURE)) && (!(request->u.addrinfo.q4.ValidationRequired == DNSSEC_VALIDATION_INSECURE)) + && hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1)) + { + request->u.addrinfo.q4.AppendSearchDomains = 1; + request->u.addrinfo.q4.AppendLocalSearchDomains = 1; + } + else + { + request->u.addrinfo.q4.AppendSearchDomains = 0; + request->u.addrinfo.q4.AppendLocalSearchDomains = 0; + } + request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0); + request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback; + request->u.addrinfo.q4.QuestionContext = request; + err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4); + if (err != mStatus_NoError) + { + LogMsg("ERROR: mDNS_StartQuery: %d", (int)err); + request->u.addrinfo.q4.QuestionContext = mDNSNULL; + if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6) + { + // If we started a query for IPv6, we need to cancel it + mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6); + request->u.addrinfo.q6.QuestionContext = mDNSNULL; + } + } + #if APPLE_OSX_mDNSResponder + err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err); + #endif // APPLE_OSX_mDNSResponder + if (!err) + { + request->terminate = addrinfo_termination_callback; + LogMcastQ(&mDNSStorage, &request->u.addrinfo.q4, request, q_start); + } + } + LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START PID[%d](%s)", request->sd, flags, interfaceIndex, + request->u.addrinfo.protocol, d.c, request->process_id, request->pid_name); return(err); } @@ -3738,9 +4450,11 @@ mDNSlocal mStatus handle_addrinfo_request(request_state *request) mDNSlocal request_state *NewRequest(void) { request_state **p = &all_requests; - while (*p) p=&(*p)->next; + while (*p) + p=&(*p)->next; *p = mallocL("request_state", sizeof(request_state)); - if (!*p) FatalError("ERROR: malloc"); + if (!*p) + FatalError("ERROR: malloc"); mDNSPlatformMemZero(*p, sizeof(request_state)); return(*p); } @@ -3808,7 +4522,7 @@ mDNSlocal void read_msg(request_state *req) struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put struct msghdr msg; struct cmsghdr *cmsg; - char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))]; + char cbuf[CMSG_SPACE(4 * sizeof(dnssd_sock_t))]; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &vec; @@ -3831,9 +4545,8 @@ mDNSlocal void read_msg(request_state *req) LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS); LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type); #endif // DEBUG_64BIT_SCM_RIGHTS - if (msg.msg_controllen == sizeof(cbuf) && - cmsg->cmsg_len == CMSG_LEN(sizeof(dnssd_sock_t)) && - cmsg->cmsg_level == SOL_SOCKET && + if (msg.msg_controllen != 0 && + cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { #if APPLE_OSX_mDNSResponder @@ -3843,7 +4556,7 @@ mDNSlocal void read_msg(request_state *req) if (req->hdr.op == send_bpf) { dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg); - LogOperation("%3d: Got BPF %d", req->sd, x); + LogOperation("%3d: Got len %d, BPF %d", req->sd, cmsg->cmsg_len, x); mDNSPlatformReceiveBPF_fd(&mDNSStorage, x); } else @@ -3855,7 +4568,7 @@ mDNSlocal void read_msg(request_state *req) if (req->data_bytes < req->hdr.datalen) { LogMsg("%3d: Client(PID [%d](%s)) sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d", - req->sd, get_peer_pid(req->sd, pid_name), pid_name, req->errsd, req->data_bytes, req->hdr.datalen); + req->sd, req->process_id, req->pid_name, req->errsd, req->data_bytes, req->hdr.datalen); req->ts = t_error; return; } @@ -3963,45 +4676,68 @@ mDNSlocal void request_callback(int fd, short filter, void *info) for (;;) { read_msg(req); - if (req->ts == t_morecoming) return; - if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; } - if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; } - + if (req->ts == t_morecoming) + return; + if (req->ts == t_terminated || req->ts == t_error) + { + AbortUnlinkAndFree(req); + return; + } + if (req->ts != t_complete) + { + LogMsg("request_callback: req->ts %d != t_complete PID[%d][%s]", req->ts, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; + } if (req->hdr.version != VERSION) { - LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION); + LogMsg("request_callback: ERROR: client IPC version %d incompatible with daemon IPC version %d PID[%d][%s]", + req->hdr.version, VERSION, req->process_id, req->pid_name); AbortUnlinkAndFree(req); return; } switch(req->hdr.op) // Interface + other data { - case connection_request: min_size = 0; break; - case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break; - case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break; - case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break; - case remove_record_request: break; - case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break; - case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; - case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break; - case enumeration_request: min_size += sizeof(mDNSu32); break; - case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break; - case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break; - case setdomain_request: min_size += 1 /* domain */; break; - case getproperty_request: min_size = 2; break; - case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break; - case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break; - case send_bpf: // Same as cancel_request below - case cancel_request: min_size = 0; break; - case release_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; - default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1; break; + case connection_request: min_size = 0; break; + case connection_delegate_request: min_size = 4; /* pid */ break; + case reg_service_request: min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break; + case add_record_request: min_size += 4 /* type, rdlen */ + 4 /* ttl */; break; + case update_record_request: min_size += 2 /* rdlen */ + 4 /* ttl */; break; + case remove_record_request: break; + case browse_request: min_size += sizeof(mDNSu32) + 2 /* type, domain */; break; + case resolve_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; + case query_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 4 /* type, class*/; break; + case enumeration_request: min_size += sizeof(mDNSu32); break; + case reg_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */; break; + case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */; break; + case setdomain_request: min_size += 1 /* domain */; break; + case getproperty_request: min_size = 2; break; + case getpid_request: min_size = 2; break; + case port_mapping_request: min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */ + 4 /* ttl */; break; + case addrinfo_request: min_size += sizeof(mDNSu32) + 4 /* v4/v6 */ + 1 /* hostname */; break; + case send_bpf: // Same as cancel_request below + case cancel_request: min_size = 0; break; + case release_request: min_size += sizeof(mDNSu32) + 3 /* type, type, domain */; break; + default: LogMsg("request_callback: ERROR: validate_message - unsupported req type: %d PID[%d][%s]", + req->hdr.op, req->process_id, req->pid_name); + min_size = -1; break; } if ((mDNSs32)req->data_bytes < min_size) - { LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; } - + { + LogMsg("request_callback: Invalid message %d bytes; min for %d is %d PID[%d][%s]", + req->data_bytes, req->hdr.op, min_size, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; + } if (LightweightOp(req->hdr.op) && !req->terminate) - { LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op); AbortUnlinkAndFree(req); return; } + { + LogMsg("request_callback: Reg/Add/Update/Remove %d require existing connection PID[%d][%s]", + req->hdr.op, req->process_id, req->pid_name); + AbortUnlinkAndFree(req); + return; + } // check if client wants silent operation if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1; @@ -4018,6 +4754,28 @@ mDNSlocal void request_callback(int fd, short filter, void *info) newreq->msgbuf = req->msgbuf; newreq->msgptr = req->msgptr; newreq->msgend = req->msgend; + // if the parent request is a delegate connection, copy the + // relevant bits + if (req->validUUID) + { + int i; + newreq->validUUID = mDNStrue; + for (i = 0; i < UUID_SIZE; i++) + { + newreq->uuid[i] = req->uuid[i]; + } + } + else + { + if (req->process_id) + { + newreq->process_id = req->process_id; + } + else + { + set_peer_pid(newreq); + } + } req = newreq; } @@ -4027,40 +4785,52 @@ mDNSlocal void request_callback(int fd, short filter, void *info) { err = mStatus_ServiceNotRunning; } - else switch(req->hdr.op) + else + { + switch(req->hdr.op) { - // These are all operations that have their own first-class request_state object - case connection_request: LogOperation("%3d: DNSServiceCreateConnection START PID[%d](%s)", - req->sd, get_peer_pid(req->sd, pid_name), pid_name); - req->terminate = connection_termination; break; - case resolve_request: err = handle_resolve_request (req); break; - case query_request: err = handle_queryrecord_request (req); break; - case browse_request: err = handle_browse_request (req); break; - case reg_service_request: err = handle_regservice_request (req); break; - case enumeration_request: err = handle_enum_request (req); break; - case reconfirm_record_request: err = handle_reconfirm_request (req); break; - case setdomain_request: err = handle_setdomain_request (req); break; - case getproperty_request: handle_getproperty_request (req); break; - case port_mapping_request: err = handle_port_mapping_request(req); break; - case addrinfo_request: err = handle_addrinfo_request (req); break; - case send_bpf: /* Do nothing for send_bpf */ break; - - // These are all operations that work with an existing request_state object - case reg_record_request: err = handle_regrecord_request (req); break; - case add_record_request: err = handle_add_request (req); break; - case update_record_request: err = handle_update_request (req); break; - case remove_record_request: err = handle_removerecord_request(req); break; - case cancel_request: handle_cancel_request (req); break; - case release_request: err = handle_release_request (req); break; - default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op); + // These are all operations that have their own first-class request_state object + case connection_request: + LogOperation("%3d: DNSServiceCreateConnection START PID[%d](%s)", + req->sd, req->process_id, req->pid_name); + req->terminate = connection_termination; + break; + case connection_delegate_request: + LogOperation("%3d: DNSServiceCreateDelegateConnection START PID[%d](%s)", + req->sd, req->process_id, req->pid_name); + req->terminate = connection_termination; + handle_connection_delegate_request(req); + break; + case resolve_request: err = handle_resolve_request (req); break; + case query_request: err = handle_queryrecord_request (req); break; + case browse_request: err = handle_browse_request (req); break; + case reg_service_request: err = handle_regservice_request (req); break; + case enumeration_request: err = handle_enum_request (req); break; + case reconfirm_record_request: err = handle_reconfirm_request (req); break; + case setdomain_request: err = handle_setdomain_request (req); break; + case getproperty_request: handle_getproperty_request (req); break; + case getpid_request: handle_getpid_request (req); break; + case port_mapping_request: err = handle_port_mapping_request(req); break; + case addrinfo_request: err = handle_addrinfo_request (req); break; + case send_bpf: /* Do nothing for send_bpf */ break; + + // These are all operations that work with an existing request_state object + case reg_record_request: err = handle_regrecord_request (req); break; + case add_record_request: err = handle_add_request (req); break; + case update_record_request: err = handle_update_request (req); break; + case remove_record_request: err = handle_removerecord_request(req); break; + case cancel_request: handle_cancel_request (req); break; + case release_request: err = handle_release_request (req); break; + default: LogMsg("request_callback: %3d:ERROR: Unsupported UDS req:%d PID[%d][%s]", + req->sd, req->hdr.op, req->process_id, req->pid_name); break; } - + } // req->msgbuf may be NULL, e.g. for connection_request or remove_record_request if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf); // There's no return data for a cancel request (DNSServiceRefDeallocate returns no result) // For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here - if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf) + if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf && req->hdr.op != getpid_request) { const mStatus err_netorder = dnssd_htonl(err); send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder)); @@ -4127,6 +4897,7 @@ mDNSlocal void connect_callback(int fd, short filter, void *info) request->ts = t_morecoming; request->sd = sd; request->errsd = sd; + set_peer_pid(request); #if APPLE_OSX_mDNSResponder struct xucred x; socklen_t xucredlen = sizeof(x); @@ -4173,8 +4944,11 @@ mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt) my_perror("ERROR: could not add listen socket to event loop"); return mDNSfalse; } - else LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt); - + else + { + LogMsg("%3d: Listening for incoming Unix Domain Socket client requests", skt); + mDNSStorage.uds_listener_skt = skt; + } return mDNStrue; } @@ -4184,7 +4958,7 @@ mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count) int ret; mDNSu32 i = 0; - LogInfo("udsserver_init"); + LogInfo("udsserver_init: %d %d", _DNS_SD_H/10000, mDNSStorage.mDNS_plat); // If a particular platform wants to opt out of having a PID file, define PID_FILE to be "" if (PID_FILE[0]) @@ -4331,13 +5105,13 @@ mDNSexport int udsserver_exit(void) return 0; } -mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req) +mDNSlocal void LogClientInfo(mDNS *const m, request_state *req) { char prefix[16]; - if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> "); - else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); - - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + if (req->primary) + mDNS_snprintf(prefix, sizeof(prefix), " -> "); + else + mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd); if (!req->terminate) LogMsgNoIdent("%s No operation yet on this socket", prefix); @@ -4345,59 +5119,198 @@ mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req) { int num_records = 0, num_ops = 0; const registered_record_entry *p; - const request_state *r; + request_state *r; for (p = req->u.reg_recs; p; p=p->next) num_records++; for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++; LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s PID[%d](%s)", - prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", get_peer_pid(req->sd, pid_name), pid_name); + prefix, num_records, num_records != 1 ? "s" : "", num_ops, num_ops != 1 ? "s" : "", + req->process_id, req->pid_name); for (p = req->u.reg_recs; p; p=p->next) - LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s PID[%d](%s)", p->key, ARDisplayString(m, p->rr), get_peer_pid(req->sd, pid_name), pid_name); + LogMsgNoIdent(" -> DNSServiceRegisterRecord %3d %s PID[%d](%s)", p->key, ARDisplayString(m, p->rr), + req->process_id, req->pid_name); for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r); } else if (req->terminate == regservice_termination_callback) { service_instance *ptr; + char anonstr[256]; for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) - LogMsgNoIdent("%s DNSServiceRegister %##s %u/%u PID[%d](%s)", - (ptr == req->u.servicereg.instances) ? prefix : " ", ptr->srs.RR_SRV.resrec.name->c, - mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs), get_peer_pid(req->sd, pid_name), pid_name); + LogMsgNoIdent("%s DNSServiceRegister %##s%s %u/%u PID[%d](%s)", + (ptr == req->u.servicereg.instances) ? prefix : " ", ptr->srs.RR_SRV.resrec.name->c, + AnonDataToString(ptr->srs.AnonData, 0, anonstr, sizeof(anonstr)), mDNSVal16(req->u.servicereg.port), + SRS_PORT(&ptr->srs), req->process_id, req->pid_name); } else if (req->terminate == browse_termination_callback) { browser_t *blist; + char anonstr[256]; for (blist = req->u.browser.browsers; blist; blist = blist->next) - LogMsgNoIdent("%s DNSServiceBrowse %##s PID[%d](%s)", - (blist == req->u.browser.browsers) ? prefix : " ",blist->q.qname.c, get_peer_pid(req->sd, pid_name), pid_name); + LogMsgNoIdent("%s DNSServiceBrowse %##s%s PID[%d](%s)", + (blist == req->u.browser.browsers) ? prefix : " ",blist->q.qname.c, + AnonDataToString(req->u.browser.AnonData, 0, anonstr, sizeof(anonstr)), req->process_id, req->pid_name); } else if (req->terminate == resolve_termination_callback) LogMsgNoIdent("%s DNSServiceResolve %##s PID[%d](%s)", - prefix, req->u.resolve.qsrv.qname.c, get_peer_pid(req->sd, pid_name), pid_name); + prefix, req->u.resolve.qsrv.qname.c, req->process_id, req->pid_name); else if (req->terminate == queryrecord_termination_callback) LogMsgNoIdent("%s DNSServiceQueryRecord %##s (%s) PID[%d](%s)", - prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), get_peer_pid(req->sd, pid_name), pid_name); + prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), req->process_id, req->pid_name); else if (req->terminate == enum_termination_callback) - LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s PID[%d](%s)", prefix, req->u.enumeration.q_all.qname.c, get_peer_pid(req->sd, pid_name), pid_name); + LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s PID[%d](%s)", prefix, req->u.enumeration.q_all.qname.c, + req->process_id, req->pid_name); else if (req->terminate == port_mapping_termination_callback) - LogMsgNoIdent("%s DNSServiceNATPortMapping %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d PID[%d](%s)", + LogMsgNoIdent("%s DNSServiceNATPortMapping %s%s Int %5d Req %5d Ext %.4a:%5d Req TTL %5d Granted TTL %5d PID[%d](%s)", prefix, - &req->u.pm.NATinfo.ExternalAddress, req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : " ", req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : " ", mDNSVal16(req->u.pm.NATinfo.IntPort), mDNSVal16(req->u.pm.ReqExt), + &req->u.pm.NATinfo.ExternalAddress, mDNSVal16(req->u.pm.NATinfo.ExternalPort), req->u.pm.NATinfo.NATLease, req->u.pm.NATinfo.Lifetime, - get_peer_pid(req->sd, pid_name), pid_name); + req->process_id, req->pid_name); else if (req->terminate == addrinfo_termination_callback) LogMsgNoIdent("%s DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)", prefix, req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", - req->u.addrinfo.q4.qname.c, get_peer_pid(req->sd, pid_name), pid_name); + req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name); else LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate); } +mDNSlocal void GetMcastClients(request_state *req) +{ + if (req->terminate == connection_termination) + { + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + request_state *r; + for (p = req->u.reg_recs; p; p=p->next) + num_records++; + for (r = req->next; r; r=r->next) + if (r->primary == req) + num_ops++; + for (p = req->u.reg_recs; p; p=p->next) + { + if (!AuthRecord_uDNS(p->rr)) + n_mrecords++; + } + for (r = req->next; r; r=r->next) + if (r->primary == req) + GetMcastClients(r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + { + if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) + n_mrecords++; + } + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->u.browser.browsers; blist; blist = blist->next) + { + if (mDNSOpaque16IsZero(blist->q.TargetQID)) + n_mquests++; + } + } + else if (req->terminate == resolve_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0)) + n_mquests++; + } + else if (req->terminate == queryrecord_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0)) + n_mquests++; + } + else if (req->terminate == addrinfo_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0)) + n_mquests++; + } + else + { + return; + } +} + + +mDNSlocal void LogMcastClientInfo(request_state *req) +{ + if (!req->terminate) + LogMcastNoIdent("No operation yet on this socket"); + else if (req->terminate == connection_termination) + { + int num_records = 0, num_ops = 0; + const registered_record_entry *p; + request_state *r; + for (p = req->u.reg_recs; p; p=p->next) + num_records++; + for (r = req->next; r; r=r->next) + if (r->primary == req) + num_ops++; + for (p = req->u.reg_recs; p; p=p->next) + { + if (!AuthRecord_uDNS(p->rr)) + LogMcastNoIdent("R: -> DNSServiceRegisterRecord: %##s %s PID[%d](%s)", p->rr->resrec.name->c, + DNSTypeName(p->rr->resrec.rrtype), req->process_id, req->pid_name, i_mcount++); + } + for (r = req->next; r; r=r->next) + if (r->primary == req) + LogMcastClientInfo(r); + } + else if (req->terminate == regservice_termination_callback) + { + service_instance *ptr; + for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next) + { + if (!AuthRecord_uDNS(&ptr->srs.RR_SRV)) + LogMcastNoIdent("R: DNSServiceRegister: %##s %u/%u PID[%d](%s)", ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), + SRS_PORT(&ptr->srs), req->process_id, req->pid_name, i_mcount++); + } + } + else if (req->terminate == browse_termination_callback) + { + browser_t *blist; + for (blist = req->u.browser.browsers; blist; blist = blist->next) + { + if (mDNSOpaque16IsZero(blist->q.TargetQID)) + LogMcastNoIdent("Q: DNSServiceBrowse %##s %s PID[%d](%s)", blist->q.qname.c, DNSTypeName(blist->q.qtype), + req->process_id, req->pid_name, i_mcount++); + } + } + else if (req->terminate == resolve_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.resolve.qsrv.TargetQID)) && (req->u.resolve.qsrv.ThisQInterval > 0)) + LogMcastNoIdent("Q: DNSServiceResolve %##s %s PID[%d](%s)", req->u.resolve.qsrv.qname.c, DNSTypeName(req->u.resolve.qsrv.qtype), + req->process_id, req->pid_name, i_mcount++); + } + else if (req->terminate == queryrecord_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.queryrecord.q.TargetQID)) && (req->u.queryrecord.q.ThisQInterval > 0)) + LogMcastNoIdent("Q: DNSServiceQueryRecord %##s %s PID[%d](%s)", req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype), + req->process_id, req->pid_name, i_mcount++); + } + else if (req->terminate == addrinfo_termination_callback) + { + if ((mDNSOpaque16IsZero(req->u.addrinfo.q4.TargetQID)) && (req->u.addrinfo.q4.ThisQInterval > 0)) + LogMcastNoIdent("Q: DNSServiceGetAddrInfo %s%s %##s PID[%d](%s)", + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : " ", + req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : " ", + req->u.addrinfo.q4.qname.c, req->process_id, req->pid_name, i_mcount++); + } + else + { + return; + } + +} + mDNSlocal char *RecordTypeName(mDNSu8 rtype) { switch (rtype) @@ -4444,7 +5357,6 @@ mDNSlocal void LogEtcHosts(mDNS *const m) LogMsgNoIdent(" %s %u %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar)); } } - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); } } @@ -4472,13 +5384,44 @@ mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m) LogMsgNoIdent(" %s LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); else if (ar->ARType == AuthRecordP2P) LogMsgNoIdent(" %s PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar)); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); } } if (showheader) LogMsgNoIdent(""); } +mDNSlocal char *AnonInfoToString(AnonymousInfo *ai, char *anonstr, int anstrlen) +{ + anonstr[0] = 0; + if (ai && ai->AnonData) + { + return (AnonDataToString(ai->AnonData, ai->AnonDataLen, anonstr, anstrlen)); + } + return anonstr; +} + +mDNSlocal void LogOneAuthRecord(mDNS *const m, const AuthRecord *ar, mDNSs32 now, const char *const ifname) +{ + char anstr[256]; + if (AuthRecord_uDNS(ar)) + { + LogMsgNoIdent("%7d %7d %7d %7d %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, + ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, + ar->state, ARDisplayString(m, ar)); + } + else + { + LogMsgNoIdent("%7d %7d %7d %7s %s%s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, + ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, + ifname ? ifname : "ALL", + ARDisplayString(m, ar), AnonInfoToString(ar->resrec.AnonInfo, anstr, sizeof(anstr))); + } +} + mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy) { mDNSBool showheader = mDNStrue; @@ -4502,28 +5445,152 @@ mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *Reso LogMsgNoIdent("Proxying for %.6a seq %d", &owner.HMAC, owner.seq); } if (AuthRecord_uDNS(ar)) - LogMsgNoIdent("%7d %7d %7d %7d %s", - ar->ThisAPInterval / mDNSPlatformOneSecond, - (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond, - ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0, - ar->state, ARDisplayString(m, ar)); + { + LogOneAuthRecord(m, ar, now, ifname); + } else if (ar->ARType == AuthRecordLocalOnly) + { LogMsgNoIdent(" LO %s", ARDisplayString(m, ar)); + } else if (ar->ARType == AuthRecordP2P) + { LogMsgNoIdent(" PP %s", ARDisplayString(m, ar)); + } else - LogMsgNoIdent("%7d %7d %7d %7s %s", - ar->ThisAPInterval / mDNSPlatformOneSecond, - ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, - ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, - ifname ? ifname : "ALL", - ARDisplayString(m, ar)); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + { + LogOneAuthRecord(m, ar, now, ifname); + if (ar->resrec.AnonInfo) + { + ResourceRecord *nsec3 = ar->resrec.AnonInfo->nsec3RR; + // We just print the values from the AuthRecord to keep it nicely aligned though + // all we want here is the nsec3 information. + LogMsgNoIdent("%7d %7d %7d %7s %s", + ar->ThisAPInterval / mDNSPlatformOneSecond, + ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0, + ar->TimeExpire ? (ar->TimeExpire - now) / mDNSPlatformOneSecond : 0, + ifname ? ifname : "ALL", + RRDisplayString(m, nsec3)); + } + } } } if (showheader) LogMsgNoIdent(""); } +mDNSlocal void PrintOneCacheRecord(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) +{ + LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", + slot, + cr->CRActiveQuestion ? "*" : " ", + remain, + ifname ? ifname : "-U-", + (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : + (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", + DNSTypeName(cr->resrec.rrtype), + CRDisplayString(m, cr)); + (*CacheUsed)++; +} + +mDNSlocal void PrintCachedRecords(mDNS *const m, const CacheRecord *cr, mDNSu32 slot, const mDNSu32 remain, const char *ifname, mDNSu32 *CacheUsed) +{ + CacheRecord *nsec; + CacheRecord *soa; + nsec = cr->nsec; + + // The records that are cached under the main cache record like nsec, soa don't have + // their own lifetime. If the main cache record expires, they also expire. + while (nsec) + { + PrintOneCacheRecord(m, nsec, slot, remain, ifname, CacheUsed); + nsec = nsec->next; + } + soa = cr->soa; + if (soa) + { + PrintOneCacheRecord(m, soa, slot, remain, ifname, CacheUsed); + } + if (cr->resrec.AnonInfo) + { + ResourceRecord *nsec3 = cr->resrec.AnonInfo->nsec3RR; + // Even though it is a resource record, we print the sameway + // as a cache record so that it aligns properly. + if (nsec3) + { + LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", + slot, + " ", + remain, + ifname ? ifname : "-U-", + (nsec3->RecordType == kDNSRecordTypePacketNegative) ? "-" : + (nsec3->RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", + DNSTypeName(nsec3->rrtype), + RRDisplayString(m, nsec3)); + } + } +} + +mDNSlocal char *AnonDataToString(const mDNSu8 *ad, int adlen, char *adstr, int adstrlen) +{ + adstr[0] = 0; + if (ad) + { + int len; + char *orig = adstr; + + // If the caller is lazy to compute the length, we do it for them. + if (!adlen) + len = strlen((const char *)ad); + else + len = adlen; + + // Print the anondata within brackets. Hence, we need space for two + // brackets and a NULL byte. + if (len > (adstrlen - 3)) + len = adstrlen - 3; + + *adstr++ = '('; + mDNSPlatformMemCopy(adstr, ad, len); + adstr[len] = ')'; + adstr[len+1] = 0; + return orig; + } + return adstr; +} + +mDNSexport void LogMDNSStatistics(mDNS *const m) +{ + LogMsgNoIdent("--- MDNS Statistics ---"); + + LogMsgNoIdent("Name Conflicts %u", m->mDNSStats.NameConflicts); + LogMsgNoIdent("KnownUnique Name Conflicts %u", m->mDNSStats.KnownUniqueNameConflicts); + LogMsgNoIdent("Duplicate Query Suppressions %u", m->mDNSStats.DupQuerySuppressions); + LogMsgNoIdent("KA Suppressions %u", m->mDNSStats.KnownAnswerSuppressions); + LogMsgNoIdent("KA Multiple Packets %u", m->mDNSStats.KnownAnswerMultiplePkts); + LogMsgNoIdent("Poof Cache Deletions %u", m->mDNSStats.PoofCacheDeletions); + LogMsgNoIdent("--------------------------------"); + + LogMsgNoIdent("Multicast packets Sent %u", m->MulticastPacketsSent); + LogMsgNoIdent("Multicast packets Received %u", m->MPktNum); + LogMsgNoIdent("Remote Subnet packets %u", m->RemoteSubnet); + LogMsgNoIdent("QU questions received %u", m->mDNSStats.UnicastBitInQueries); + LogMsgNoIdent("Normal multicast questions %u", m->mDNSStats.NormalQueries); + LogMsgNoIdent("Answers for questions %u", m->mDNSStats.MatchingAnswersForQueries); + LogMsgNoIdent("Unicast responses %u", m->mDNSStats.UnicastResponses); + LogMsgNoIdent("Multicast responses %u", m->mDNSStats.MulticastResponses); + LogMsgNoIdent("Unicast response Demotions %u", m->mDNSStats.UnicastDemotedToMulticast); + LogMsgNoIdent("--------------------------------"); + + LogMsgNoIdent("Sleeps %u", m->mDNSStats.Sleeps); + LogMsgNoIdent("Wakeups %u", m->mDNSStats.Wakes); + LogMsgNoIdent("Interface UP events %u", m->mDNSStats.InterfaceUp); + LogMsgNoIdent("Interface UP Flap events %u", m->mDNSStats.InterfaceUpFlap); + LogMsgNoIdent("Interface Down events %u", m->mDNSStats.InterfaceDown); + LogMsgNoIdent("Interface DownFlap events %u", m->mDNSStats.InterfaceDownFlap); + LogMsgNoIdent("Cache refresh queries %u", m->mDNSStats.CacheRefreshQueries); + LogMsgNoIdent("Cache refreshed %u", m->mDNSStats.CacheRefreshed); + LogMsgNoIdent("Wakeup on Resolves %u", m->mDNSStats.WakeOnResolves); +} + mDNSexport void udsserver_info(mDNS *const m) { const mDNSs32 now = mDNS_TimeNow(m); @@ -4540,6 +5607,7 @@ mDNSexport void udsserver_info(mDNS *const m) LogMsgNoIdent("------------ Cache -------------"); LogMsgNoIdent("Slt Q TTL if U Type rdlen"); for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) + { for (cg = m->rrcache_hash[slot]; cg; cg=cg->next) { CacheUsed++; // Count one cache entity for the CacheGroup object @@ -4547,40 +5615,16 @@ mDNSexport void udsserver_info(mDNS *const m) { const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond; const char *ifname; - CacheRecord *nsec; mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID; if (!InterfaceID && cr->resrec.rDNSServer && cr->resrec.rDNSServer->scoped) InterfaceID = cr->resrec.rDNSServer->interface; ifname = InterfaceNameForID(m, InterfaceID); - CacheUsed++; if (cr->CRActiveQuestion) CacheActive++; - LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", - slot, - cr->CRActiveQuestion ? "*" : " ", - remain, - ifname ? ifname : "-U-", - (cr->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : - (cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", - DNSTypeName(cr->resrec.rrtype), - CRDisplayString(m, cr)); - nsec = cr->nsec; - while (nsec) - { - LogMsgNoIdent("%3d %s%8d %-7s%s %-6s%s", - slot, - nsec->CRActiveQuestion ? "*" : " ", - remain, - ifname ? ifname : "-U-", - (nsec->resrec.RecordType == kDNSRecordTypePacketNegative) ? "-" : - (nsec->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+", - DNSTypeName(nsec->resrec.rrtype), - CRDisplayString(m, nsec)); - CacheUsed++; - nsec = nsec->next; - } - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + PrintOneCacheRecord(m, cr, slot, remain, ifname, &CacheUsed); + PrintCachedRecords(m, cr, slot, remain, ifname, &CacheUsed); } } + } if (m->rrcache_totalused != CacheUsed) LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed); @@ -4610,6 +5654,7 @@ mDNSexport void udsserver_info(mDNS *const m) if (!m->Questions) LogMsgNoIdent(""); else { + char anonstr[256]; CacheUsed = 0; CacheActive = 0; LogMsgNoIdent(" Int Next if T NumAns VDNS Qptr DupOf SU SQ Type Name"); @@ -4620,14 +5665,15 @@ mDNSexport void udsserver_info(mDNS *const m) char *ifname = InterfaceNameForID(m, q->InterfaceID); CacheUsed++; if (q->ThisQInterval) CacheActive++; - LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s", + LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s%s", i, n, ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-", mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"), PrivateQuery(q) ? "P" : q->ValidationRequired ? "V" : q->ValidatingResponse ? "R" : " ", q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf, - q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, + AnonInfoToString(q->AnonInfo, anonstr, sizeof(anonstr)), + q->DuplicateOf ? " (dup)" : ""); } LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive); } @@ -4638,11 +5684,11 @@ mDNSexport void udsserver_info(mDNS *const m) LogMsgNoIdent(" %5d %-6s%##s%s", q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : ""); - LogMsgNoIdent("---- Active Client Requests ----"); + LogMsgNoIdent("---- Active UDS Client Requests ----"); if (!all_requests) LogMsgNoIdent(""); else { - const request_state *req, *r; + request_state *req, *r; for (req = all_requests; req; req=req->next) { if (req->primary) // If this is a subbordinate operation, check that the parent is in the list @@ -4657,24 +5703,30 @@ foundparent:; } LogMsgNoIdent("-------- NAT Traversals --------"); - if (!m->NATTraversals) LogMsgNoIdent(""); - else + LogMsgNoIdent("ExtAddress %.4a Retry %d Interval %d", + &m->ExtAddress, + m->retryGetAddr ? (m->retryGetAddr - now) / mDNSPlatformOneSecond : 0, + m->retryIntervalGetAddr / mDNSPlatformOneSecond); + if (m->NATTraversals) { const NATTraversalInfo *nat; for (nat = m->NATTraversals; nat; nat=nat->next) { - if (nat->Protocol) - LogMsgNoIdent("%p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d", - nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP", - mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result, - nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, - nat->retryInterval / mDNSPlatformOneSecond, - nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0); - else - LogMsgNoIdent("%p Address Request Retry %5d Interval %5d", nat, - (m->retryGetAddr - now) / mDNSPlatformOneSecond, - m->retryIntervalGetAddr / mDNSPlatformOneSecond); - usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000); + LogMsgNoIdent("%p %s Int %5d %s Err %d Retry %5d Interval %5d Expire %5d Req %.4a:%d Ext %.4a:%d", + nat, + nat->Protocol ? (nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP") : "ADD", + mDNSVal16(nat->IntPort), + (nat->lastSuccessfulProtocol == NATTProtocolNone ? "None " : + nat->lastSuccessfulProtocol == NATTProtocolNATPMP ? "NAT-PMP " : + nat->lastSuccessfulProtocol == NATTProtocolUPNPIGD ? "UPnP/IGD" : + nat->lastSuccessfulProtocol == NATTProtocolPCP ? "PCP " : + /* else */ "Unknown " ), + nat->Result, + nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0, + nat->retryInterval / mDNSPlatformOneSecond, + nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0, + &nat->NewAddress, mDNSVal16(nat->RequestedPort), + &nat->ExternalAddress, mDNSVal16(nat->ExternalPort)); } } @@ -4716,11 +5768,10 @@ foundparent:; m->SleepState == SleepState_Sleeping ? "Sleeping" : "?", m->SleepSeqNum); - LogMsgNoIdent("m->clearIgnoreNA %d", m->clearIgnoreNA); - if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service"); +#ifndef SPC_DISABLED else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c); - +#endif if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD); else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords); @@ -4742,8 +5793,11 @@ foundparent:; LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : ""); } } - LogMsgNoIdent("--- Trust Anchors --"); - if (!m->TrustAnchors) LogMsgNoIdent(""); + LogInfo("--- Trust Anchors ---"); + if (!m->TrustAnchors) + { + LogInfo(""); + } else { TrustAnchor *ta; @@ -4754,11 +5808,42 @@ foundparent:; { mDNSPlatformFormatTime((unsigned long)ta->validFrom, fromTimeBuf, sizeof(fromTimeBuf)); mDNSPlatformFormatTime((unsigned long)ta->validUntil, untilTimeBuf, sizeof(untilTimeBuf)); - LogMsgNoIdent("%##s %d %d %d %d %s %s", ta->zone.c, ta->rds.keyTag, + LogInfo("%##s %d %d %d %d %s %s", ta->zone.c, ta->rds.keyTag, ta->rds.alg, ta->rds.digestType, ta->digestLen, fromTimeBuf, untilTimeBuf); } } + LogInfo("--- DNSSEC Statistics ---"); + + LogInfo("Next Stats Time %u", m->NextStatLogTime - mDNSPlatformUTC()); + LogMsgNoIdent("Unicast Cache size %u", m->rrcache_totalused_unicast); + LogInfo("DNSSEC Cache size %u", m->DNSSECStats.TotalMemUsed); + if (m->rrcache_totalused_unicast) + LogInfo("DNSSEC usage percentage %u", ((unsigned long)(m->DNSSECStats.TotalMemUsed * 100))/m->rrcache_totalused_unicast); + LogInfo("DNSSEC Extra Packets (0 to 2) %u", m->DNSSECStats.ExtraPackets0); + LogInfo("DNSSEC Extra Packets (3 to 6) %u", m->DNSSECStats.ExtraPackets3); + LogInfo("DNSSEC Extra Packets (7 to 9) %u", m->DNSSECStats.ExtraPackets7); + LogInfo("DNSSEC Extra Packets ( >= 10) %u", m->DNSSECStats.ExtraPackets10); + + LogInfo("DNSSEC Latency (0 to 4ms) %u", m->DNSSECStats.Latency0); + LogInfo("DNSSEC Latency (4 to 9ms) %u", m->DNSSECStats.Latency5); + LogInfo("DNSSEC Latency (10 to 19ms) %u", m->DNSSECStats.Latency10); + LogInfo("DNSSEC Latency (20 to 49ms) %u", m->DNSSECStats.Latency20); + LogInfo("DNSSEC Latency (50 to 99ms) %u", m->DNSSECStats.Latency50); + LogInfo("DNSSEC Latency ( >=100ms) %u", m->DNSSECStats.Latency100); + + LogInfo("DNSSEC Secure Status %u", m->DNSSECStats.SecureStatus); + LogInfo("DNSSEC Insecure Status %u", m->DNSSECStats.InsecureStatus); + LogInfo("DNSSEC Indeterminate Status %u", m->DNSSECStats.IndeterminateStatus); + LogInfo("DNSSEC Bogus Status %u", m->DNSSECStats.BogusStatus); + LogInfo("DNSSEC NoResponse Status %u", m->DNSSECStats.NoResponseStatus); + LogInfo("DNSSEC Probes sent %u", m->DNSSECStats.NumProbesSent); + LogInfo("DNSSEC Msg Size (<=1024) %u", m->DNSSECStats.MsgSize0); + LogInfo("DNSSEC Msg Size (<=2048) %u", m->DNSSECStats.MsgSize1); + LogInfo("DNSSEC Msg Size (> 2048) %u", m->DNSSECStats.MsgSize2); + + LogMDNSStatistics(m); + LogMsgNoIdent("---- Task Scheduling Timers ----"); if (!m->NewQuestions) @@ -4782,6 +5867,11 @@ foundparent:; LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " "); LogMsgNoIdent("LocalRemoveEvents%s", m->LocalRemoveEvents ? "" : " "); LogMsgNoIdent("m->AutoTunnelRelayAddr %.16a", &m->AutoTunnelRelayAddr); + LogMsgNoIdent("m->WABBrowseQueriesCount %d", m->WABBrowseQueriesCount); + LogMsgNoIdent("m->WABLBrowseQueriesCount %d", m->WABLBrowseQueriesCount); + LogMsgNoIdent("m->WABRegQueriesCount %d", m->WABRegQueriesCount); + LogMsgNoIdent("m->mDNSOppCaching %d", m->mDNSOppCaching); + LogMsgNoIdent("m->AutoTargetServices %d", m->AutoTargetServices); #define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d %08X %11d", (T), (T), (T)-now, (T)-now) @@ -4930,7 +6020,7 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) { r->u.resolve.ReportTime = 0; LogMsgNoIdent("Client application bug PID[%d](%s) : DNSServiceResolve(%##s) active for over two minutes. " - "This places considerable burden on the network.", get_peer_pid(r->sd, pid_name), pid_name, r->u.resolve.qsrv.qname.c); + "This places considerable burden on the network.", r->process_id, r->pid_name, r->u.resolve.qsrv.qname.c); } // Note: Only primary req's have reply lists, not subordinate req's. @@ -4951,7 +6041,7 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) } else if (result == t_terminated || result == t_error) { - LogMsg("%3d: Could not write data to clientPID[%d](%s) because of error - aborting connection", r->sd, get_peer_pid(r->sd, pid_name), pid_name); + LogMsg("%3d: Could not write data to clientPID[%d](%s) because of error - aborting connection", r->sd, r->process_id, r->pid_name); LogClientInfo(&mDNSStorage, r); abort_request(r); } @@ -4977,10 +6067,10 @@ mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent) x=x->next; } LogMsg("%3d: Could not write data to client PID[%d](%s) after %ld seconds, %d repl%s waiting", - r->sd, get_peer_pid(r->sd, pid_name), pid_name, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies"); + r->sd, r->process_id, r->pid_name, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies"); if (++r->unresponsiveness_reports >= 60) { - LogMsg("%3d: Client PID[%d](%s) unresponsive; aborting connection", r->sd, get_peer_pid(r->sd, pid_name), pid_name); + LogMsg("%3d: Client PID[%d](%s) unresponsive; aborting connection", r->sd, r->process_id, r->pid_name); LogClientInfo(&mDNSStorage, r); abort_request(r); } @@ -5007,7 +6097,7 @@ struct CompileTimeAssertionChecks_uds_daemon char sizecheck_request_state [(sizeof(request_state) <= 2000) ? 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) <= 1050) ? 1 : -1]; + char sizecheck_browser_t [(sizeof(browser_t) <= 1096) ? 1 : -1]; char sizecheck_reply_hdr [(sizeof(reply_hdr) <= 12) ? 1 : -1]; char sizecheck_reply_state [(sizeof(reply_state) <= 64) ? 1 : -1]; }; diff --git a/mDNSShared/uds_daemon.h b/mDNSShared/uds_daemon.h index 5254946..ca36117 100644 --- a/mDNSShared/uds_daemon.h +++ b/mDNSShared/uds_daemon.h @@ -34,6 +34,11 @@ extern mDNSs32 udsserver_idle(mDNSs32 nextevent); extern void udsserver_info(mDNS *const m); // print out info about current state extern void udsserver_handle_configchange(mDNS *const m); extern int udsserver_exit(void); // should be called prior to app exit +extern void LogMcastStateInfo(mDNS *const m, mDNSBool mflag, mDNSBool start, mDNSBool mstatelog); +#define LogMcastQ (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastQuestion +#define LogMcastS (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMcastService +#define LogMcast (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsg +#define LogMcastNoIdent (mDNS_McastLoggingEnabled == 0) ? ((void)0) : LogMsgNoIdent /* Routines that uds_daemon expects to link against: */ @@ -50,16 +55,17 @@ extern mDNS mDNSStorage; extern DNameListElem *AutoRegistrationDomains; extern DNameListElem *AutoBrowseDomains; -extern mDNSs32 ChopSubTypes(char *regtype); -extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p); +extern mDNSs32 ChopSubTypes(char *regtype, char **AnonData); +extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p, char **AnonData); extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port); extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result); extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs); #if APPLE_OSX_mDNSResponder + extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add); extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add); -// External support +// D2D interface support extern void external_start_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); extern void external_stop_browsing_for_service(mDNSInterfaceID InterfaceID, const domainname *const type, DNS_TypeValues qtype, DNSServiceFlags flags); extern void external_start_advertising_service(const ResourceRecord *const resourceRecord, DNSServiceFlags flags); @@ -67,7 +73,9 @@ extern void external_stop_advertising_service(const ResourceRecord *const resour extern void external_start_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags); extern void external_stop_resolving_service(mDNSInterfaceID InterfaceID, const domainname *const fqdn, DNSServiceFlags flags); extern void external_connection_release(const domainname *instance); -#else + +#else // APPLE_OSX_mDNSResponder + #define external_start_browsing_for_service(A,B,C,D) (void)(A) #define external_stop_browsing_for_service(A,B,C,D) (void)(A) #define external_start_advertising_service(A,B) (void)(A) @@ -75,6 +83,7 @@ extern void external_connection_release(const domainname *instance); #define external_start_resolving_service(A,B,C) (void)(A) #define external_stop_resolving_service(A,B,C) (void)(A) #define external_connection_release(A) (void)(A) + #endif // APPLE_OSX_mDNSResponder extern const char mDNSResponderVersionString_SCCS[]; diff --git a/mDNSWindows/DLLX/DLLX.idl b/mDNSWindows/DLLX/DLLX.idl index 475558e..e44865b 100755 --- a/mDNSWindows/DLLX/DLLX.idl +++ b/mDNSWindows/DLLX/DLLX.idl @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2009 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2009-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -215,8 +215,8 @@ enum DNSSDError kDNSSDError_BadKey = -65561, kDNSSDError_Transient = -65562, kDNSSDError_ServiceNotRunning = -65563, /* Background daemon not running */ - kDNSSDError_NATPortMappingUnsupported = -65564, /* NAT doesn't support NAT-PMP or UPnP */ - kDNSSDError_NATPortMappingDisabled = -65565, /* NAT supports NAT-PMP or UPnP but it's disabled by the administrator */ + kDNSSDError_NATPortMappingUnsupported = -65564, /* NAT doesn't support PCP, NAT-PMP or UPnP */ + kDNSSDError_NATPortMappingDisabled = -65565, /* NAT supports PCP, NAT-PMP or UPnP, but it's disabled by the administrator */ kDNSSDError_NoRouter = -65566, /* No router currently configured (probably no network connectivity) */ kDNSSDError_PollingMode = -65567 } DNSSDError; diff --git a/mDNSWindows/DLLX/DLLX.vcxproj b/mDNSWindows/DLLX/DLLX.vcxproj index 93ca80c..a57e4ed 100755 --- a/mDNSWindows/DLLX/DLLX.vcxproj +++ b/mDNSWindows/DLLX/DLLX.vcxproj @@ -158,7 +158,7 @@ ..;$(IntDir);%(AdditionalIncludeDirectories) - true + false /NXCOMPAT /DYNAMICBASE %(AdditionalOptions) ws2_32.lib;../../mDNSWindows/DLLStub/$(Platform)/$(Configuration)/dnssdStatic.lib;%(AdditionalDependencies) $(OutDir)dnssdX.dll diff --git a/mDNSWindows/Secret.c b/mDNSWindows/Secret.c index 5abd28b..b5f254c 100644 --- a/mDNSWindows/Secret.c +++ b/mDNSWindows/Secret.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, cons mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input ); -BOOL +mDNSBool LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize ) { PLSA_UNICODE_STRING domainLSA; diff --git a/mDNSWindows/Secret.h b/mDNSWindows/Secret.h index 79643d6..f5434f0 100644 --- a/mDNSWindows/Secret.h +++ b/mDNSWindows/Secret.h @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ extern "C" { extern mDNSBool -LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainLength, char * outKey, unsigned outKeyLength, char * outSecret, unsigned outSecretLength ); +LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize ); extern mDNSBool @@ -39,4 +39,4 @@ LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret ) #endif -#endif \ No newline at end of file +#endif diff --git a/mDNSWindows/SystemService/Service.vcxproj b/mDNSWindows/SystemService/Service.vcxproj index fe0758c..c0eb7a0 100755 --- a/mDNSWindows/SystemService/Service.vcxproj +++ b/mDNSWindows/SystemService/Service.vcxproj @@ -249,6 +249,8 @@ xcopy /I/Y "$(TargetDir)$(TargetName).pdb" + + @@ -283,6 +285,8 @@ xcopy /I/Y "$(TargetDir)$(TargetName).pdb" + + diff --git a/mDNSWindows/SystemService/Service.vcxproj.filters b/mDNSWindows/SystemService/Service.vcxproj.filters index 0f3b710..ba41b93 100755 --- a/mDNSWindows/SystemService/Service.vcxproj.filters +++ b/mDNSWindows/SystemService/Service.vcxproj.filters @@ -63,6 +63,12 @@ Source Files + + Source Files + + + Source Files + @@ -107,6 +113,12 @@ Header Files + + Header Files + + + Header Files + diff --git a/mDNSWindows/mDNSWin32.c b/mDNSWindows/mDNSWin32.c index 86dfa3a..73954e6 100755 --- a/mDNSWindows/mDNSWin32.c +++ b/mDNSWindows/mDNSWin32.c @@ -1,6 +1,6 @@ /* -*- Mode: C; tab-width: 4 -*- * - * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2002-2013 Apple Computer, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -673,6 +673,38 @@ mDNSexport mDNSBool AddNSECSForCacheRecord(mDNS *const m, CacheRecord *crlist, C return mDNSfalse; } +mDNSexport void BumpDNSSECStats(mDNS *const m, DNSSECStatsAction action, DNSSECStatsType type, mDNSu32 value) + { + (void)m; + (void)action; + (void)type; + (void)value; + } + +// Proxy stub functions +mDNSexport mDNSu8 *DNSProxySetAttributes(DNSQuestion *q, DNSMessageHeader *h, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit) +{ + (void) q; + (void) h; + (void) msg; + (void) ptr; + (void) limit; + + return ptr; +} + +mDNSexport void DNSProxyInit(mDNS *const m, mDNSu32 IpIfArr[], mDNSu32 OpIf) +{ + (void) m; + (void) IpIfArr; + (void) OpIf; +} + +mDNSexport void DNSProxyTerminate(mDNS *const m) +{ + (void) m; +} + //=========================================================================================================================== // mDNSPlatformMemZero //=========================================================================================================================== @@ -1419,6 +1451,13 @@ exit: } +mDNSexport mDNSBool mDNSPlatformPeekUDP(mDNS *const m, UDPSocket *src) +{ + DEBUG_UNUSED( m ); + DEBUG_UNUSED( src ); + return mDNSfalse; +} + mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID) { DEBUG_UNUSED( m ); @@ -1648,8 +1687,10 @@ mDNSPlatformTLSTearDownCerts(void) mDNSlocal void SetDNSServers( mDNS *const m ); mDNSlocal void SetSearchDomainList( void ); -mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains) +mDNSexport mDNSBool mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains, mDNSBool ackConfig) { + (void) ackConfig; + if (setservers) SetDNSServers(m); if (setsearch) SetSearchDomainList(); @@ -1667,6 +1708,7 @@ mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDN { GetDDNSDomains( regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains ); } + return mDNStrue; } @@ -1920,7 +1962,7 @@ SetDNSServers( mDNS *const m ) { mDNSAddr addr; err = StringToAddress( &addr, ipAddr->IpAddress.String ); - if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, DEFAULT_UDNS_TIMEOUT, mDNSfalse, 0); + if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, 0, &addr, UnicastDNSPort, kScopeNone, DEFAULT_UDNS_TIMEOUT, mDNSfalse, 0, mDNStrue, mDNStrue, mDNSfalse); } exit: @@ -2113,6 +2155,18 @@ mDNSexport void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNSIP (void) win; // Unused } +mDNSexport mStatus mDNSPlatformGetRemoteMacAddr(mDNSAddr *raddr, char *eth) + { + (void) raddr; // Unused + (void) eth; // Unused + } + +mDNSexport mStatus mDNSPlatformStoreSPSMACAddr(mDNSAddr *spsaddr, char *ifname) + { + (void) spsaddr; // Unused + (void) ifname; // Unused + } + mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, mDNSIPPort *lport, mDNSAddr *raddr, mDNSIPPort *rport, mDNSTCPInfo *mti) { (void) m; // Unused @@ -2123,6 +2177,46 @@ mDNSexport mStatus mDNSPlatformRetrieveTCPInfo(mDNS *const m, mDNSAddr *laddr, m (void) mti; // Unused } +mDNSexport mDNSBool mDNSPlatformAllowPID(mDNS *const m, DNSQuestion *q) + { + (void) m; + (void) q; + return mDNStrue; + } + +mDNSexport mDNSs32 mDNSPlatformGetServiceID(mDNS *const m, DNSQuestion *q) + { + (void) m; + (void) q; + return 0; + } + +mDNSexport void mDNSPlatformSetDelegatePID(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q) + { + (void) src; + (void) dst; + (void) q; + } + +mDNSexport mDNSs32 mDNSPlatformGetPID() + { + return 0; + } + +mDNSexport mDNSu16 mDNSPlatformGetUDPPort(UDPSocket *sock) +{ + DEBUG_UNUSED( sock ); + + return (mDNSu16)-1; +} + +mDNSexport mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID) +{ + DEBUG_UNUSED( InterfaceID ); + + return mDNSfalse; +} + #if 0 #pragma mark - #endif -- 2.45.2