summaryrefslogtreecommitdiffstats
path: root/mDNSResponder/mDNSCore
diff options
context:
space:
mode:
Diffstat (limited to 'mDNSResponder/mDNSCore')
-rw-r--r--mDNSResponder/mDNSCore/DNSCommon.c359
-rw-r--r--mDNSResponder/mDNSCore/DNSCommon.h4
-rwxr-xr-xmDNSResponder/mDNSCore/mDNS.c265
-rwxr-xr-xmDNSResponder/mDNSCore/mDNSDebug.h6
-rwxr-xr-xmDNSResponder/mDNSCore/mDNSEmbeddedAPI.h60
-rwxr-xr-xmDNSResponder/mDNSCore/uDNS.c2
6 files changed, 520 insertions, 176 deletions
diff --git a/mDNSResponder/mDNSCore/DNSCommon.c b/mDNSResponder/mDNSCore/DNSCommon.c
index 597c4cc0..a249b967 100644
--- a/mDNSResponder/mDNSCore/DNSCommon.c
+++ b/mDNSResponder/mDNSCore/DNSCommon.c
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2002-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -3450,7 +3450,6 @@ mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage
rr->TimeRcvd = m ? m->timenow : 0;
rr->DelayDelivery = 0;
rr->NextRequiredQuery = m ? m->timenow : 0; // Will be updated to the real value when we call SetNextCacheCheckTimeForRecord()
- rr->LastUsed = m ? m->timenow : 0;
rr->CRActiveQuestion = mDNSNULL;
rr->UnansweredQueries = 0;
rr->LastUnansweredTime= 0;
@@ -3630,25 +3629,6 @@ mDNSexport mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, cons
return mDNSfalse;
}
-mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label)
-{
- int i;
- LogInfo("%2d %s", count, label);
- for (i = 0; i < count && ptr; i++)
- {
- // This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
- // but since it's only used for debugging (and probably only on OS X, not on
- // embedded systems) putting a 9kB object on the stack isn't a big problem.
- LargeCacheRecord largecr;
- ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr);
- if (ptr)
- LogInfo("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r));
- }
- if (!ptr)
- LogInfo("DumpRecords: ERROR: Premature end of packet data");
- return(ptr);
-}
-
#define DNS_OP_Name(X) ( \
(X) == kDNSFlag0_OP_StdQuery ? "" : \
(X) == kDNSFlag0_OP_Iquery ? "Iquery " : \
@@ -3672,52 +3652,198 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg,
(X) == kDNSFlag1_RC_NotAuth ? "NotAuth" : \
(X) == kDNSFlag1_RC_NotZone ? "NotZone" : "??" )
-// Note: DumpPacket expects the packet header fields in host byte order, not network byte order
-mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
- const mDNSAddr *srcaddr, mDNSIPPort srcport,
- const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end)
+mDNSlocal void mDNS_snprintf_add(char **ptr, const char *lim, const char *fmt, ...)
{
- mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update);
- const mDNSu8 *ptr = msg->data;
- int i;
- DNSQuestion q;
- char tbuffer[64], sbuffer[64], dbuffer[64] = "";
- if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received" )] = 0;
- else tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receive")] = 0;
- if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port " )] = 0;
- else sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
- if (dstaddr || !mDNSIPPortIsZero(dstport))
- dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0;
-
- LogInfo("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --",
- tbuffer, transport,
+ va_list args;
+ mDNSu32 buflen, n;
+ char *const dst = *ptr;
+
+ buflen = (mDNSu32)(lim - dst);
+ if (buflen > 0)
+ {
+ va_start(args, fmt);
+ n = mDNS_vsnprintf(dst, buflen, fmt, args);
+ va_end(args);
+ *ptr = dst + n;
+ }
+}
+
+#define DNSTypeString(X) (((X) == kDNSType_A) ? "A" : DNSTypeName(X))
+
+#define ReadField16(PTR) ((mDNSu16)((((mDNSu16)((mDNSu8 *)(PTR))[0]) << 8) | ((mDNSu16)((mDNSu8 *)(PTR))[1])))
+#define ReadField32(PTR) \
+ ((mDNSu32)( \
+ (((mDNSu32)((mDNSu8 *)(PTR))[0]) << 24) | \
+ (((mDNSu32)((mDNSu8 *)(PTR))[1]) << 16) | \
+ (((mDNSu32)((mDNSu8 *)(PTR))[2]) << 8) | \
+ ((mDNSu32)((mDNSu8 *)(PTR))[3])))
+
+mDNSlocal void DNSMessageDump(const DNSMessage *const msg, const mDNSu8 *const end, char *buffer, mDNSu32 buflen)
+{
+ domainname *name;
+ const mDNSu8 *ptr;
+ domainname nameStorage[2];
+ char *dst = buffer;
+ const char *const lim = &buffer[buflen];
+ mDNSu32 i;
+ const mDNSu32 rrcount = msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals;
+
+ mDNS_snprintf_add(&dst, lim, "DNS %s%s (%lu) (flags %02X%02X) RCODE: %s (%d)%s%s%s%s%s%s ID: %u:",
DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
- msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query",
+ (msg->h.flags.b[0] & kDNSFlag0_QR_Response) ? "Response" : "Query",
+ (unsigned long)(end - (const mDNSu8 *)msg),
msg->h.flags.b[0], msg->h.flags.b[1],
DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
- msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "",
- msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "",
- msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "",
- msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "",
- msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "",
- msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "",
- mDNSVal16(msg->h.id),
- end - msg->data,
- sbuffer, mDNSVal16(srcport), dbuffer,
- (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : ""
- );
-
- LogInfo("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions");
- for (i = 0; i < msg->h.numQuestions && ptr; i++)
+ (msg->h.flags.b[0] & kDNSFlag0_AA) ? " AA" : "",
+ (msg->h.flags.b[0] & kDNSFlag0_TC) ? " TC" : "",
+ (msg->h.flags.b[0] & kDNSFlag0_RD) ? " RD" : "",
+ (msg->h.flags.b[1] & kDNSFlag1_RA) ? " RA" : "",
+ (msg->h.flags.b[1] & kDNSFlag1_AD) ? " AD" : "",
+ (msg->h.flags.b[1] & kDNSFlag1_CD) ? " CD" : "",
+ mDNSVal16(msg->h.id));
+
+ name = mDNSNULL;
+ ptr = msg->data;
+ for (i = 0; i < msg->h.numQuestions; i++)
{
- ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q);
- if (ptr) LogInfo("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype));
+ mDNSu16 qtype, qclass;
+
+ name = &nameStorage[0];
+ ptr = getDomainName(msg, ptr, end, name);
+ if (!ptr) goto exit;
+
+ if ((end - ptr) < 4) goto exit;
+ qtype = ReadField16(&ptr[0]);
+ qclass = ReadField16(&ptr[2]);
+ ptr += 4;
+
+ mDNS_snprintf_add(&dst, lim, " %##s %s", name->c, DNSTypeString(qtype));
+ if (qclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", qclass);
+ mDNS_snprintf_add(&dst, lim, "?");
}
- ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers, IsUpdate ? "Prerequisites" : "Answers");
- ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates" : "Authorities");
- DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals");
- LogInfo("--------------");
+
+ mDNS_snprintf_add(&dst, lim, " %u/%u/%u", msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals);
+ for (i = 0; i < rrcount; i++)
+ {
+ mDNSu16 rrtype, rrclass, rdlength;
+ mDNSu32 ttl;
+ int handled;
+ const mDNSu8 *rdata;
+ const domainname *const previousName = name;
+
+ name = &nameStorage[(name == &nameStorage[0]) ? 1 : 0];
+ ptr = getDomainName(msg, ptr, end, name);
+ if (!ptr) goto exit;
+
+ if ((end - ptr) < 10) goto exit;
+ rrtype = ReadField16(&ptr[0]);
+ rrclass = ReadField16(&ptr[2]);
+ ttl = ReadField32(&ptr[4]);
+ rdlength = ReadField16(&ptr[8]);
+ ptr += 10;
+
+ if ((end - ptr) < rdlength) goto exit;
+ rdata = ptr;
+
+ if (i > 0) mDNS_snprintf_add(&dst, lim, ",");
+ if (!previousName || !SameDomainName(name, previousName)) mDNS_snprintf_add(&dst, lim, " %##s", name);
+
+ mDNS_snprintf_add(&dst, lim, " %s", DNSTypeString(rrtype));
+ if (rrclass != kDNSClass_IN) mDNS_snprintf_add(&dst, lim, "/%u", rrclass);
+ mDNS_snprintf_add(&dst, lim, " ");
+
+ handled = mDNSfalse;
+ switch (rrtype)
+ {
+ case kDNSType_A:
+ if (rdlength == 4)
+ {
+ mDNS_snprintf_add(&dst, lim, "%.4a", rdata);
+ handled = mDNStrue;
+ }
+ break;
+
+ case kDNSType_AAAA:
+ if (rdlength == 16)
+ {
+ mDNS_snprintf_add(&dst, lim, "%.16a", rdata);
+ handled = mDNStrue;
+ }
+ break;
+
+ case kDNSType_CNAME:
+ ptr = getDomainName(msg, rdata, end, name);
+ if (!ptr) goto exit;
+
+ mDNS_snprintf_add(&dst, lim, "%##s", name);
+ handled = mDNStrue;
+ break;
+
+ case kDNSType_SOA:
+ {
+ mDNSu32 serial, refresh, retry, expire, minimum;
+ domainname *const mname = &nameStorage[0];
+ domainname *const rname = &nameStorage[1];
+ name = mDNSNULL;
+
+ ptr = getDomainName(msg, rdata, end, mname);
+ if (!ptr) goto exit;
+
+ ptr = getDomainName(msg, ptr, end, rname);
+ if (!ptr) goto exit;
+
+ if ((end - ptr) < 20) goto exit;
+ serial = ReadField32(&ptr[0]);
+ refresh = ReadField32(&ptr[4]);
+ retry = ReadField32(&ptr[8]);
+ expire = ReadField32(&ptr[12]);
+ minimum = ReadField32(&ptr[16]);
+
+ mDNS_snprintf_add(&dst, lim, "%##s %##s %lu %lu %lu %lu %lu", mname, rname, (unsigned long)serial,
+ (unsigned long)refresh, (unsigned long)retry, (unsigned long)expire, (unsigned long)minimum);
+
+ handled = mDNStrue;
+ break;
+ }
+
+ default:
+ break;
+ }
+ if (!handled) mDNS_snprintf_add(&dst, lim, "RDATA[%u]: %.*H", rdlength, rdlength, rdata);
+ mDNS_snprintf_add(&dst, lim, " (%lu)", (unsigned long)ttl);
+ ptr = rdata + rdlength;
+ }
+
+exit:
+ return;
+}
+
+// Note: DumpPacket expects the packet header fields in host byte order, not network byte order
+mDNSexport void DumpPacket(mStatus status, mDNSBool sent, char *transport,
+ const mDNSAddr *srcaddr, mDNSIPPort srcport,
+ const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end)
+{
+ char buffer[512];
+ char *dst = buffer;
+ const char *const lim = &buffer[512];
+
+ buffer[0] = '\0';
+ if (!status) mDNS_snprintf_add(&dst, lim, sent ? "Sent" : "Received");
+ else mDNS_snprintf_add(&dst, lim, "ERROR %d %sing", status, sent ? "Send" : "Receiv");
+
+ mDNS_snprintf_add(&dst, lim, " %s DNS Message %u bytes from ", transport, (unsigned long)(end - (const mDNSu8 *)msg));
+
+ if (sent) mDNS_snprintf_add(&dst, lim, "port %d", mDNSVal16(srcport));
+ else mDNS_snprintf_add(&dst, lim, "%#a:%d", srcaddr, mDNSVal16(srcport));
+
+ if (dstaddr || !mDNSIPPortIsZero(dstport)) mDNS_snprintf_add(&dst, lim, " to %#a:%d", dstaddr, mDNSVal16(dstport));
+
+ LogInfo("%s", buffer);
+
+ buffer[0] = '\0';
+ DNSMessageDump(msg, end, buffer, (mDNSu32)sizeof(buffer));
+ LogInfo("%s", buffer);
}
// ***************************************************************************
@@ -3824,7 +3950,7 @@ mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNS
// Dump the packet with the HINFO and TSIG
if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id))
- DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end);
+ DumpPacket(status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end);
// put the number of additionals back the way it was
msg->h.numAdditionals = numAdditionals;
@@ -4053,6 +4179,9 @@ static const struct mDNSprintf_format
unsigned int precision;
} mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+#define kHexDigitsLowercase "0123456789abcdef"
+#define kHexDigitsUppercase "0123456789ABCDEF";
+
mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
{
mDNSu32 nwritten = 0;
@@ -4064,6 +4193,7 @@ mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt
for (c = *fmt; c != 0; c = *++fmt)
{
unsigned long n;
+ int hexdump = mDNSfalse;
if (c != '%')
{
*sbuffer++ = (char)c;
@@ -4190,10 +4320,63 @@ decimal: if (!F.havePrecision)
a[0], a[1], a[2], a[3]); break;
case 6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
a[0], a[1], a[2], a[3], a[4], a[5]); break;
- case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
- "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
- a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
- a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
+ case 16: {
+ // Print IPv6 addresses according to RFC 5952, A Recommendation for IPv6 Address Text
+ // Representation. See <https://tools.ietf.org/html/rfc5952>.
+
+ int idx, runLen = 0, runStart = 0, maxRunLen = 0, maxRunStart = 0, maxRunEnd;
+
+ // Find the leftmost longest run of consecutive zero hextets.
+ for (idx = 0; idx < 8; ++idx)
+ {
+ const unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1];
+ if (hextet == 0)
+ {
+ if (runLen++ == 0) runStart = idx;
+ if (runLen > maxRunLen)
+ {
+ maxRunStart = runStart;
+ maxRunLen = runLen;
+ }
+ }
+ else
+ {
+ // If the number of remaining hextets is less than or equal to the length of the longest
+ // run so far, then we've found the leftmost longest run.
+ if ((8 - (idx + 1)) <= maxRunLen) break;
+ runLen = 0;
+ }
+ }
+
+ // Compress the leftmost longest run of two or more consecutive zero hextets as "::".
+ // For each reminaing hextet, suppress zeros leading up to the least-significant nibble, which
+ // is always written, even if it's zero. Because of this requirement, it's easier to write the
+ // IPv6 address in reverse. Also, write a colon separator before each hextet except for the
+ // first one.
+ s = mDNS_VACB_Lim;
+ maxRunEnd = (maxRunLen >= 2) ? (maxRunStart + maxRunLen - 1) : -1;
+ for (idx = 7; idx >= 0; --idx)
+ {
+ if (idx == maxRunEnd)
+ {
+ if (idx == 7) *--s = ':';
+ idx = maxRunStart;
+ *--s = ':';
+ }
+ else
+ {
+ unsigned int hextet = (a[idx * 2] << 8) | a[(idx * 2) + 1];
+ do {
+ *--s = kHexDigitsLowercase[hextet % 16];
+ hextet /= 16;
+ } while (hextet);
+ if (idx > 0) *--s = ':';
+ }
+ }
+ i = (unsigned int)(mDNS_VACB_Lim - s);
+ }
+ break;
+
default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
" address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
}
@@ -4203,9 +4386,9 @@ decimal: if (!F.havePrecision)
case 'p': F.havePrecision = F.lSize = 1;
F.precision = sizeof(void*) * 2; // 8 characters on 32-bit; 16 characters on 64-bit
- case 'X': digits = "0123456789ABCDEF";
+ case 'X': digits = kHexDigitsUppercase;
goto hexadecimal;
- case 'x': digits = "0123456789abcdef";
+ case 'x': digits = kHexDigitsLowercase;
hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
else n = va_arg(arg, unsigned int);
if (F.hSize) n = (unsigned short) n;
@@ -4288,6 +4471,12 @@ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
{ i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
break;
+ case 'H': {
+ s = va_arg(arg, char *);
+ hexdump = mDNStrue;
+ }
+ break;
+
case 'n': s = va_arg(arg, char *);
if (F.hSize) *(short *) s = (short)nwritten;
else if (F.lSize) *(long *) s = (long)nwritten;
@@ -4308,14 +4497,34 @@ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long);
if (++nwritten >= buflen) goto exit;
} while (i < --F.fieldWidth);
- // Make sure we don't truncate in the middle of a UTF-8 character.
- // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
- // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
- // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
- // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
- if (i > buflen - nwritten)
- { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
- for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
+ if (hexdump)
+ {
+ char *dst = sbuffer;
+ const char *const lim = &sbuffer[buflen - nwritten];
+ if (F.havePrecision)
+ {
+ for (i = 0; (i < F.precision) && (dst < lim); i++)
+ {
+ const unsigned int b = (unsigned int) *s++;
+ if (i > 0) *dst++ = ' ';
+ if (dst < lim) *dst++ = kHexDigitsLowercase[(b >> 4) & 0xF];
+ if (dst < lim) *dst++ = kHexDigitsLowercase[ b & 0xF];
+ }
+ }
+ i = (unsigned int)(dst - sbuffer);
+ sbuffer = dst;
+ }
+ else
+ {
+ // Make sure we don't truncate in the middle of a UTF-8 character.
+ // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
+ // allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
+ // so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
+ // formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
+ if (i > buflen - nwritten)
+ { i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--;}
+ for (j=0; j<i; j++) *sbuffer++ = *s++; // Write the converted result
+ }
nwritten += i;
if (nwritten >= buflen) goto exit;
diff --git a/mDNSResponder/mDNSCore/DNSCommon.h b/mDNSResponder/mDNSCore/DNSCommon.h
index e1ef261e..b100a400 100644
--- a/mDNSResponder/mDNSCore/DNSCommon.h
+++ b/mDNSResponder/mDNSCore/DNSCommon.h
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2002-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -260,7 +260,7 @@ extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8
extern const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize);
extern const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end);
extern mDNSBool GetPktLease(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, mDNSu32 *const lease);
-extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
+extern void DumpPacket(mStatus status, mDNSBool sent, char *transport,
const mDNSAddr *srcaddr, mDNSIPPort srcport,
const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end);
extern mDNSBool RRAssertsNonexistence(const ResourceRecord *const rr, mDNSu16 type);
diff --git a/mDNSResponder/mDNSCore/mDNS.c b/mDNSResponder/mDNSCore/mDNS.c
index 0788ab67..8deada2e 100755
--- a/mDNSResponder/mDNSCore/mDNS.c
+++ b/mDNSResponder/mDNSCore/mDNS.c
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2002-2017 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -548,6 +548,7 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re
// because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
q->CNAMEReferrals = c;
#if AWD_METRICS
+ metrics.expiredAnswerState = q->metrics.expiredAnswerState; // We want the newly initialized state for this value
q->metrics = metrics;
#endif
if (sock)
@@ -785,6 +786,7 @@ mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRec
#define GoodbyeCount ((mDNSu8)3)
#define WakeupCount ((mDNSu8)18)
#define MAX_PROBE_RESTARTS ((mDNSu8)20)
+#define MAX_GHOST_TIME ((mDNSs32)((60*60*24*7)*mDNSPlatformOneSecond)) // One week
// Number of wakeups we send if WakeOnResolve is set in the question
#define InitialWakeOnResolveCount ((mDNSu8)3)
@@ -3220,21 +3222,25 @@ mDNSlocal mDNSBool BuildQuestion(mDNS *const m, const NetworkInterfaceInfo *intf
// Depth 3: PTR "_services._dns-sd._udp.local." refers to "_example._tcp.local."; may be stale
// Currently depths 4 and 5 are not expected to occur; if we did get to depth 5 we'd reconfim any records we
// found referring to the given name, but not recursively descend any further reconfirm *their* antecedents.
-mDNSlocal void ReconfirmAntecedents(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const int depth)
+mDNSlocal void ReconfirmAntecedents(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSInterfaceID InterfaceID, const int depth)
{
mDNSu32 slot;
- CacheGroup *cg;
+ const CacheGroup *cg;
CacheRecord *cr;
debugf("ReconfirmAntecedents (depth=%d) for %##s", depth, name->c);
+ if (!InterfaceID) return; // mDNS records have a non-zero InterfaceID. If InterfaceID is 0, then there's nothing to do.
FORALL_CACHERECORDS(slot, cg, cr)
{
- domainname *crtarget = GetRRDomainNameTarget(&cr->resrec);
- if (crtarget && cr->resrec.rdatahash == namehash && SameDomainName(crtarget, name))
+ const domainname *crtarget;
+ if (cr->resrec.InterfaceID != InterfaceID) continue; // Skip non-mDNS records and mDNS records from other interfaces.
+ if (cr->resrec.rdatahash != namehash) continue; // Skip records whose rdata hash doesn't match the name hash.
+ crtarget = GetRRDomainNameTarget(&cr->resrec);
+ if (crtarget && SameDomainName(crtarget, name))
{
- LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth, CRDisplayString(m, cr));
+ LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d, InterfaceID=%p) %s", depth, InterfaceID, CRDisplayString(m, cr));
mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
if (depth < 5)
- ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, depth+1);
+ ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, InterfaceID, depth+1);
}
}
}
@@ -3612,7 +3618,8 @@ mDNSlocal void SendQueries(mDNS *const m)
{
q->ThisQInterval = MaxQuestionInterval;
}
- else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast &&
+ else if (mDNSOpaque16IsZero(q->TargetQID) && q->InterfaceID &&
+ q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast &&
!(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash)))
{
// Generally don't need to log this.
@@ -3623,7 +3630,7 @@ mDNSlocal void SendQueries(mDNS *const m)
debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents",
q->qname.c, DNSTypeName(q->qtype));
// Sending third query, and no answers yet; time to begin doubting the source
- ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0);
+ ReconfirmAntecedents(m, &q->qname, q->qnamehash, q->InterfaceID, 0);
}
}
@@ -4107,8 +4114,9 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
DNSQuestion *const q = m->CurrentQuestion;
const mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord);
- verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s",
- q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr));
+ verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s (%s) TTL %d %s",
+ q->CurrentAnswers, AddRecord ? "Add" : "Rmv", MortalityDisplayString(rr->resrec.mortality),
+ rr->resrec.rroriginalttl, CRDisplayString(m, rr));
// When the response for the question was validated, the entire rrset was validated. If we deliver
// a RMV for a single record in the rrset, we invalidate the response. If we deliver another add
@@ -4149,7 +4157,11 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0))
return;
}
-
+
+ // Set the record to immortal if appropriate
+ if (AddRecord == QC_add && Question_uDNS(q) && rr->resrec.RecordType != kDNSRecordTypePacketNegative &&
+ q->allowExpired != AllowExpired_None && rr->resrec.mortality == Mortality_Mortal ) rr->resrec.mortality = Mortality_Immortal; // Update a non-expired cache record to immortal if appropriate
+
#if AWD_METRICS
if ((AddRecord == QC_add) && Question_uDNS(q) && !followcname)
{
@@ -4170,7 +4182,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
responseLatencyMs = 0;
}
- MetricsUpdateDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, responseLatencyMs, isForCellular);
+ MetricsUpdateDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, q->metrics.expiredAnswerState, responseLatencyMs, isForCellular);
q->metrics.answered = mDNStrue;
}
if (q->metrics.querySendCount > 0)
@@ -4183,8 +4195,7 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
// may be called twice, once when the record is received, and again when it's time to notify local clients.
// If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
- rr->LastUsed = m->timenow;
- if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q)
+ if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q && rr->resrec.mortality != Mortality_Ghost)
{
if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count
debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d",
@@ -4293,14 +4304,21 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco
return;
}
- // Note: Proceed with caution here because client callback function is allowed to do anything,
- // including starting/stopping queries, registering/deregistering records, etc.
- //
- // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG),
- // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated
- // first before following it
- if ((m->CurrentQuestion == q) && followcname && !ValidatingQuestion(q))
- AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
+ if ((m->CurrentQuestion == q) && !ValidatingQuestion(q))
+ {
+ // If we get a CNAME back while we are validating the response (i.e., CNAME for DS, DNSKEY, RRSIG),
+ // don't follow them. If it is a ValidationRequired question, wait for the CNAME to be validated
+ // first before following it
+ if (followcname) AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
+
+ // If we are returning expired RRs, then remember the first expired qname we we can start the query again
+ if (rr->resrec.mortality == Mortality_Ghost && !q->firstExpiredQname.c[0] && (q->allowExpired == AllowExpired_AllowExpiredAnswers) && rr->resrec.RecordType != kDNSRecordTypePacketNegative)
+ {
+ debugf("AnswerCurrentQuestionWithResourceRecord: Keeping track of domain for expired RR %s for question %p", CRDisplayString(m,rr), q);
+ // Note: question->qname is already changed at this point if following a CNAME
+ AssignDomainName(&q->firstExpiredQname, rr->resrec.name); // Update firstExpiredQname
+ }
+ }
}
mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
@@ -4474,7 +4492,8 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
// response. A cache may be present that answers this question e.g., cache entry generated
// before the question became suppressed. We need to skip the suppressed questions here as
// the RMV event has already been generated.
- if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
+ if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q) &&
+ (q->allowExpired == AllowExpired_None || rr->resrec.mortality == Mortality_Mortal))
{
verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr));
q->FlappingInterface1 = mDNSNULL;
@@ -4503,11 +4522,11 @@ mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
}
if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results
{
- if (q->CurrentAnswers == 0)
+ if ((q->CurrentAnswers == 0) && mDNSOpaque16IsZero(q->TargetQID))
{
LogInfo("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents",
q->qname.c, DNSTypeName(q->qtype));
- ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0);
+ ReconfirmAntecedents(m, &q->qname, q->qnamehash, rr->resrec.InterfaceID, 0);
}
AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv);
}
@@ -4631,16 +4650,15 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou
while (*rp)
{
CacheRecord *const rr = *rp;
+ mDNSBool recordReleased = mDNSfalse;
mDNSs32 event = RRExpireTime(rr);
if (m->timenow - event >= 0) // If expired, delete it
{
- *rp = rr->next; // Cut it from the list
-
- verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s",
- m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr));
if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away
{
DNSQuestion *q = rr->CRActiveQuestion;
+ verbosedebugf("CheckCacheExpiration: Removing%7d %7d %p %s",
+ m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr));
// When a cache record is about to expire, we expect to do four queries at 80-82%, 85-87%, 90-92% and
// then 95-97% of the TTL. If the DNS server does not respond, then we will remove the cache entry
// before we pick a new DNS server. As the question interval is set to MaxQuestionInterval, we may
@@ -4657,9 +4675,30 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou
CacheRecordRmv(m, rr);
m->rrcache_active--;
}
- ReleaseCacheRecord(m, rr);
+
+ event += MAX_GHOST_TIME; // Adjust so we can check for a ghost expiration
+ if (rr->resrec.mortality == Mortality_Mortal || // Normal expired mortal record that needs released
+ (rr->resrec.mortality == Mortality_Ghost && m->timenow - event >= 0)) // A ghost record that expired more than MAX_GHOST_TIME ago
+ { // Release as normal
+ *rp = rr->next; // Cut it from the list before ReleaseCacheRecord
+ verbosedebugf("CheckCacheExpiration: Deleting (%s)%7d %7d %p %s",
+ MortalityDisplayString(rr->resrec.mortality),
+ m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr));
+ ReleaseCacheRecord(m, rr);
+ recordReleased = mDNStrue;
+ }
+ else // An immortal record needs to become a ghost when it expires
+ { // Don't release this entry
+ if (rr->resrec.mortality == Mortality_Immortal)
+ {
+ rr->resrec.mortality = Mortality_Ghost; // Expired immortal records become ghosts
+ verbosedebugf("CheckCacheExpiration: NOT Deleting (%s)%7d %7d %p %s",
+ MortalityDisplayString(rr->resrec.mortality),
+ m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr));
+ }
+ }
}
- else // else, not expired; see if we need to query
+ else // else, not expired; see if we need to query
{
// If waiting to delay delivery, do nothing until then
if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0)
@@ -4682,6 +4721,10 @@ mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGrou
}
}
}
+ }
+
+ if (!recordReleased) // Schedule if we did not release the record
+ {
verbosedebugf("CheckCacheExpiration:%6d %5d %s",
(event - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr));
if (m->rrcache_nextcheck[slot] - event > 0)
@@ -4893,12 +4936,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
{
// SecsSinceRcvd is whole number of elapsed seconds, rounded down
mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
- if (rr->resrec.rroriginalttl <= SecsSinceRcvd)
- {
- LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %s %d %d",
- rr->resrec.rroriginalttl, SecsSinceRcvd, CRDisplayString(m, rr), m->timenow, rr->TimeRcvd);
- continue; // Go to next one in loop
- }
+ if (rr->resrec.rroriginalttl <= SecsSinceRcvd && q->allowExpired != AllowExpired_AllowExpiredAnswers) continue; // Go to next one in loop
// If this record set is marked unique, then that means we can reasonably assume we have the whole set
// -- we don't need to rush out on the network and query immediately to see if there are more answers out there
@@ -4908,6 +4946,9 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
AnsweredFromCache = mDNStrue;
+#if AWD_METRICS
+ if (q->metrics.expiredAnswerState == ExpiredAnswer_Allowed) q->metrics.expiredAnswerState = ExpiredAnswer_AnsweredWithExpired;
+#endif
AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add);
if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
}
@@ -4930,6 +4971,21 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m)
if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; }
+ if (q->allowExpired == AllowExpired_AllowExpiredAnswers)
+ {
+ q->allowExpired = AllowExpired_MakeAnswersImmortal; // After looking through the cache for an answer, demote to make immortal
+ if (q->firstExpiredQname.c[0]) // If an original query name was saved on an expired answer, start it over in case it is updated
+ {
+ LogMsg("AnswerNewQuestion: Restarting original question %p firstExpiredQname %##s for allowExpiredAnswers question", q, &q->firstExpiredQname.c);
+ mDNS_StopQuery_internal(m, q); // Stop old query
+ AssignDomainName(&q->qname, &q->firstExpiredQname); // Update qname
+ q->qnamehash = DomainNameHashValue(&q->qname); // and namehash
+ mDNS_StartQuery_internal(m, q); // start new query
+ q->CNAMEReferrals = 0; // Reset referral count
+ q->firstExpiredQname.c[0] = 0; // Erase the domain name
+ }
+ }
+
// Note: When a query gets suppressed or retried with search domains, we de-activate the question.
// Hence we don't execute the following block of code for those cases.
if (ShouldQueryImmediately && ActiveQuestion(q))
@@ -7467,6 +7523,7 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
AuthRecord **nrp = &ResponseRecords;
#if POOF_ENABLED
+ mDNSBool notD2D = !mDNSPlatformInterfaceIsD2D(InterfaceID); // We don't run the POOF algorithm on D2D interfaces.
CacheRecord *ExpectedAnswers = mDNSNULL; // Records in our cache we expect to see updated
CacheRecord **eap = &ExpectedAnswers;
#endif // POOF_ENABLED
@@ -7626,18 +7683,21 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
if (QuestionNeedsMulticastResponse && !(query->h.flags.b[0] & kDNSFlag0_TC))
{
#if POOF_ENABLED
- CacheGroup *cg = CacheGroupForName(m, pktq.qnamehash, &pktq.qname);
- CacheRecord *cr;
-
- // Make a list indicating which of our own cache records we expect to see updated as a result of this query
- // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
- for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next)
- if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit)
- if (!cr->NextInKAList && eap != &cr->NextInKAList)
- {
- *eap = cr;
- eap = &cr->NextInKAList;
- }
+ if (notD2D)
+ {
+ CacheGroup *cg = CacheGroupForName(m, pktq.qnamehash, &pktq.qname);
+ CacheRecord *cr;
+
+ // Make a list indicating which of our own cache records we expect to see updated as a result of this query
+ // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
+ for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next)
+ if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit)
+ if (!cr->NextInKAList && eap != &cr->NextInKAList)
+ {
+ *eap = cr;
+ eap = &cr->NextInKAList;
+ }
+ }
#endif // POOF_ENABLED
// Check if this question is the same as any of mine.
@@ -7728,15 +7788,18 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con
ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
#if POOF_ENABLED
- // Having built our ExpectedAnswers list from the questions in this packet, we then remove
- // any records that are suppressed by the Known Answer list in this packet.
- eap = &ExpectedAnswers;
- while (*eap)
+ if (notD2D)
{
- CacheRecord *cr = *eap;
- if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec))
- { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; }
- else eap = &cr->NextInKAList;
+ // Having built our ExpectedAnswers list from the questions in this packet, we then remove
+ // any records that are suppressed by the Known Answer list in this packet.
+ eap = &ExpectedAnswers;
+ while (*eap)
+ {
+ CacheRecord *cr = *eap;
+ if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec))
+ { *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; }
+ else eap = &cr->NextInKAList;
+ }
}
#endif // POOF_ENABLED
@@ -7900,7 +7963,7 @@ exit:
}
#if POOF_ENABLED
- while (ExpectedAnswers)
+ while (ExpectedAnswers && notD2D)
{
CacheRecord *cr = ExpectedAnswers;
ExpectedAnswers = cr->NextInKAList;
@@ -8106,10 +8169,11 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C
if (!rr) NoCacheAnswer(m, &m->rec.r);
else
{
- RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer
- *rr = m->rec.r; // Block copy the CacheRecord object
- rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment
- rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header
+ RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer
+ *rr = m->rec.r; // Block copy the CacheRecord object
+ rr->resrec.rdata = saveptr; // Restore rr->resrec.rdata after the structure assignment
+ rr->resrec.name = cg->name; // And set rr->resrec.name to point into our CacheGroup header
+ rr->resrec.mortality = Mortality_Mortal;
// We need to add the anonymous info before we call CacheRecordAdd so that
// if it finds a matching question with this record, it bumps up the counters like
@@ -8176,6 +8240,7 @@ mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl)
rr->TimeRcvd = m->timenow;
rr->resrec.rroriginalttl = ttl;
rr->UnansweredQueries = 0;
+ if (rr->resrec.mortality != Mortality_Mortal) rr->resrec.mortality = Mortality_Immortal;
SetNextCacheCheckTimeForRecord(m, rr);
}
@@ -8246,7 +8311,7 @@ mDNSlocal mDNSBool IsResponseAcceptable(mDNS *const m, const CacheRecord *crlist
if (target && cr->resrec.rdatahash == rr->namehash && SameDomainName(target, rr->name))
{
- LogInfo("IsResponseAcceptable: Found a matching entry for %##s in the CacheFlushRecords %s", rr->name->c, CRDisplayString(m, cr));
+ LogDebug("IsResponseAcceptable: Found a matching entry for %##s in the CacheFlushRecords %s", rr->name->c, CRDisplayString(m, cr));
return (mDNStrue);
}
}
@@ -8744,6 +8809,12 @@ mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage
DNSQuestion *q;
m->mDNSStats.CacheRefreshed++;
+
+ if (rr->resrec.mortality == Mortality_Ghost && unicastQuestion && (unicastQuestion->allowExpired != AllowExpired_AllowExpiredAnswers) && !rr->DelayDelivery)
+ {
+ rr->DelayDelivery = NonZeroTime(m->timenow);
+ debugf("mDNSCoreReceiveCacheCheck: Reset DelayDelivery for mortalityExpired EXP:%d RR %s", m->timenow - RRExpireTime(rr), CRDisplayString(m, rr));
+ }
if (rr->resrec.rroriginalttl == 0) debugf("uDNS rescuing %s", CRDisplayString(m, rr));
RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl);
@@ -9513,6 +9584,12 @@ exit:
r1->resrec.rrtype == r2->resrec.rrtype &&
r1->resrec.rrclass == r2->resrec.rrclass)
{
+ if (r1->resrec.mortality == Mortality_Mortal && r2->resrec.mortality != Mortality_Mortal)
+ {
+ verbosedebugf("mDNSCoreReceiveResponse: R1(%p) is being immortalized by R2(%p)", r1, r2);
+ r1->resrec.mortality = Mortality_Immortal; // Immortalize the replacement record
+ }
+
// If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
// else, if record is old, mark it to be flushed
if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond)
@@ -9588,7 +9665,23 @@ exit:
}
else
{
+#if AWD_METRICS
+ if (r2->resrec.mortality == Mortality_Ghost)
+ {
+ DNSQuestion * q;
+ for (q = m->Questions; q; q=q->next)
+ {
+ if (!q->LongLived && ActiveQuestion(q) &&
+ ResourceRecordAnswersQuestion(&r2->resrec, q) &&
+ q->metrics.expiredAnswerState == ExpiredAnswer_AnsweredWithExpired)
+ {
+ q->metrics.expiredAnswerState = ExpiredAnswer_ExpiredAnswerChanged;
+ }
+ }
+ }
+#endif
// Old uDNS records are scheduled to be purged instead of given at most one second to live.
+ r2->resrec.mortality = Mortality_Mortal; // We want it purged, so remove any immortality
mDNS_PurgeCacheResourceRecord(m, r2);
purgedRecords = mDNStrue;
}
@@ -10207,7 +10300,7 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return;
if (mDNS_PacketLoggingEnabled)
- DumpPacket(m, mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
+ DumpPacket(mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space);
if (ptr)
@@ -10466,7 +10559,6 @@ mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr,
cr->TimeRcvd = m->timenow;
cr->DelayDelivery = 0;
cr->NextRequiredQuery = m->timenow;
- cr->LastUsed = m->timenow;
cr->CRActiveQuestion = mDNSNULL;
cr->UnansweredQueries = 0;
cr->LastUnansweredTime = 0;
@@ -10561,7 +10653,7 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNS
{
ifid = mDNSInterface_Any;
if (mDNS_PacketLoggingEnabled)
- DumpPacket(m, mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
+ DumpPacket(mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport);
// Note: mDNSCore also needs to get access to received unicast responses
}
@@ -11126,11 +11218,11 @@ mDNSlocal DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInter
curmatch = GetBestServer(m, name, InterfaceID, ServiceID, allValid, mDNSNULL, mDNStrue);
if (curmatch != mDNSNULL)
- LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr,
+ LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) for %##s", &curmatch->addr,
mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None",
InterfaceID, name);
else
- LogInfo("GetServerForName: no DNS server (Scope %s:%p) found for name %##s", ifname ? ifname : "None", InterfaceID, name);
+ LogInfo("GetServerForName: no DNS server (Scope %s:%p) for %##s", ifname ? ifname : "None", InterfaceID, name);
return(curmatch);
}
@@ -11159,14 +11251,14 @@ mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question)
if (curmatch != mDNSNULL)
{
- LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) found for name %##s (%s)",
+ LogInfo("GetServerForQuestion: %p DNS server (%p) %#a:%d (Penalty Time Left %d) (Scope %s:%p:%d) for %##s (%s)",
question, curmatch, &curmatch->addr, mDNSVal16(curmatch->port),
(curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None",
InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype));
}
else
{
- LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) found for name %##s (%s)",
+ LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p:%d) for %##s (%s)",
question, ifname ? ifname : "None", InterfaceID, question->ServiceID, name, DNSTypeName(question->qtype));
}
@@ -11218,14 +11310,14 @@ mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNS
// Some callers don't check for the qtype
if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA)
{
- LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype));
+ LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", q->qname.c, DNSTypeName(q->qtype));
return mDNSfalse;
}
// Private domains are exempted irrespective of what the DNSServer says
if (IsPrivateDomain(m, q))
{
- LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype));
+ LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, Private Domain", q->qname.c, DNSTypeName(q->qtype));
return mDNSfalse;
}
@@ -11238,20 +11330,20 @@ mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNS
// Check if the DNS Configuration allows A/AAAA queries to be sent
if ((q->qtype == kDNSType_A) && (d->req_A))
{
- LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows A queries", q->qname.c,
+ LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows A queries", q->qname.c,
DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port));
return mDNSfalse;
}
if ((q->qtype == kDNSType_AAAA) && (d->req_AAAA))
{
- LogInfo("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows AAAA queries", q->qname.c,
+ LogDebug("ShouldSuppressUnicastQuery: Query not suppressed for %##s, qtype %s, DNSServer %##s %#a:%d allows AAAA queries", q->qname.c,
DNSTypeName(q->qtype), d->domain.c, &d->addr, mDNSVal16(d->port));
return mDNSfalse;
}
#if USE_DNS64
if (DNS64IsQueryingARecord(q->dns64.state))
{
- LogInfo("ShouldSuppressUnicastQuery: DNS64 query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype));
+ LogDebug("ShouldSuppressUnicastQuery: DNS64 query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype));
return mDNSfalse;
}
#endif
@@ -11652,6 +11744,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question)
question->StopTime = (question->TimeoutQuestion) ? question->StopTime : 0;
#if AWD_METRICS
mDNSPlatformMemZero(&question->metrics, sizeof(question->metrics));
+ question->metrics.expiredAnswerState = (question->allowExpired != AllowExpired_None) ? ExpiredAnswer_Allowed : ExpiredAnswer_None;
#endif
// Need not initialize the DNS Configuration for Local Only OR P2P Questions when timeout not specified
@@ -11672,7 +11765,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question)
}
question->qDNSServer = GetServerForQuestion(m, question);
- LogInfo("InitDNSConfig: question %p %##s (%s) Timeout %d, DNS Server %#a:%d",
+ LogDebug("InitDNSConfig: question %p %##s (%s) Timeout %d, DNS Server %#a:%d",
question, question->qname.c, DNSTypeName(question->qtype), timeout,
question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
@@ -12113,7 +12206,7 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que
queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname;
isForCell = (question->qDNSServer && question->qDNSServer->cellIntf);
durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond;
- MetricsUpdateDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, durationMs, isForCell);
+ MetricsUpdateDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, question->metrics.expiredAnswerState, durationMs, isForCell);
}
#endif
// Take care to cut question from list *before* calling UpdateQuestionDuplicates
@@ -12333,7 +12426,7 @@ mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const cr)
mStatus status;
mDNS_Lock(m);
status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
- if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0);
+ if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, cr->resrec.InterfaceID, 0);
mDNS_Unlock(m);
return(status);
}
@@ -12346,7 +12439,7 @@ mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr
cr = FindIdenticalRecordInCache(m, rr);
debugf("mDNS_ReconfirmByValue: %p %s", cr, RRDisplayString(m, rr));
if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
- if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0);
+ if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, cr->resrec.InterfaceID, 0);
mDNS_Unlock(m);
return(status);
}
@@ -13152,6 +13245,7 @@ mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *se
}
else
{
+ rr->resrec.mortality = Mortality_Mortal;
mDNS_PurgeCacheResourceRecord(m, rr);
}
}
@@ -13349,7 +13443,7 @@ mDNSexport mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType)
// If the optional target host parameter is set, then the storage it points to must remain valid for the lifetime of the service registration
mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
const domainlabel *const name, const domainname *const type, const domainname *const domain,
- const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
+ const domainname *const host, mDNSIPPort port, RData *const txtrdata, const mDNSu8 txtinfo[], mDNSu16 txtlen,
AuthRecord *SubTypes, mDNSu32 NumSubTypes,
mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags)
{
@@ -13386,7 +13480,7 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
hostTTL = kHostNameTTL;
mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr);
- mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, recordType, artype, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_TXT, txtrdata, InterfaceID, kDNSType_TXT, kStandardTTL, recordType, artype, ServiceCallback, sr);
// If port number is zero, that means the client is really trying to do a RegisterNoSuchService
if (mDNSIPPortIsZero(port))
@@ -13596,7 +13690,9 @@ mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordS
else debugf("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c);
err = mDNS_RegisterService(m, sr, newname, &type, &domain,
- host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
+ host, sr->RR_SRV.resrec.rdata->u.srv.port,
+ (sr->RR_TXT.resrec.rdata != &sr->RR_TXT.rdatastorage) ? sr->RR_TXT.resrec.rdata : mDNSNULL,
+ sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
sr->SubTypes, sr->NumSubTypes,
sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, sr->flags);
@@ -14235,6 +14331,7 @@ mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const s
mDNS_RegisterService(m, srs,
&name, &SleepProxyServiceType, &localdomain,
mDNSNULL, m->SPSSocket->port, // Host, port
+ mDNSNULL,
(mDNSu8 *)"", 1, // TXT data, length
mDNSNULL, 0, // Subtypes (none)
mDNSInterface_Any, // Interface ID
@@ -14953,6 +15050,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
{
LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s, New DNS server %#a , Old DNS server %#a", CRDisplayString(m, cr),
&ptr->addr, (cr->resrec.rDNSServer != mDNSNULL ? &cr->resrec.rDNSServer->addr : mDNSNULL));
+ cr->resrec.mortality = Mortality_Mortal;
mDNS_PurgeCacheResourceRecord(m, cr);
}
else
@@ -15021,6 +15119,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
cr->resrec.rDNSServer = mDNSNULL;
}
+ cr->resrec.mortality = Mortality_Mortal;
PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue);
}
}
diff --git a/mDNSResponder/mDNSCore/mDNSDebug.h b/mDNSResponder/mDNSCore/mDNSDebug.h
index 68a696ef..d690fd2b 100755
--- a/mDNSResponder/mDNSCore/mDNSDebug.h
+++ b/mDNSResponder/mDNSCore/mDNSDebug.h
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2002-2015 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -99,12 +99,14 @@ extern "C" {
#define LogOperation(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__);} while (0)
#define LogSPS(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, __VA_ARGS__);} while (0)
#define LogInfo(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, __VA_ARGS__);} while (0)
+ #define LogDebug(... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_DEBUG, __VA_ARGS__);} while (0)
#elif (MDNS_GNU_VA_ARGS)
#define debug_noop( ARGS... ) ((void)0)
#define LogMsg( ARGS... ) LogMsgWithLevel(MDNS_LOG_MSG, ARGS)
#define LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS);} while (0)
#define LogSPS( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS, ARGS);} while (0)
#define LogInfo( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO, ARGS);} while (0)
+ #define LogDebug( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_DEBUG, ARGS);} while (0)
#else
#error Unknown variadic macros
#endif
@@ -116,10 +118,12 @@ extern "C" {
#define LogOperation (mDNS_LoggingEnabled == 0) ? ((void)0) : LogOperation_
#define LogSPS (mDNS_LoggingEnabled == 0) ? ((void)0) : LogSPS_
#define LogInfo (mDNS_LoggingEnabled == 0) ? ((void)0) : LogInfo_
+ #define LogDebug (mDNS_LoggingEnabled == 0) ? ((void)0) : LogDebug_
extern void LogMsg_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogSPS_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
extern void LogInfo_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+extern void LogDebug_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
#endif
#if MDNS_DEBUGMSGS
diff --git a/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h b/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h
index 1962bb19..511aa3b6 100755
--- a/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h
+++ b/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h
@@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 4 -*-
*
- * Copyright (c) 2002-2017 Apple Inc. All rights reserved.
+ * Copyright (c) 2002-2018 Apple Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -101,6 +101,10 @@ extern "C" {
#define MaximumRDSize 264
#endif
+#if !defined(MDNSRESPONDER_BTMM_SUPPORT)
+#define MDNSRESPONDER_BTMM_SUPPORT 0
+#endif
+
// ***************************************************************************
// Function scope indicators
@@ -1335,6 +1339,13 @@ typedef struct McastResolver
mDNSu32 timeout; // timeout value for questions
} McastResolver;
+enum {
+ Mortality_Mortal = 0, // This cache record can expire and get purged
+ Mortality_Immortal = 1, // Allow this record to remain in the cache indefinitely
+ Mortality_Ghost = 2 // An immortal record that has expired and can linger in the cache
+};
+typedef mDNSu8 MortalityState;
+
// scoped values for DNSServer matching
enum
{
@@ -1386,6 +1397,7 @@ typedef struct
struct ResourceRecord_struct
{
mDNSu8 RecordType; // See kDNSRecordTypes enum.
+ MortalityState mortality; // Mortality of this resource record (See MortalityState enum)
mDNSu16 rrtype; // See DNS_TypeValues enum.
mDNSu16 rrclass; // See DNS_ClassValues enum.
mDNSu32 rroriginalttl; // In seconds
@@ -1399,7 +1411,6 @@ struct ResourceRecord_struct
// ReconfirmAntecedents(), etc., use rdatahash as a pre-flight check to see
// whether it's worth doing a full SameDomainName() call. If the rdatahash
// is not a correct case-insensitive name hash, they'll get false negatives.
-
// Grouping pointers together at the end of the structure improves the memory layout efficiency
mDNSInterfaceID InterfaceID; // Set if this RR is specific to one interface
// For records received off the wire, InterfaceID is *always* set to the receiving interface
@@ -1638,7 +1649,7 @@ struct CacheRecord_struct
mDNSs32 TimeRcvd; // In platform time units
mDNSs32 DelayDelivery; // Set if we want to defer delivery of this answer to local clients
mDNSs32 NextRequiredQuery; // In platform time units
- mDNSs32 LastUsed; // In platform time units
+ // Extra four bytes here (on 64bit)
DNSQuestion *CRActiveQuestion; // Points to an active question referencing this answer. Can never point to a NewQuestion.
mDNSs32 LastUnansweredTime; // In platform time units; last time we incremented UnansweredQueries
mDNSu8 UnansweredQueries; // Number of times we've issued a query for this record without getting an answer
@@ -1812,7 +1823,12 @@ typedef enum {
DNSPUSH_ESTABLISHED = 4
} DNSPush_State;
-
+enum {
+ AllowExpired_None = 0, // Don't allow expired answers or mark answers immortal (behave normally)
+ AllowExpired_MakeAnswersImmortal = 1, // Any answers to this question get marked as immortal
+ AllowExpired_AllowExpiredAnswers = 2 // Allow already expired answers from the cache
+};
+typedef mDNSu8 AllowExpiredState;
#define HMAC_LEN 64
#define HMAC_IPAD 0x36
@@ -1899,13 +1915,26 @@ typedef enum { DNSSECValNotRequired = 0, DNSSECValRequired, DNSSECValInProgress,
#define AWD_METRICS (USE_AWD && TARGET_OS_IOS)
#if AWD_METRICS
-typedef struct
+
+enum
{
- domainname * originalQName; // Name of original A/AAAA record if this question is for a CNAME record.
- mDNSu32 querySendCount; // Number of queries that have been sent to DNS servers so far.
- mDNSs32 firstQueryTime; // The time when the first query was sent to a DNS server.
- mDNSBool answered; // Has this question been answered?
+ ExpiredAnswer_None = 0, // No expired answers used
+ ExpiredAnswer_Allowed = 1, // An expired answer is allowed by this request
+ ExpiredAnswer_AnsweredWithExpired = 2, // Question was answered with an expired answer
+ ExpiredAnswer_ExpiredAnswerChanged = 3, // Expired answer changed on refresh
+
+ ExpiredAnswer_EnumCount
+};
+typedef mDNSu8 ExpiredAnswerMetric;
+typedef struct
+{
+ domainname * originalQName; // Name of original A/AAAA record if this question is for a CNAME record.
+ mDNSu32 querySendCount; // Number of queries that have been sent to DNS servers so far.
+ mDNSs32 firstQueryTime; // The time when the first query was sent to a DNS server.
+ mDNSBool answered; // Has this question been answered?
+ ExpiredAnswerMetric expiredAnswerState; // Expired answer state (see ExpiredAnswerMetric above)
+
} uDNSMetrics;
#endif
@@ -1977,6 +2006,7 @@ struct DNSQuestion_struct
mDNSu16 noServerResponse; // At least one server did not respond.
mDNSu16 triedAllServersOnce; // Tried all DNS servers once
mDNSu8 unansweredQueries; // The number of unanswered queries to this server
+ AllowExpiredState allowExpired; // Allow expired answers state (see enum AllowExpired_None, etc. above)
ZoneData *nta; // Used for getting zone data for private or LLQ query
mDNSAddr servAddr; // Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query
@@ -2016,6 +2046,7 @@ struct DNSQuestion_struct
mDNSIPPort TargetPort; // Must be set if Target is set
mDNSOpaque16 TargetQID; // Must be set if Target is set
domainname qname;
+ domainname firstExpiredQname; // first expired qname in request chain
mDNSu16 qtype;
mDNSu16 qclass;
mDNSBool LongLived; // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer.
@@ -2778,7 +2809,7 @@ extern void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDN
extern mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType);
extern mStatus mDNS_RegisterService (mDNS *const m, ServiceRecordSet *sr,
const domainlabel *const name, const domainname *const type, const domainname *const domain,
- const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
+ const domainname *const host, mDNSIPPort port, RData *txtrdata, const mDNSu8 txtinfo[], mDNSu16 txtlen,
AuthRecord *SubTypes, mDNSu32 NumSubTypes,
mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags);
extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl, mDNSu32 flags);
@@ -2939,6 +2970,7 @@ extern char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataB
#define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer)
#define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer)
#define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer)
+#define MortalityDisplayString(M) (M == Mortality_Mortal ? "mortal" : (M == Mortality_Immortal ? "immortal" : "ghost"))
extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2);
extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText);
extern mDNSBool mDNSv4AddrIsRFC1918(const mDNSv4Addr * const addr); // returns true for RFC1918 private addresses
@@ -3601,17 +3633,17 @@ struct CompileTimeAssertionChecks_mDNS
char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1208) ? 1 : -1];
char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 232) ? 1 : -1];
char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 232) ? 1 : -1];
- char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 912) ? 1 : -1];
+ char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 1168) ? 1 : -1];
- char sizecheck_ZoneData [(sizeof(ZoneData) <= 1744) ? 1 : -1];
+ char sizecheck_ZoneData [(sizeof(ZoneData) <= 2000) ? 1 : -1];
char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 200) ? 1 : -1];
char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1];
char sizecheck_DNSServer [(sizeof(DNSServer) <= 330) ? 1 : -1];
- char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 7376) ? 1 : -1];
+ char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 8400) ? 1 : -1];
char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5540) ? 1 : -1];
char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1];
#if APPLE_OSX_mDNSResponder
- char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1256) ? 1 : -1];
+ char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1512) ? 1 : -1];
#endif
};
diff --git a/mDNSResponder/mDNSCore/uDNS.c b/mDNSResponder/mDNSCore/uDNS.c
index 64dae891..cd91f4da 100755
--- a/mDNSResponder/mDNSCore/uDNS.c
+++ b/mDNSResponder/mDNSCore/uDNS.c
@@ -5807,7 +5807,7 @@ struct CompileTimeAssertionChecks_uDNS
// other overly-large structures instead of having a pointer to them, can inadvertently
// cause structure sizes (and therefore memory usage) to balloon unreasonably.
char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1];
- char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1];
+ char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 6136) ? 1 : -1];
};
#if COMPILER_LIKES_PRAGMA_MARK