diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-09-19 08:53:26 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-09-20 11:23:32 +0200 |
commit | f01edf10244ccd53e098abdc1773c1aa0e4c5f8d (patch) | |
tree | 958a1ee323520629c4f027de1d4c56715949aa5c /mDNSResponder/mDNSCore | |
parent | mDNSResponder: Update to v625.41.2 (diff) | |
download | rtems-libbsd-f01edf10244ccd53e098abdc1773c1aa0e4c5f8d.tar.bz2 |
mDNSResponder: Update to v765.1.2
The sources can be obtained via:
https://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-765.1.2.tar.gz
Move mDNS_StartResolveService() and mDNS_StopResolveService() to an
RTEMS-specific file (rtemsbsd/mdns/mDNSResolveService.c) using the
v576.30.4 implementation. Apple removed these functions without
explanation.
Update #3522.
Diffstat (limited to 'mDNSResponder/mDNSCore')
-rw-r--r-- | mDNSResponder/mDNSCore/DNSCommon.c | 59 | ||||
-rw-r--r-- | mDNSResponder/mDNSCore/DNSCommon.h | 25 | ||||
-rw-r--r-- | mDNSResponder/mDNSCore/anonymous.c | 7 | ||||
-rw-r--r-- | mDNSResponder/mDNSCore/dnsproxy.c | 28 | ||||
-rw-r--r-- | mDNSResponder/mDNSCore/dnssec.c | 7 | ||||
-rwxr-xr-x | mDNSResponder/mDNSCore/mDNS.c | 1826 | ||||
-rwxr-xr-x | mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h | 247 | ||||
-rw-r--r-- | mDNSResponder/mDNSCore/nsec3.c | 4 | ||||
-rwxr-xr-x | mDNSResponder/mDNSCore/uDNS.c | 801 | ||||
-rwxr-xr-x | mDNSResponder/mDNSCore/uDNS.h | 14 |
10 files changed, 1734 insertions, 1284 deletions
diff --git a/mDNSResponder/mDNSCore/DNSCommon.c b/mDNSResponder/mDNSCore/DNSCommon.c index d364ee02..a2b703f7 100644 --- a/mDNSResponder/mDNSCore/DNSCommon.c +++ b/mDNSResponder/mDNSCore/DNSCommon.c @@ -43,6 +43,7 @@ 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; +mDNSexport const mDNSInterfaceID mDNSInterface_BLE = (mDNSInterfaceID)-6; // 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 @@ -107,6 +108,8 @@ mDNSexport const mDNSOpaque16 DNSSecQFlags = { { kDNSFlag0_QR_Query | kDNS mDNSexport const mDNSOpaque16 ResponseFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } }; mDNSexport const mDNSOpaque16 UpdateReqFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Update, 0 } }; mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, 0 } }; +mDNSexport const mDNSOpaque16 SubscribeFlags = { { kDNSFlag0_QR_Query | kDNSFlag0_OP_Subscribe, 0 } }; +mDNSexport const mDNSOpaque16 UnSubscribeFlags= { { kDNSFlag0_QR_Query | kDNSFlag0_OP_UnSubscribe, 0 } }; mDNSexport const mDNSOpaque64 zeroOpaque64 = { { 0 } }; @@ -541,7 +544,7 @@ mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RD } break; - default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data); + default: mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %.*s", rr->rdlength, rr->rdlength, rd->data); // Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.'; break; @@ -803,6 +806,7 @@ mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstri mDNSu8 c = (mDNSu8)*cstr++; // Read the character if (c == '\\') // If escape character, check next character { + if (*cstr == '\0') break; // If this is the end of the string, then break c = (mDNSu8)*cstr++; // Assume we'll just take the next character if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1])) { // If three decimal digits, @@ -815,7 +819,7 @@ mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstri } *ptr++ = c; // Write the character } - if (*cstr) cstr++; // Skip over the trailing dot (if present) + if (*cstr == '.') cstr++; // Skip over the trailing dot (if present) if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL) // If illegal label, abort return(mDNSNULL); *lengthbyte = (mDNSu8)(ptr - lengthbyte - 1); // Fill in the length byte @@ -1165,7 +1169,8 @@ mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3 const mDNSu8 hash[NSEC3_MAX_HASH_LEN], int *dlen) { AlgContext *ctx; - int i; + unsigned int i; + unsigned int iterations; domainname lname; mDNSu8 *p = (mDNSu8 *)&nsec3->salt; const mDNSu8 *digest; @@ -1183,7 +1188,8 @@ mDNSexport const mDNSu8 *NSEC3HashName(const domainname *name, rdataNSEC3 *nsec3 // 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++) + iterations = swap16(nsec3->iterations); + for (i = 0; i <= iterations; i++) { ctx = AlgCreate(DIGEST_ALG, nsec3->alg); if (!ctx) @@ -1367,17 +1373,14 @@ mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mD if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly) { LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype); - return; } else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P) { LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype); - return; } else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly)) { LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype); - return; } // Don't try to store a TTL bigger than we can represent in platform time units @@ -1467,8 +1470,6 @@ mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID I q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNSfalse; q->SuppressUnusable = mDNSfalse; - q->DenyOnCellInterface = mDNSfalse; - q->DenyOnExpInterface = mDNSfalse; q->SearchListIndex = 0; q->AppendSearchDomains = 0; q->RetryWithSearchDomains = mDNSfalse; @@ -1779,7 +1780,7 @@ mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records // are handled in LocalOnlyRecordAnswersQuestion - if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + if (LocalOnlyOrP2PInterface(rr->InterfaceID)) { LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); return mDNSfalse; @@ -1914,7 +1915,7 @@ mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, { // LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records // are handled in LocalOnlyRecordAnswersQuestion - if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P)) + if (LocalOnlyOrP2PInterface(rr->InterfaceID)) { LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID); return mDNSfalse; @@ -2142,7 +2143,6 @@ mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const return(mDNSNULL); } -// Put a string of dot-separated labels as length-prefixed labels // domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't) // msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers) // end points to the end of the message so far @@ -2843,7 +2843,7 @@ mDNSlocal mDNSu8 *SanityCheckBitMap(const mDNSu8 *bmap, const mDNSu8 *end, int l // (domainnames are expanded to 255 bytes) when stored in memory. // // This function can also be called with "NULL" msg to parse a single resource record pointed to by ptr. -// The caller can do this only if the names in the resource records are compressed and validity of the +// The caller can do this only if the names in the resource records are not compressed and validity of the // resource record has already been done before. DNSSEC currently uses it this way. mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *end, LargeCacheRecord *const largecr, mDNSu16 rdlength) @@ -3348,6 +3348,12 @@ mDNSexport mDNSBool SetRData(const DNSMessage *const msg, const mDNSu8 *ptr, con dlen = DomainNameLength(&name); rlen = end - ptr; rr->resrec.rdlength = dlen + rlen; + if (rr->resrec.rdlength > MaximumRDSize) + { + LogInfo("SetRData: Malformed TSIG/TKEY rdlength %d, rr->resrec.rdlength %d, " + "bmaplen %d, name %##s", rdlength, rr->resrec.rdlength, name.c); + goto fail; + } AssignDomainName((domainname *)rdb->data, &name); mDNSPlatformMemCopy(rdb->data + dlen, ptr, rlen); break; @@ -3626,7 +3632,7 @@ mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end) mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label) { int i; - LogMsg("%2d %s", count, label); + 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, @@ -3634,9 +3640,11 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, // 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) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); + if (ptr) + LogInfo("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r)); } - if (!ptr) LogMsg("DumpRecords: ERROR: Premature end of packet data"); + if (!ptr) + LogInfo("DumpRecords: ERROR: Premature end of packet data"); return(ptr); } @@ -3646,7 +3654,9 @@ mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, (X) == kDNSFlag0_OP_Status ? "Status " : \ (X) == kDNSFlag0_OP_Unused3 ? "Unused3 " : \ (X) == kDNSFlag0_OP_Notify ? "Notify " : \ - (X) == kDNSFlag0_OP_Update ? "Update " : "?? " ) + (X) == kDNSFlag0_OP_Update ? "Update " : \ + (X) == kDNSFlag0_OP_Subscribe? "Subscribe": \ + (X) == kDNSFlag0_OP_UnSubscribe? "UnSubscribe" : "?? " ) #define DNS_RC_Name(X) ( \ (X) == kDNSFlag1_RC_NoErr ? "NoErr" : \ @@ -3678,7 +3688,7 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t if (dstaddr || !mDNSIPPortIsZero(dstport)) dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0; - LogMsg("-- %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 --", + 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, DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask), msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query", @@ -3697,16 +3707,16 @@ mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *t (msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : "" ); - LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); + LogInfo("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions"); for (i = 0; i < msg->h.numQuestions && ptr; i++) { ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q); - if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); + if (ptr) LogInfo("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype)); } 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"); - LogMsg("--------------"); + LogInfo("--------------"); } // *************************************************************************** @@ -3896,6 +3906,10 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (e - m->NextScheduledSPS > 0) e = m->NextScheduledSPS; if (e - m->NextScheduledKA > 0) e = m->NextScheduledKA; +#if BONJOUR_ON_DEMAND + if (m->NextBonjourDisableTime && (e - m->NextBonjourDisableTime > 0)) e = m->NextBonjourDisableTime; +#endif // BONJOUR_ON_DEMAND + // NextScheduledSPRetry only valid when DelaySleep not set if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry; if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep; @@ -3911,6 +3925,9 @@ mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m) if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse; } if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime; + + if (m->NextBLEServiceTime && (e - m->NextBLEServiceTime > 0)) e = m->NextBLEServiceTime; + return(e); } diff --git a/mDNSResponder/mDNSCore/DNSCommon.h b/mDNSResponder/mDNSCore/DNSCommon.h index e51b06dd..2da19700 100644 --- a/mDNSResponder/mDNSCore/DNSCommon.h +++ b/mDNSResponder/mDNSCore/DNSCommon.h @@ -40,17 +40,19 @@ extern "C" { typedef enum { - kDNSFlag0_QR_Mask = 0x80, // Query or response? - kDNSFlag0_QR_Query = 0x00, - kDNSFlag0_QR_Response = 0x80, - - kDNSFlag0_OP_Mask = 0x78, // Operation type - kDNSFlag0_OP_StdQuery = 0x00, - kDNSFlag0_OP_Iquery = 0x08, - kDNSFlag0_OP_Status = 0x10, - kDNSFlag0_OP_Unused3 = 0x18, - kDNSFlag0_OP_Notify = 0x20, - kDNSFlag0_OP_Update = 0x28, + kDNSFlag0_QR_Mask = 0x80, // Query or response? + kDNSFlag0_QR_Query = 0x00, + kDNSFlag0_QR_Response = 0x80, + + kDNSFlag0_OP_Mask = 0x78, // Operation type + kDNSFlag0_OP_StdQuery = 0x00, + kDNSFlag0_OP_Subscribe = 0x06, + kDNSFlag0_OP_UnSubscribe = 0x07, + kDNSFlag0_OP_Iquery = 0x08, + kDNSFlag0_OP_Status = 0x10, + kDNSFlag0_OP_Unused3 = 0x18, + kDNSFlag0_OP_Notify = 0x20, + kDNSFlag0_OP_Update = 0x28, kDNSFlag0_QROP_Mask = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask, @@ -84,6 +86,7 @@ typedef enum TSIG_ErrBadTime = 18 } TSIG_ErrorCode; + // *************************************************************************** #if COMPILER_LIKES_PRAGMA_MARK #pragma mark - diff --git a/mDNSResponder/mDNSCore/anonymous.c b/mDNSResponder/mDNSCore/anonymous.c index 779e4ea6..aaaebc27 100644 --- a/mDNSResponder/mDNSCore/anonymous.c +++ b/mDNSResponder/mDNSCore/anonymous.c @@ -71,7 +71,7 @@ mDNSlocal mDNSBool InitializeNSEC3Record(ResourceRecord *rr, const mDNSu8 *AnonD // 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); + LogMsg("InitializeNSEC3Record: NSEC3HashName failed for %##s", rr->name->c); return mDNSfalse; } if (hlen != SHA1_HASH_LENGTH) @@ -280,9 +280,10 @@ mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNS int AnonDataLen; rdataNSEC3 *nsec3; int hlen; - const mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; int nxtLength; mDNSu8 *nxtName; + mDNSu8 hashName[NSEC3_MAX_HASH_LEN]; + mDNSPlatformMemZero(hashName, sizeof(hashName)); debugf("AnonInfoAnswersQuestion: question qname %##s", q->qname.c); @@ -403,7 +404,7 @@ mDNSexport int AnonInfoAnswersQuestion(const ResourceRecord *const rr, const DNS if (!NSEC3HashName(nsec3RR->name, nsec3, AnonData, AnonDataLen, hashName, &hlen)) { - LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for ##s", nsec3RR->name->c); + LogMsg("AnonInfoAnswersQuestion: NSEC3HashName failed for %##s", nsec3RR->name->c); return mDNSfalse; } if (hlen != SHA1_HASH_LENGTH) diff --git a/mDNSResponder/mDNSCore/dnsproxy.c b/mDNSResponder/mDNSCore/dnsproxy.c index 5b358864..05b70dd9 100644 --- a/mDNSResponder/mDNSCore/dnsproxy.c +++ b/mDNSResponder/mDNSCore/dnsproxy.c @@ -553,14 +553,21 @@ mDNSlocal mDNSBool CheckDNSProxyIpIntf(const mDNS *const m, mDNSInterfaceID Inte 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]); + LogInfo("CheckDNSProxyIpIntf: Check for ifindex[%d] in stored input interface list: [%d] [%d] [%d] [%d] [%d]", + ip_ifindex, 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 > 0) { - if (ip_ifindex == m->dp_ipintf[i]) - return mDNStrue; + for (i = 0; i < MaxIp; i++) + { + if (ip_ifindex == m->dp_ipintf[i]) + return mDNStrue; + } } + + LogMsg("CheckDNSProxyIpIntf: ifindex[%d] not in stored input interface list: [%d] [%d] [%d] [%d] [%d]", + ip_ifindex, m->dp_ipintf[0], m->dp_ipintf[1], m->dp_ipintf[2], m->dp_ipintf[3], m->dp_ipintf[4]); + return mDNSfalse; } @@ -583,7 +590,10 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, 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)) + { + LogMsg("ProxyCallbackCommon: Rejecting DNS Query coming from InterfaceID %p", InterfaceID); return; + } if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { @@ -691,8 +701,7 @@ mDNSlocal void ProxyCallbackCommon(mDNS *const m, void *socket, void *const pkt, 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 + // Set ReturnIntermed so that we get the negative responses pc->q.ReturnIntermed = mDNStrue; pc->q.ProxyQuestion = mDNStrue; pc->q.ProxyDNSSECOK = pc->DNSSECOK; @@ -735,9 +744,10 @@ mDNSexport void ProxyTCPCallback(mDNS *const m, void *socket, void *const pkt, c 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 + + // If the connection was closed from the other side or incoming packet does not match stored input interface list, locate the client // state and free it. - if ((end - (mDNSu8 *)pkt) == 0) + if (((end - (mDNSu8 *)pkt) == 0) || (!CheckDNSProxyIpIntf(m, InterfaceID))) { DNSProxyClient **ppc = &DNSProxyClients; DNSProxyClient **prevpc; diff --git a/mDNSResponder/mDNSCore/dnssec.c b/mDNSResponder/mDNSCore/dnssec.c index bf9acfe8..514a488c 100644 --- a/mDNSResponder/mDNSCore/dnssec.c +++ b/mDNSResponder/mDNSCore/dnssec.c @@ -87,6 +87,7 @@ mDNSlocal mDNSBool TrustedKeyPresent(mDNS *const m, DNSSECVerifier *dv); mDNSlocal mStatus ValidateDS(DNSSECVerifier *dv); mDNSlocal void DNSSECNegativeValidationCB(mDNS *const m, DNSSECVerifier *dv, CacheGroup *cg, ResourceRecord *answer, DNSSECStatus status); mDNSlocal RRVerifier* CopyRRVerifier(RRVerifier *from); +mDNSlocal void FreeDNSSECAuthChainInfo(AuthChain *ac); // Currently we use this to convert a RRVerifier to resource record so that we can // use the standard DNS utility functions @@ -300,6 +301,8 @@ mDNSlocal AuthChain *AuthChainCopy(AuthChain *ae) if (!ac) { LogMsg("AuthChainCopy: AuthChain alloc failure"); + if (retac) + FreeDNSSECAuthChainInfo(retac); return mDNSfalse; } @@ -2523,7 +2526,9 @@ mDNSlocal void FinishDNSSECVerification(mDNS *const m, DNSSECVerifier *dv) LogDNSSEC("FinishDNSSECVerification: all rdata sets available for sig verification for %##s (%s)", dv->origName.c, DNSTypeName(dv->origType)); - mDNS_StopQuery(m, &dv->q); + // Stop outstanding query if one exists + if (dv->q.ThisQInterval != -1) + mDNS_StopQuery(m, &dv->q); if (ValidateSignature(dv, &resultKey, &resultRRSig) == mStatus_NoError) { rdataDNSKey *key; diff --git a/mDNSResponder/mDNSCore/mDNS.c b/mDNSResponder/mDNSCore/mDNS.c index 6b02be46..d1841a5e 100755 --- a/mDNSResponder/mDNSCore/mDNS.c +++ b/mDNSResponder/mDNSCore/mDNS.c @@ -49,6 +49,9 @@ #if APPLE_OSX_mDNSResponder #include <WebFilterDNS/WebFilterDNS.h> +// Delay in seconds before disabling multicast after there are no active queries or registrations. +#define BONJOUR_DISABLE_DELAY 60 + #if !NO_WCF WCFConnection *WCFConnectionNew(void) __attribute__((weak_import)); void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); @@ -69,7 +72,7 @@ void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import)); // Forward declarations mDNSlocal void BeginSleepProcessing(mDNS *const m); mDNSlocal void RetrySPSRegistrations(mDNS *const m); -mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password); +mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly); mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q); mDNSlocal void mDNS_PurgeForQuestion(mDNS *const m, DNSQuestion *q); @@ -106,16 +109,39 @@ mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *et #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 +#define kDefaultRequestUnicastCount SET_QU_IN_FIRST_QUERY + +// The time needed to offload records to a sleep proxy after powerd sends the kIOMessageSystemWillSleep notification +#define DARK_WAKE_DELAY_SLEEP 5 +#define kDarkWakeDelaySleep (mDNSPlatformOneSecond * DARK_WAKE_DELAY_SLEEP) + +// The maximum number of times we delay probing to prevent spurious conflicts due to stale packets +#define MAX_CONFLICT_PROCESSING_DELAYS 3 + +// RFC 6762 defines Passive Observation Of Failures (POOF) +// +// A host observes the multicast queries issued by the other hosts on +// the network. One of the major benefits of also sending responses +// using multicast is that it allows all hosts to see the responses +// (or lack thereof) to those queries. +// +// If a host sees queries, for which a record in its cache would be +// expected to be given as an answer in a multicast response, but no +// such answer is seen, then the host may take this as an indication +// that the record may no longer be valid. +// +// After seeing two or more of these queries, and seeing no multicast +// response containing the expected answer within ten seconds, then even +// though its TTL may indicate that it is not yet due to expire, that +// record SHOULD be flushed from the cache. +// +// <https://tools.ietf.org/html/rfc6762#section-10.5> +#define POOF_ENABLED 1 mDNSexport const char *const mDNS_DomainTypeNames[] = { @@ -294,13 +320,13 @@ mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const Resourc // Returns the AuthGroup in which the AuthRecord was inserted mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) { + (void)m; AuthGroup *ag; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); ag = AuthGroupForRecord(r, slot, &rr->resrec); if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec); // If we don't have a AuthGroup for this name, make one now if (ag) { - LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr)); *(ag->rrauth_tail) = rr; // Append this record to tail of cache slot list ag->rrauth_tail = &(rr->next); // Advance tail pointer } @@ -310,13 +336,12 @@ mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *r mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr) { AuthGroup *a; - AuthGroup **ag = &a; AuthRecord **rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; } - rp = &(*ag)->members; + rp = &a->members; while (*rp) { if (*rp != rr) @@ -330,7 +355,7 @@ mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *r } } // TBD: If there are no more members, release authgroup ? - (*ag)->rrauth_tail = rp; + a->rrauth_tail = rp; return a; } @@ -412,14 +437,15 @@ mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID Interfa } // Caller should hold the lock -mDNSlocal void GenerateNegativeResponse(mDNS *const m, QC_result qc) +mDNSlocal void GenerateNegativeResponse(mDNS *const m, mDNSInterfaceID InterfaceID, QC_result qc) { DNSQuestion *q; if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; } q = m->CurrentQuestion; LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); - MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL); + MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, InterfaceID, mDNSNULL); + // We need to force the response through in the following cases // // a) SuppressUnusable questions that are suppressed @@ -447,28 +473,10 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re UDPSocket *sock = q->LocalSocket; mDNSOpaque16 id = q->TargetQID; #if TARGET_OS_EMBEDDED - domainname *originalQName; + uDNSMetrics metrics; #endif - // 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. - // This is a stupid hack and we should get rid of it. - // The chance of there being a second unicast UDP packet already waiting in the kernel before we’ve - // finished processing the previous one is virtually nil, and will only happen by luck on very rare - // occasions when running on a machine with a fast network connection and a slow or busy processor. - // The idea that we’d rely for correctness on this random chance event occurring is ridiculous. - // -- SC - 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; - } + q->LocalSocket = 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, @@ -489,29 +497,24 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr)); #if TARGET_OS_EMBEDDED - if (q->metrics.originalQName) - { - originalQName = q->metrics.originalQName; - q->metrics.originalQName = mDNSNULL; - } - else + if ((q->CNAMEReferrals == 0) && !q->metrics.originalQName) { - mDNSu16 qNameLen; + domainname * qName; + mDNSu16 qNameLen; qNameLen = DomainNameLength(&q->qname); if ((qNameLen > 0) && (qNameLen <= MAX_DOMAIN_NAME)) { - originalQName = mDNSPlatformMemAllocate(qNameLen); - if (originalQName) + qName = mDNSPlatformMemAllocate(qNameLen); + if (qName) { - mDNSPlatformMemCopy(originalQName->c, q->qname.c, qNameLen); + mDNSPlatformMemCopy(qName->c, q->qname.c, qNameLen); + q->metrics.originalQName = qName; } } - else - { - originalQName = mDNSNULL; - } } + metrics = q->metrics; + mDNSPlatformMemZero(&q->metrics, sizeof(q->metrics)); #endif mDNS_StopQuery_internal(m, q); // Stop old query AssignDomainName(&q->qname, &rr->rdata->u.name); // Update qname @@ -530,19 +533,96 @@ mDNSexport void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, Re // because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero q->CNAMEReferrals = c; #if TARGET_OS_EMBEDDED - q->metrics.originalQName = originalQName; + q->metrics = metrics; #endif 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; + // If our new query is a duplicate, then it can't have a socket of its own, so we have to close the one we saved. + if (q->DuplicateOf) mDNSPlatformUDPClose(sock); + else + { + // Transplant the old socket into the new question, and copy the query ID across too. + // No need to close the old q->LocalSocket value because it won't have been created yet (they're made lazily on-demand). + q->LocalSocket = sock; + q->TargetQID = id; + } } } } +#ifdef USE_LIBIDN + +#include <unicode/uidna.h> + +// #define DEBUG_PUNYCODE 1 + +mDNSlocal mDNSu8 *PunycodeConvert(const mDNSu8 *const src, mDNSu8 *const dst, const mDNSu8 *const end) +{ + UErrorCode errorCode = U_ZERO_ERROR; + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + UIDNA *uts46 = uidna_openUTS46(UIDNA_USE_STD3_RULES|UIDNA_NONTRANSITIONAL_TO_UNICODE, &errorCode); + int32_t len = uidna_nameToASCII_UTF8(uts46, (const char *)src+1, src[0], (char *)dst+1, end-(dst+1), &info, &errorCode); + uidna_close(uts46); + #if DEBUG_PUNYCODE + if (errorCode) LogMsg("uidna_nameToASCII_UTF8(%##s) failed errorCode %d", src, errorCode); + if (info.errors) LogMsg("uidna_nameToASCII_UTF8(%##s) failed info.errors 0x%08X", src, info.errors); + if (len > MAX_DOMAIN_LABEL) LogMsg("uidna_nameToASCII_UTF8(%##s) result too long %d", src, len); + #endif + if (errorCode || info.errors || len > MAX_DOMAIN_LABEL) return mDNSNULL; + *dst = len; + return(dst + 1 + len); +} + +mDNSlocal mDNSBool IsHighASCIILabel(const mDNSu8 *d) +{ + int i; + for (i=1; i<=d[0]; i++) if (d[i] & 0x80) return mDNStrue; + return mDNSfalse; +} + +mDNSlocal const mDNSu8 *FindLastHighASCIILabel(const domainname *const d) +{ + const mDNSu8 *ptr = d->c; + const mDNSu8 *ans = mDNSNULL; + while (ptr[0]) + { + const mDNSu8 *const next = ptr + 1 + ptr[0]; + if (ptr[0] > MAX_DOMAIN_LABEL || next >= d->c + MAX_DOMAIN_NAME) return mDNSNULL; + if (IsHighASCIILabel(ptr)) ans = ptr; + ptr = next; + } + return ans; +} + +mDNSlocal mDNSBool PerformNextPunycodeConversion(const DNSQuestion *const q, domainname *const newname) +{ + const mDNSu8 *h = FindLastHighASCIILabel(&q->qname); + #if DEBUG_PUNYCODE + LogMsg("PerformNextPunycodeConversion: %##s (%s) Last High-ASCII Label %##s", q->qname.c, DNSTypeName(q->qtype), h); + #endif + if (!h) return mDNSfalse; // There are no high-ascii labels to convert + + mDNSu8 *const dst = PunycodeConvert(h, newname->c + (h - q->qname.c), newname->c + MAX_DOMAIN_NAME); + if (!dst) + return mDNSfalse; // The label was not convertible to Punycode + else + { + // If Punycode conversion of final eligible label was successful, copy the rest of the domainname + const mDNSu8 *const src = h + 1 + h[0]; + const mDNSu8 remainder = DomainNameLength((domainname*)src); + if (dst + remainder > newname->c + MAX_DOMAIN_NAME) return mDNSfalse; // Name too long -- cannot be converted to Punycode + + mDNSPlatformMemCopy(newname->c, q->qname.c, h - q->qname.c); // Fill in the leading part + mDNSPlatformMemCopy(dst, src, remainder); // Fill in the trailing part + #if DEBUG_PUNYCODE + LogMsg("PerformNextPunycodeConversion: %##s converted to %##s", q->qname.c, newname->c); + #endif + return mDNStrue; + } +} + +#endif // USE_LIBIDN + // For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord // Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord) @@ -766,6 +846,8 @@ mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, cons pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse); if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) || authrr->WakeUp.HMAC.l[0]) if (pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse); + if ((authrr->resrec.InterfaceID == mDNSInterface_Any) && + !mDNSPlatformValidRecordForInterface(authrr, pktrr->resrec.InterfaceID)) return(mDNSfalse); return (mDNSBool)( pktrr->resrec.rrclass == authrr->resrec.rrclass && pktrr->resrec.namehash == authrr->resrec.namehash && @@ -843,11 +925,12 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0) { // To allow us to aggregate probes when a group of services are registered together, - // the first probe is delayed 1/4 second. This means the common-case behaviour is: - // 1/4 second wait; probe + // the first probe is delayed by a random delay in the range 1/8 to 1/4 second. + // This means the common-case behaviour is: + // randomized wait; probe // 1/4 second wait; probe // 1/4 second wait; probe - // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered) + // 1/4 second wait; announce (i.e. service is normally announced 7/8 to 1 second after being registered) m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2)); // If we already have a *probe* scheduled to go out sooner, then use that time to get better aggregation @@ -879,7 +962,9 @@ mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr) } rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval; } - else if (m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) + // Skip kDNSRecordTypeKnownUnique records here and set their LastAPTime in the "else" block below so that they get announced immediately, + // otherwise, their announcement would be delayed until all other record probes complete. + else if ((rr->resrec.RecordType != kDNSRecordTypeKnownUnique) && m->SuppressProbes && m->SuppressProbes - m->timenow >= 0) rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2; else rr->LastAPTime = m->timenow - rr->ThisAPInterval; @@ -890,7 +975,7 @@ 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) + if (rr->AddressProxy.type) rr->LastAPTime = m->timenow; // Set LastMCTime to now, to inhibit multicast responses @@ -1068,26 +1153,25 @@ mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr) mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr) { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; + const AuthGroup *a; + AuthRecord *rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) return mDNSNULL; - rp = &(*ag)->members; - while (*rp) + rp = a->members; + while (rp) { - if (!RecordIsLocalDuplicate(*rp, rr)) - rp=&(*rp)->next; + if (!RecordIsLocalDuplicate(rp, rr)) + rp = rp->next; else { - if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering) + if (rp->resrec.RecordType == kDNSRecordTypeDeregistering) { - (*rp)->AnnounceCount = 0; - rp=&(*rp)->next; + rp->AnnounceCount = 0; + rp = rp->next; } - else return *rp; + else return rp; } } return (mDNSNULL); @@ -1095,22 +1179,21 @@ mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr) mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr) { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; + const AuthGroup *a; + const AuthRecord *rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) return mDNSfalse; - rp = &(*ag)->members; - while (*rp) + rp = a->members; + while (rp) { const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr; - const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp; - if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec)) + const AuthRecord *s2 = rp->RRSet ? rp->RRSet : rp; + if (s1 != s2 && SameResourceRecordSignature(rp, rr) && !IdenticalSameNameRecord(&rp->resrec, &rr->resrec)) return mDNStrue; else - rp=&(*rp)->next; + rp = rp->next; } return (mDNSfalse); } @@ -1118,21 +1201,20 @@ mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr) // checks to see if "rr" is already present mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr) { - AuthGroup *a; - AuthGroup **ag = &a; - AuthRecord **rp; + const AuthGroup *a; + AuthRecord *rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(r, slot, &rr->resrec); if (!a) return mDNSNULL; - rp = &(*ag)->members; - while (*rp) + rp = a->members; + while (rp) { - if (*rp != rr) - rp=&(*rp)->next; + if (rp != rr) + rp = rp->next; else { - return *rp; + return rp; } } return (mDNSNULL); @@ -1158,20 +1240,22 @@ mDNSlocal void DecrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) LogInfo("DecrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); } -#if TARGET_OS_WATCH +#if BONJOUR_ON_DEMAND if (!AuthRecord_uDNS(rr)) { if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) - m->NetworkChanged = m->timenow; + m->NextBonjourDisableTime = NonZeroTime(m->timenow + (BONJOUR_DISABLE_DELAY * mDNSPlatformOneSecond)); m->NumAllInterfaceRecords--; LogInfo("DecrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); } -#endif +#endif // BONJOUR_ON_DEMAND } mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) { + mDNSBool enablingBonjour = 0; + if (RRLocalOnly(rr)) { // A sanity check, this should be prevented in calling code. @@ -1179,34 +1263,51 @@ mDNSlocal void IncrementAutoTargetServices(mDNS *const m, AuthRecord *const rr) return; } -#if TARGET_OS_WATCH +#if BONJOUR_ON_DEMAND if (!AuthRecord_uDNS(rr)) { m->NumAllInterfaceRecords++; LogInfo("IncrementAutoTargetServices: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %s", m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, ARDisplayString(m, rr)); if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) - m->NetworkChanged = m->timenow; + { + m->NextBonjourDisableTime = 0; + if (m->BonjourEnabled == 0) + { + // Enable Bonjour immediately by scheduling network changed processing where + // we will join the multicast group on each active interface. + m->BonjourEnabled = 1; + enablingBonjour = 1; + m->NetworkChanged = m->timenow; + } + } } -#endif +#endif // BONJOUR_ON_DEMAND if (!AuthRecord_uDNS(rr) && rr->resrec.rrtype == kDNSType_SRV && rr->AutoTarget == Target_AutoHost) { m->AutoTargetServices++; LogInfo("IncrementAutoTargetServices: AutoTargetServices %d Record %s", m->AutoTargetServices, ARDisplayString(m, rr)); - // If this is the first advertised service - if (m->AutoTargetServices == 1) + + // If this is the first advertised service and we did not just enable Bonjour above, then + // advertise all the interface records. If we did enable Bonjour above, the interface records will + // be advertised during the network changed processing scheduled above, so no need + // to do it here. + if ((m->AutoTargetServices == 1) && (enablingBonjour == 0)) 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; + mDNSAddr laddr = zeroAddr; + mDNSEthAddr eth = zeroEthAddr; + mDNSIPPort lport = zeroIPPort; + mDNSIPPort rport = zeroIPPort; + mDNSu32 timeout = 0; + mDNSu32 seq = 0; + mDNSu32 ack = 0; + mDNSu16 win = 0; if (mDNS_KeepaliveRecord(&rr->resrec)) { @@ -1305,15 +1406,15 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // Set up by client prior to call // Field Group 2: Persistent metadata for Authoritative Records -// rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client -// rr->Callback = already set in mDNS_SetupResourceRecord -// rr->Context = already set in mDNS_SetupResourceRecord -// rr->RecordType = already set in mDNS_SetupResourceRecord -// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client -// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client +// rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client +// rr->Callback = already set in mDNS_SetupResourceRecord +// rr->Context = already set in mDNS_SetupResourceRecord +// rr->RecordType = already set in mDNS_SetupResourceRecord +// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client +// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client // Make sure target is not uninitialized data, or we may crash writing debugging log messages if (rr->AutoTarget && target) target->c[0] = 0; @@ -1336,9 +1437,9 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; if (!rr->AutoTarget) InitializeLastAPTime(m, rr); -// rr->LastAPTime = Set for us in InitializeLastAPTime() -// rr->LastMCTime = Set for us in InitializeLastAPTime() -// rr->LastMCInterface = Set for us in InitializeLastAPTime() +// rr->LastAPTime = Set for us in InitializeLastAPTime() +// rr->LastMCTime = Set for us in InitializeLastAPTime() +// rr->LastMCInterface = Set for us in InitializeLastAPTime() rr->NewRData = mDNSNULL; rr->newrdlength = 0; rr->UpdateCallback = mDNSNULL; @@ -1371,12 +1472,12 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) // times with different values if the external NAT port changes during the lifetime of the service registration. //if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port; -// rr->resrec.interface = already set in mDNS_SetupResourceRecord -// rr->resrec.name->c = MUST be set by client -// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord -// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord -// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord -// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set +// rr->resrec.interface = already set in mDNS_SetupResourceRecord +// rr->resrec.name->c = MUST be set by client +// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord +// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord +// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord +// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct, // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s". @@ -1471,7 +1572,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) if (r) { - debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr)); + LogInfo("mDNS_Register_internal: Adding to duplicate list %s", ARDisplayString(m,rr)); *d = rr; // If the previous copy of this record is already verified unique, // then indicate that we should move this record promptly to kDNSRecordTypeUnique state. @@ -1482,7 +1583,7 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } else { - debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); + LogInfo("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr)); if (RRLocalOnly(rr)) { AuthGroup *ag; @@ -1504,6 +1605,19 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) } } + 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); + + // Adding a record may affect whether or not we should sleep + mDNS_UpdateAllowSleep(m); + } + // If this is a non-sleep proxy keepalive record, fetch the MAC address of the remote host. // This is used by the in-NIC proxy to send the keepalive packets. if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec)) @@ -1519,19 +1633,6 @@ mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr) 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); - - // Adding a record may affect whether or not we should sleep - mDNS_UpdateAllowSleep(m); - } - return(mStatus_NoError); } @@ -1577,13 +1678,12 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (RRLocalOnly(rr)) { AuthGroup *a; - AuthGroup **ag = &a; AuthRecord **rp; const mDNSu32 slot = AuthHashSlot(rr->resrec.name); a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec); if (!a) return mDNSfalse; - rp = &(*ag)->members; + rp = &a->members; while (*rp && *rp != rr) rp=&(*rp)->next; p = rp; } @@ -1793,7 +1893,7 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, if (drt != mDNS_Dereg_conflict) { mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback - LogInfo("mDNS_Deregister_internal: mStatus_MemFree for %s", ARDisplayString(m, rr)); + LogInfo("mDNS_Deregister_internal: callback with mStatus_MemFree for %s", ARDisplayString(m, rr)); if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again @@ -1817,6 +1917,10 @@ mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, } else { +#if APPLE_OSX_mDNSResponder + // See if this record was also registered with any D2D plugins. + D2D_stop_advertising_record(r2); +#endif mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); // As this is a duplicate record, it will be unlinked from the list // immediately @@ -1927,9 +2031,8 @@ mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const d rr->v6Requester = zerov6Addr; // Only sent records registered for P2P over P2P interfaces - if (intf && !mDNSPlatformValidRecordForInterface(rr, intf)) + if (intf && !mDNSPlatformValidRecordForInterface(rr, intf->InterfaceID)) { - LogInfo("SendDelayedUnicastResponse: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, InterfaceID)); continue; } @@ -2343,6 +2446,22 @@ mDNSlocal mDNSBool ShouldSendGoodbyesBeforeSleep(mDNS *const m, const NetworkInt } } +mDNSlocal mDNSBool IsInterfaceValidForAuthRecord(const AuthRecord *ar, mDNSInterfaceID InterfaceID) +{ + mDNSBool result; + + if (ar->resrec.InterfaceID == mDNSInterface_Any) + { + result = mDNSPlatformValidRecordForInterface(ar, InterfaceID); + } + else + { + result = (ar->resrec.InterfaceID == InterfaceID); + } + + return(result); +} + // 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; @@ -2404,8 +2523,10 @@ mDNSlocal void SendResponses(mDNS *const m) } else { + mDNSBool unicastOnly; 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); + unicastOnly = ((rr->AnnounceCount == WakeupCount) || (rr->AnnounceCount == WakeupCount - 1)) ? mDNStrue : mDNSfalse; + SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password, unicastOnly); for (r2 = rr; r2; r2=r2->next) if ((r2->resrec.RecordType == kDNSRecordTypeDeregistering) && r2->AnnounceCount && (r2->resrec.InterfaceID == rr->resrec.InterfaceID) && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC) && !mDNSSameEthAddress(&zeroEthAddr, &r2->WakeUp.HMAC)) @@ -2506,17 +2627,28 @@ mDNSlocal void SendResponses(mDNS *const m) if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked { for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (ResourceRecordIsValidAnswer(r2)) - if (r2->ImmedAnswer != mDNSInterfaceMark && - r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(r2, rr)) - r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark; + { + if ((r2->resrec.RecordType & kDNSRecordTypeUniqueMask) && ResourceRecordIsValidAnswer(r2) && + (r2->ImmedAnswer != mDNSInterfaceMark) && (r2->ImmedAnswer != rr->ImmedAnswer) && + SameResourceRecordSignature(r2, rr) && + ((rr->ImmedAnswer == mDNSInterfaceMark) || IsInterfaceValidForAuthRecord(r2, rr->ImmedAnswer))) + { + r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark; + } + } } else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked { for (r2 = m->ResourceRecords; r2; r2=r2->next) - if (ResourceRecordIsValidAnswer(r2)) - if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(r2, rr)) - r2->ImmedAdditional = rr->ImmedAdditional; + { + if ((r2->resrec.RecordType & kDNSRecordTypeUniqueMask) && ResourceRecordIsValidAnswer(r2) && + (r2->ImmedAdditional != rr->ImmedAdditional) && + SameResourceRecordSignature(r2, rr) && + IsInterfaceValidForAuthRecord(r2, rr->ImmedAdditional)) + { + r2->ImmedAdditional = rr->ImmedAdditional; + } + } } } @@ -2529,6 +2661,7 @@ mDNSlocal void SendResponses(mDNS *const m) rr->ImmedAdditional = mDNSNULL; // No need to send as additional if sending as answer rr->LastMCTime = m->timenow; rr->LastMCInterface = rr->ImmedAnswer; + rr->ProbeRestartCount = 0; // Reset the probe restart count // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2)) { @@ -2576,9 +2709,8 @@ mDNSlocal void SendResponses(mDNS *const m) // Skip this interface if the record InterfaceID is *Any and the record is not // appropriate for the interface type. if ((rr->SendRNow == intf->InterfaceID) && - ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf))) + ((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf->InterfaceID))) { - // LogInfo("SendResponses: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow)); rr->SendRNow = GetNextActiveInterfaceID(intf); } else if (rr->SendRNow == intf->InterfaceID) @@ -2651,7 +2783,7 @@ mDNSlocal void SendResponses(mDNS *const m) // Get the reserved space back OwnerRecordSpace -= AnoninfoSpace; - TraceRecordSpace -= AnoninfoSpace; + TraceRecordSpace -= AnoninfoSpace; newptr = responseptr; for (rr = m->ResourceRecords; rr; rr=rr->next) { @@ -2800,10 +2932,9 @@ mDNSlocal void SendResponses(mDNS *const m) SetupTracerOpt(m, &opt.resrec.rdata->u.opt[0]); } newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec); - if (newptr) - { - responseptr = newptr; - LogInfo("SendResponses put %s %s: %s %s", OwnerRecordSpace ? "OWNER" : "", TraceRecordSpace ? "TRACER" : "", intf->ifname, ARDisplayString(m, &opt)); + if (newptr) + { + responseptr = newptr; } else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1) { @@ -2816,7 +2947,7 @@ mDNSlocal void SendResponses(mDNS *const m) 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", @@ -3097,11 +3228,11 @@ mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *c } return(bestcr); #else // SPC_DISABLED - (void) m; - (void) q; - (void) c0; - (void) c1; - (void) c1; + (void) m; + (void) q; + (void) c0; + (void) c1; + (void) c1; return mDNSNULL; #endif // SPC_DISABLED } @@ -3199,7 +3330,7 @@ mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q) domainname *d = &q->qname; // We can't send magic packets without knowing which interface to send it on. - if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P) + if (InterfaceID == mDNSInterface_Any || LocalOnlyOrP2PInterface(InterfaceID)) { LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c); return; @@ -3415,7 +3546,7 @@ mDNSlocal void SendQueries(mDNS *const m) // 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 + if (q->BrowseThreshold && (q->CurrentAnswers >= q->BrowseThreshold) && (q->CachedAnswerNeedsUpdate == mDNSfalse) && !((q->flags & kDNSServiceFlagsThresholdFinder) && (q->ThisQInterval == InitialQuestionInterval))) @@ -3437,7 +3568,7 @@ mDNSlocal void SendQueries(mDNS *const m) debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast); - if (q->ThisQInterval >= QuestionIntervalThreshold) + if (q->ThisQInterval > MaxQuestionInterval) { q->ThisQInterval = MaxQuestionInterval; } @@ -3500,11 +3631,11 @@ mDNSlocal void SendQueries(mDNS *const m) { if (ar->AddressProxy.type == mDNSAddrType_IPv4) { - // There's a problem here. If a host is waking up, and we probe to see if it responds, then - // it will see those ARP probes as signalling intent to use the address, so it picks a different one. - // A more benign way to find out if a host is responding to ARPs might be send a standard ARP *request* - // (using our sender IP address) instead of an ARP *probe* (using all-zero sender IP address). - // A similar concern may apply to the NDP Probe too. -- SC + // There's a problem here. If a host is waking up, and we probe to see if it responds, then + // it will see those ARP probes as signalling intent to use the address, so it picks a different one. + // A more benign way to find out if a host is responding to ARPs might be send a standard ARP *request* + // (using our sender IP address) instead of an ARP *probe* (using all-zero sender IP address). + // A similar concern may apply to the NDP Probe too. -- SC LogSPS("SendQueries ARP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar)); SendARP(m, 1, ar, &zerov4Addr, &zeroEthAddr, &ar->AddressProxy.ip.v4, &ar->WakeUp.IMAC); } @@ -3596,7 +3727,6 @@ mDNSlocal void SendQueries(mDNS *const m) // If interface is P2P type, verify that query should be sent over it. if (!mDNSPlatformValidQuestionForInterface(q, intf)) { - LogInfo("SendQueries: Not sending (%s) %##s on %s", DNSTypeName(q->qtype), q->qname.c, InterfaceNameForID(m, intf->InterfaceID)); q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf); } // If we're suppressing this question, or we successfully put it, update its SendQNow state @@ -3636,24 +3766,62 @@ mDNSlocal void SendQueries(mDNS *const m) // Put probe questions in this packet for (ar = m->ResourceRecords; ar; ar=ar->next) - if (ar->SendRNow == intf->InterfaceID) + { + if (ar->SendRNow != intf->InterfaceID) + continue; + + // If interface is a P2P variant, verify that the probe should be sent over it. + if (!mDNSPlatformValidRecordForInterface(ar, intf->InterfaceID)) + { + ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + ar->IncludeInProbe = mDNSfalse; + } + else { mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353 && intf->SupportsUnicastMDNSResponse; mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0); const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData); // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n) mDNSu32 forecast = answerforecast + 12 + ar->resrec.rdestimate; - mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, (mDNSu16)(ar->resrec.rrclass | ucbit)); - if (newptr) + mDNSBool putProbe = mDNStrue; + mDNSu16 qclass = ar->resrec.rrclass | ucbit; + + {// Determine if this probe question is already in packet's dns message + const mDNSu8 *questionptr = m->omsg.data; + DNSQuestion question; + mDNSu16 n; + for (n = 0; n < m->omsg.h.numQuestions && questionptr; n++) + { + questionptr = getQuestion(&m->omsg, questionptr, limit, mDNSInterface_Any, &question); + if (questionptr && (question.qtype == kDNSQType_ANY) && (question.qclass == qclass) && + (question.qnamehash == ar->resrec.namehash) && SameDomainName(&question.qname, ar->resrec.name)) + { + putProbe = mDNSfalse; // set to false if already in message + break; + } + } + } + + if (putProbe) + { + mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, qclass); + if (newptr) + { + queryptr = newptr; + answerforecast = forecast; + ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); + ar->IncludeInProbe = mDNStrue; + verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d InterfaceID= %d %d %d", + ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount, ar->resrec.InterfaceID, ar->resrec.rdestimate, answerforecast); + } + } + else { - queryptr = newptr; - answerforecast = forecast; ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf); ar->IncludeInProbe = mDNStrue; - verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", - ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount); } } + } } // Put our known answer list (either new one from this question or questions, or remainder of old one from last time) @@ -3719,7 +3887,7 @@ mDNSlocal void SendQueries(mDNS *const m) AuthRecord opt; mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); opt.resrec.rrclass = NormalMaxDNSMessageData; - opt.resrec.rdlength = sizeof(rdataOPT); + opt.resrec.rdlength = sizeof(rdataOPT); opt.resrec.rdestimate = sizeof(rdataOPT); if (OwnerRecordSpace && TraceRecordSpace) { @@ -3736,19 +3904,18 @@ mDNSlocal void SendQueries(mDNS *const m) { 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 %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 %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, + 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)); } } @@ -3817,17 +3984,21 @@ mDNSlocal void SendQueries(mDNS *const m) { DNSQuestion *x; for (x = m->NewQuestions; x; x=x->next) if (x == q) break; // Check if this question is a NewQuestion - LogInfo("SendQueries: No active interface %d to send %s question: %d %##s (%s)", - (uint32_t)q->SendQNow, x ? "new" : "old", (uint32_t)q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + // There will not be an active interface for questions applied to mDNSInterface_BLE + // so don't log the warning in that case. + if (q->InterfaceID != mDNSInterface_BLE) + LogInfo("SendQueries: No active interface %d to send %s question: %d %##s (%s)", + (uint32_t)q->SendQNow, x ? "new" : "old", (uint32_t)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) +mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password, mDNSBool unicastOnly) { int i, j; + mDNSu8 *ptr = m->omsg.data; NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found", InterfaceID); return; } @@ -3853,13 +4024,16 @@ mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAdd mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); - // For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses, - // broadcast is the only reliable way to get a wakeup packet to the intended target machine. - // For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast - // key rotation, unicast is the only way to get a wakeup packet to the intended target machine. - // So, we send one of each, unicast first, then broadcast second. - for (i=0; i<6; i++) m->omsg.data[i] = 0xFF; - mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); + if (!unicastOnly) + { + // For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses, + // broadcast is the only reliable way to get a wakeup packet to the intended target machine. + // For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast + // key rotation, unicast is the only way to get a wakeup packet to the intended target machine. + // So, we send one of each, unicast first, then broadcast second. + for (i=0; i<6; i++) m->omsg.data[i] = 0xFF; + mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID); + } } // *************************************************************************** @@ -3891,7 +4065,7 @@ mDNSlocal void ResetQuestionState(mDNS *const m, DNSQuestion *q) mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord) { DNSQuestion *const q = m->CurrentQuestion; - mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord); + 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)); @@ -3937,28 +4111,32 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco } #if TARGET_OS_EMBEDDED - if ((AddRecord == QC_add) && Question_uDNS(q) && (!q->metrics.answered || (q->metrics.querySendCount > 0))) + if ((AddRecord == QC_add) && Question_uDNS(q) && !followcname) { - uDNSMetrics * metrics; const domainname * queryName; mDNSu32 responseLatencyMs; mDNSBool isForCellular; - metrics = &q->metrics; - queryName = metrics->originalQName ? metrics->originalQName : &q->qname; - if (metrics->querySendCount > 0) + queryName = q->metrics.originalQName ? q->metrics.originalQName : &q->qname; + isForCellular = (q->qDNSServer && q->qDNSServer->cellIntf); + if (!q->metrics.answered) { - responseLatencyMs = ((m->timenow - metrics->firstQueryTime) * 1000) / mDNSPlatformOneSecond; + if (q->metrics.querySendCount > 0) + { + responseLatencyMs = ((m->timenow - q->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; + } + else + { + responseLatencyMs = 0; + } + + MetricsUpdateUDNSQueryStats(queryName, q->qtype, &rr->resrec, q->metrics.querySendCount, responseLatencyMs, isForCellular); + q->metrics.answered = mDNStrue; } - else + if (q->metrics.querySendCount > 0) { - responseLatencyMs = 0; + MetricsUpdateUDNSResolveStats(queryName, &rr->resrec, isForCellular); } - isForCellular = (q->qDNSServer && q->qDNSServer->cellIntf); - - MetricsUpdateUDNSStats(queryName, mDNStrue, metrics->querySendCount, responseLatencyMs, isForCellular); - metrics->answered = mDNStrue; - metrics->querySendCount = 0; } #endif // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue) @@ -3991,6 +4169,30 @@ mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheReco if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us +#ifdef USE_LIBIDN + if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) // If negative answer, check if we need to try Punycode conversion + { + domainname newname; + if (PerformNextPunycodeConversion(q, &newname)) // Itertative Punycode conversion succeeded, so reissue question with new name + { + UDPSocket *const sock = q->LocalSocket; // Save old socket and transaction ID + const mDNSOpaque16 id = q->TargetQID; + q->LocalSocket = mDNSNULL; + mDNS_StopQuery_internal(m, q); // Stop old query + AssignDomainName(&q->qname, &newname); // Update qname + q->qnamehash = DomainNameHashValue(&q->qname); // and namehash + mDNS_StartQuery_internal(m, q); // Start new query + + if (sock) // Transplant saved socket, if appropriate + { + if (q->DuplicateOf) mDNSPlatformUDPClose(sock); + else { q->LocalSocket = sock; q->TargetQID = id; } + } + return; // All done for now; wait until we get the next answer + } + } +#endif // USE_LIBIDN + // 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/timeout OR the question is suppressed if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype))) @@ -4041,7 +4243,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 (!ValidatingQuestion(q) && followcname && m->CurrentQuestion == q) + if ((m->CurrentQuestion == q) && followcname && !ValidatingQuestion(q)) AnswerQuestionByFollowingCNAME(m, q, &rr->resrec); } @@ -4082,7 +4284,7 @@ mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *c // 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 @@ -4317,7 +4519,7 @@ mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp) if ((*cp)->rrcache_tail != &(*cp)->members) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)"); //if ((*cp)->name != (domainname*)((*cp)->namestorage)) - // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); + // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage)); if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name); (*cp)->name = mDNSNULL; *cp = (*cp)->next; // Cut record from list @@ -4364,7 +4566,7 @@ mDNSexport void ReleaseCacheRecord(mDNS *const m, CacheRecord *r) r->resrec.rdata = mDNSNULL; cg = CacheGroupForRecord(m, slot, &r->resrec); - + if (!cg) { // It is okay to have this printed for NSEC/NSEC3s @@ -4417,7 +4619,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 @@ -4509,7 +4711,7 @@ mDNSlocal mDNSBool AnswerQuestionWithLORecord(mDNS *const m, DNSQuestion *q, mDN // 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 (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && (q->InterfaceID == mDNSInterface_Any || q->InterfaceID == mDNSInterface_BLE))) if (LocalOnlyRecordAnswersQuestion(rr, q)) { if (checkOnly) @@ -4577,7 +4779,7 @@ mDNSlocal void AnswerSuppressedQuestion(mDNS *const m, DNSQuestion *q) q->SuppressQuery = mDNSfalse; q->DisallowPID = mDNSfalse; - GenerateNegativeResponse(m, QC_suppressed); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_suppressed); q->SuppressQuery = SuppressQuery; q->DisallowPID = DisallowPID; @@ -4682,7 +4884,7 @@ mDNSlocal void AnswerNewQuestion(mDNS *const m) AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } - else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) + else if (mDNSOpaque16IsZero(q->TargetQID) && RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype)) ShouldQueryImmediately = mDNSfalse; } // We don't use LogInfo for this "Question deleted" message because it happens so routinely that @@ -4696,7 +4898,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, QC_forceresponse); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); } if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; } @@ -4737,6 +4939,7 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) AuthGroup *ag; DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer m->NewLocalOnlyQuestions = q->next; // Advance NewLocalOnlyQuestions to the next (if any) + mDNSBool retEv = mDNSfalse; debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -4762,6 +4965,7 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) m->CurrentRecord = rr->next; if (LocalOnlyRecordAnswersQuestion(rr, q)) { + retEv = mDNStrue; AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } @@ -4778,12 +4982,18 @@ mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m) m->CurrentRecord = rr->next; if (ResourceRecordAnswersQuestion(&rr->resrec, q)) { + retEv = mDNStrue; AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue); if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here } } } + // The local host is the authoritative source for LocalOnly questions + // so if no records exist and client requested intermediates, then generate a negative response + if (!retEv && (m->CurrentQuestion == q) && q->ReturnIntermed) + GenerateNegativeResponse(m, mDNSInterface_LocalOnly, QC_forceresponse); + m->CurrentQuestion = mDNSNULL; m->CurrentRecord = mDNSNULL; } @@ -4898,7 +5108,7 @@ mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const Res cg->namehash = rr->namehash; cg->members = mDNSNULL; cg->rrcache_tail = &cg->members; - if (namelen > sizeof(cg->namestorage)) + if (namelen > sizeof(cg->namestorage)) cg->name = mDNSPlatformMemAllocate(namelen); else cg->name = (domainname*)cg->namestorage; @@ -5029,7 +5239,7 @@ mDNSlocal void TimeoutQuestions(mDNS *const m) if (m->timenow - q->StopTime >= 0) { LogInfo("TimeoutQuestions: question %p %##s timed out, time %d", q, q->qname.c, m->timenow - q->StopTime); - GenerateNegativeResponse(m, QC_forceresponse); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); if (m->CurrentQuestion == q) q->StopTime = 0; } else @@ -5135,10 +5345,26 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) mDNS_SendKeepalives(m); } +#if BONJOUR_ON_DEMAND + if (m->NextBonjourDisableTime && (m->timenow - m->NextBonjourDisableTime >= 0)) + { + // Schedule immediate network change processing to leave the multicast group + // since the delay time has expired since the previous active registration or query. + m->NetworkChanged = m->timenow; + m->NextBonjourDisableTime = 0; + m->BonjourEnabled = 0; + + LogInfo("mDNS_Execute: Scheduled network changed processing to leave multicast group."); + } +#endif // BONJOUR_ON_DEMAND + // Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().) if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) { m->AnnounceOwner = 0; + + // This is a good time to reset the delay counter used to prevent spurious conflicts + m->DelayConflictProcessing = 0; } if (m->DelaySleep && m->timenow - m->DelaySleep >= 0) @@ -5305,6 +5531,10 @@ mDNSexport mDNSs32 mDNS_Execute(mDNS *const m) if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m); if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m); #endif +#if APPLE_OSX_mDNSResponder + extern void serviceBLE(); + if (m->NextBLEServiceTime && (m->timenow - m->NextBLEServiceTime >= 0)) serviceBLE(); +#endif // APPLE_OSX_mDNSResponder } // Note about multi-threaded systems: @@ -5550,11 +5780,7 @@ mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q) if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) { q->ThisQInterval = InitialQuestionInterval; // MUST be > zero for an active question -#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->RequestUnicast = kDefaultRequestUnicastCount; q->LastQTime = m->timenow - q->ThisQInterval; q->RecentAnswerPkts = 0; ExpireDupSuppressInfo(q->DupSuppress, m->timenow); @@ -5563,23 +5789,26 @@ mDNSexport void mDNSCoreRestartQuestion(mDNS *const m, DNSQuestion *q) } // restart the probe/announce cycle for multicast record -mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount) +mDNSexport void mDNSCoreRestartRegistration(mDNS *const m, AuthRecord *rr, int announceCount) { if (!AuthRecord_uDNS(rr)) { if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique; rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType); - // announceCount < 0 indicates default announce count should be used - if (announceCount < 0) - announceCount = InitialAnnounceCount; - if (rr->AnnounceCount < announceCount) - rr->AnnounceCount = announceCount; - if (mDNS_KeepaliveRecord(&rr->resrec)) - rr->AnnounceCount = 0; // Do not announce keepalive records + { + rr->AnnounceCount = 0; // Do not announce keepalive records + } else - rr->AnnounceCount = InitialAnnounceCount; + { + // announceCount < 0 indicates default announce count should be used + if (announceCount < 0) + announceCount = InitialAnnounceCount; + if (rr->AnnounceCount < (mDNSu8)announceCount) + rr->AnnounceCount = (mDNSu8)announceCount; + } + rr->SendNSECNow = mDNSNULL; InitializeLastAPTime(m, rr); } @@ -5672,7 +5901,7 @@ 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) || (rr->AuthFlags & AuthFlagsWakeOnly) || mDNSOpaque16IsZero(rr->updateid) || + if (AuthRecord_uDNS(rr) || (rr->AuthFlags & AuthFlagsWakeOnly) || mDNSOpaque16IsZero(rr->updateid) || m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0) { return mDNSfalse; @@ -5693,53 +5922,56 @@ mDNSlocal mDNSBool mDNSUpdateOkToSend(mDNS *const m, AuthRecord *rr, NetworkInte return mDNSfalse; } -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 non-sleep proxy keepalive record and the remote IP address matches, update the RData - if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec)) - { - mDNSAddr raddr; - getKeepaliveRaddr(m, rr, &raddr); - if (mDNSSameAddress(&raddr, &addrmap->ipaddr)) - { - // Update the MAC address only if it is not a zero MAC address - mDNSEthAddr macAddr; - mDNSu8 *ptr = GetValueForMACAddr((mDNSu8 *)(addrmap->ethaddr), (mDNSu8 *) (addrmap->ethaddr + sizeof(addrmap->ethaddr)), &macAddr); - if (ptr != mDNSNULL && !mDNSEthAddressIsZero(macAddr)) - { - UpdateKeepaliveRData(m, rr, mDNSNULL, mDNStrue, (char *)(addrmap->ethaddr)); - } - } - } - m->CurrentRecord = rr->next; - } - - if (addrmap) - { - mDNSPlatformMemFree(addrmap); - } +mDNSexport void UpdateRMAC(mDNS *const m, void *context) +{ + IPAddressMACMapping *addrmap = (IPAddressMACMapping *)context ; + m->CurrentRecord = m->ResourceRecords; + + if (!addrmap) + { + LogMsg("UpdateRMAC: Address mapping is NULL"); + return; + } + + while (m->CurrentRecord) + { + AuthRecord *rr = m->CurrentRecord; + // If this is a non-sleep proxy keepalive record and the remote IP address matches, update the RData + if (!rr->WakeUp.HMAC.l[0] && mDNS_KeepaliveRecord(&rr->resrec)) + { + mDNSAddr raddr; + getKeepaliveRaddr(m, rr, &raddr); + if (mDNSSameAddress(&raddr, &addrmap->ipaddr)) + { + // Update the MAC address only if it is not a zero MAC address + mDNSEthAddr macAddr; + mDNSu8 *ptr = GetValueForMACAddr((mDNSu8 *)(addrmap->ethaddr), (mDNSu8 *) (addrmap->ethaddr + sizeof(addrmap->ethaddr)), &macAddr); + if (ptr != mDNSNULL && !mDNSEthAddressIsZero(macAddr)) + { + 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; + mDNSAddr laddr = zeroAddr; + mDNSAddr raddr = zeroAddr; + mDNSEthAddr eth = zeroEthAddr; + mDNSIPPort lport = zeroIPPort; + mDNSIPPort rport = zeroIPPort; + mDNSu32 timeout = 0; + mDNSu32 seq = 0; + mDNSu32 ack = 0; + mDNSu16 win = 0; UTF8str255 txt; int rdsize; RData *newrd; @@ -5749,8 +5981,7 @@ mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkIn // 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); - if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) || - mDNSIPPortIsZero(rport)) + if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport)) { LogMsg("UpdateKeepaliveRData: not a valid record %s for keepalive %#a:%d %#a:%d", ARDisplayString(m, rr), &laddr, lport.NotAnInteger, &raddr, rport.NotAnInteger); return mStatus_UnknownErr; @@ -5813,8 +6044,8 @@ mDNSexport mStatus UpdateKeepaliveRData(mDNS *const m, AuthRecord *rr, NetworkIn // free that memory here before copying in the new data. if ( rr->resrec.rdata != &rr->rdatastorage) { - mDNSPlatformMemFree(rr->resrec.rdata); LogSPS("UpdateKeepaliveRData: Freed allocated memory for keep alive packet: %s ", ARDisplayString(m, rr)); + mDNSPlatformMemFree(rr->resrec.rdata); } SetNewRData(&rr->resrec, newrd, newrdlength); // Update our rdata @@ -5954,7 +6185,7 @@ mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo * LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps, mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps])); - // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss + // if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID; // For simulating packet loss err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSNULL, mDNSfalse); if (err) LogSPS("SendSPSRegistration: mDNSSendDNSMessage err %d", err); if (err && intf->SPSAddr[sps].type == mDNSAddrType_IPv4 && intf->NetWakeResolve[sps].ThisQInterval == -1) @@ -6027,7 +6258,7 @@ mDNSlocal void SPSInitRecordsBeforeUpdate(mDNS *const m, mDNSOpaque64 updateIntI { 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, @@ -6144,7 +6375,7 @@ mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const Resour if (!AddRecord) return; // Don't care about REMOVE events if (answer->rrtype != question->qtype) return; // Don't care about CNAMEs - // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address + // if (answer->rrtype == kDNSType_AAAA && sps == 0) return; // To test failing to resolve sleep proxy's address if (answer->rrtype == kDNSType_SRV) { @@ -6230,14 +6461,8 @@ mDNSlocal void SendGoodbyesForWakeOnlyService(mDNS *const m, mDNSBool *WakeOnlyS { return SendGoodbyesForSelectServices(m, WakeOnlyService, WAKE_ONLY_SERVICE); } -#endif // APPLE_OSx_mDNSResponder +#endif // APPLE_OSX_mDNSResponder -#ifdef APPLE_OSX_mDNSResponder -mDNSlocal void SendGoodbyesForACOnlyServices(mDNS *const m, mDNSBool *acOnlyService) -{ - return SendGoodbyesForSelectServices(m, acOnlyService, AC_ONLY_SERVICE); -} -#endif mDNSlocal void SendSleepGoodbyes(mDNS *const m, mDNSBool AllInterfaces, mDNSBool unicast) { @@ -6334,11 +6559,10 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) { mDNSBool SendGoodbyes = mDNStrue; mDNSBool WakeOnlyService = mDNSfalse; - mDNSBool ACOnlyService = mDNSfalse; mDNSBool invokeKACallback = mDNStrue; const CacheRecord *sps[3] = { mDNSNULL }; mDNSOpaque64 updateIntID = zeroOpaque64; - mDNSInterfaceID registeredIntfIDS[128]; + mDNSInterfaceID registeredIntfIDS[128] = { 0 }; mDNSu32 registeredCount = 0; int skippedRegistrations = 0; @@ -6351,7 +6575,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) NetworkInterfaceInfo *intf; // Clear out the SCDynamic entry that stores the external SPS information - mDNSPlatformClearSPSMACAddr(); + mDNSPlatformClearSPSData(); for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) { @@ -6395,10 +6619,10 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) if (ActivateLocalProxy(m, intf, &keepaliveOnly) == mStatus_NoError) { SendGoodbyesForWakeOnlyService(m, &WakeOnlyService); - if (keepaliveOnly) - SendGoodbyesForACOnlyServices(m, &ACOnlyService); - SendGoodbyes = mDNSfalse; - invokeKACallback = mDNSfalse; + + // Send goodbyes for all advertised services if the only record offloaded was the keepalive record. + SendGoodbyes = (keepaliveOnly) ? mDNStrue: 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, @@ -6482,7 +6706,7 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) // // - 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. @@ -6502,10 +6726,10 @@ mDNSlocal void BeginSleepProcessing(mDNS *const m) LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server on all interfaces"); SendSleepGoodbyes(m, mDNSfalse, mDNSfalse); } - else if (WakeOnlyService || ACOnlyService) + else if (WakeOnlyService) { // If we saw WakeOnly service above, send the goodbyes now. - LogSPS("BeginSleepProcessing: Sending goodbyes for %s", WakeOnlyService? "WakeOnlyService" : "AC Only Service"); + LogSPS("BeginSleepProcessing: Sending goodbyes for WakeOnlyService"); SendResponses(m); } } @@ -6531,7 +6755,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) #ifndef SPC_DISABLED if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords); #else - (void)oldstate; + (void)oldstate; #endif mDNS_ReclaimLockAfterCallback(); } @@ -6579,7 +6803,11 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) { m->SleepState = SleepState_Awake; m->SleepSeqNum++; - m->DelaySleep = 0; + // If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake) + // then we enforce a minimum delay of five seconds before we begin sleep processing. + // This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc., + // before we make our determination of whether there's a Sleep Proxy out there we should register with. + m->DelaySleep = NonZeroTime(m->timenow + kDarkWakeDelaySleep); } if (m->SPSState == 3) @@ -6588,7 +6816,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, m->SPSFeatureFlags); } m->mDNSStats.Wakes++; - + m->DelayConflictProcessing = MAX_CONFLICT_PROCESSING_DELAYS; // ... and the same for NextSPSAttempt for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1; @@ -6629,7 +6857,7 @@ mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep) 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 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 @@ -7113,7 +7341,7 @@ mDNSlocal void DeregisterProxyRecord(mDNS *const m, AuthRecord *const rr) 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)); + LogMsg("ClearKeepaliveProxyRecords 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 @@ -7247,9 +7475,9 @@ 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); @@ -7583,7 +7811,6 @@ mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, con { SendLegacyResponse = mDNStrue; } - if (SendMulticastResponse || SendUnicastResponse) { @@ -7690,15 +7917,17 @@ exit: // For non-truncated queries, we can definitively say that we should expect // to be seeing a response for any records still left in the ExpectedAnswers list if (!(query->h.flags.b[0] & kDNSFlag0_TC)) - if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond) + if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond * 3/4) { cr->UnansweredQueries++; cr->LastUnansweredTime = m->timenow; -#if ENABLE_MULTI_PACKET_QUERY_SNOOPING if (cr->UnansweredQueries > 1) - debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", + #if ENABLE_MULTI_PACKET_QUERY_SNOOPING + debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s", cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr)); -#endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + #else + debugf("ProcessQuery: UnansweredQueries %lu %s", cr->UnansweredQueries, CRDisplayString(m, cr)); + #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING SetNextCacheCheckTimeForRecord(m, cr); } @@ -7706,12 +7935,16 @@ exit: // then mark it to expire in five seconds if we don't get a response by then. if (cr->UnansweredQueries >= MaxUnansweredQueries) { -#if ENABLE_MULTI_PACKET_QUERY_SNOOPING // Only show debugging message if this record was not about to expire anyway - if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond) - debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s", + if (RRExpireTime(cr) - m->timenow > (mDNSs32) kDefaultReconfirmTimeForNoAnswer * 4 / 3 + mDNSPlatformOneSecond) + #if ENABLE_MULTI_PACKET_QUERY_SNOOPING + 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 + #else + LogInfo("ProcessQuery: UnansweredQueries %lu TTL %lu mDNS_Reconfirm() for %s", + cr->UnansweredQueries, (RRExpireTime(cr) - m->timenow + mDNSPlatformOneSecond-1) / mDNSPlatformOneSecond, CRDisplayString(m, cr)); + #endif // ENABLE_MULTI_PACKET_QUERY_SNOOPING + m->mDNSStats.PoofCacheDeletions++; mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer); } @@ -7777,15 +8010,16 @@ mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, mDNSBool QueryWasLocalUnicast = srcaddr && dstaddr && !mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr); - if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr)) + if (!dstaddr || (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr))) { + const char *const reason = !dstaddr ? "Received over TCP connection" : "Multicast, but no InterfaceID"; LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with " - "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes (Multicast, but no InterfaceID)", + "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes (%s)", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID, msg->h.numQuestions, msg->h.numQuestions == 1 ? ", " : "s,", msg->h.numAnswers, msg->h.numAnswers == 1 ? ", " : "s,", msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y, " : "ies,", - msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data); + msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " " : "s", end - msg->data, reason); return; } @@ -7875,9 +8109,9 @@ mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m, } if (mDNSSameIPPort(srcp, port)) return(q); - // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); - // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking - // if (TrustedSource(m, srcaddr)) return(mDNStrue); + // if (mDNSSameAddress(srcaddr, &q->Target)) return(mDNStrue); + // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking + // if (TrustedSource(m, srcaddr)) return(mDNStrue); LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s", q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr)); return(mDNSNULL); @@ -7915,7 +8149,7 @@ mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, C if (!m->rec.r.resrec.InterfaceID) debugf("CreateNewCacheEntry %s", CRDisplayString(m, &m->rec.r)); //if (RDLength > InlineCacheRDSize) - // LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r)); + // LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r)); if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec); // If we don't have a CacheGroup for this name, make one now if (cg) rr = GetCacheRecord(m, cg, RDLength); // Make a cache record, being careful not to recycle cg @@ -8254,7 +8488,7 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * // the application if (qptr->ProxyQuestion) qptr->responseFlags = response->h.flags; - GenerateNegativeResponse(m, QC_forceresponse); + GenerateNegativeResponse(m, mDNSInterface_Any, QC_forceresponse); m->CurrentQuestion = mDNSNULL; } else @@ -8438,7 +8672,7 @@ mDNSlocal void mDNSCoreReceiveNoUnicastAnswers(mDNS *const m, const DNSMessage * name = (const domainname *)(name->c + 1 + name->c[0]); hash = DomainNameHashValue(name); slot = HashSlot(name); - // For now, we don't need to update cg here, because we'll do it again immediately, back up at the start of this loop + // For now, we don't need to update cg here, because we'll do it again immediately, back up at the start of this loop //cg = CacheGroupForName(m, slot, hash, name); } } @@ -8614,7 +8848,6 @@ mDNSlocal CacheRecord* mDNSCoreReceiveCacheCheck(mDNS *const m, const DNSMessage } 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. @@ -8685,7 +8918,7 @@ 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); + FreeAnonInfo(m->rec.r.resrec.AnonInfo); m->rec.r.resrec.AnonInfo = mDNSNULL; } } @@ -8722,6 +8955,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, mDNSBool rrsigsCreated = mDNSfalse; mDNSBool DNSSECQuestion = mDNSfalse; NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID); + mDNSBool recordAcceptedInResponse = mDNSfalse; // Set if a record is accepted from a unicast mDNS response that answers an existing question. // 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 @@ -8832,14 +9066,14 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // 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", + 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; + 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)", + 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); } @@ -8874,7 +9108,9 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { // All responses sent via LL multicast are acceptable for caching // All responses received over our outbound TCP connections are acceptable for caching - mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType; + // We accept all records in a unicast response to a multicast query once we find one that + // answers an active question. + mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType || recordAcceptedInResponse; // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer // to any specific question -- any code reading records from the cache needs to make that determination for itself.) @@ -8961,38 +9197,42 @@ 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); - - // 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) - { - 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) - { - debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); - AcceptableResponse = mDNSfalse; - } - } + DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr); + + // 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) + { + debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype)); + m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer; + } + else + { + // Accept all remaining records in this unicast response to an mDNS query. + recordAcceptedInResponse = mDNStrue; + LogInfo("mDNSCoreReceiveResponse: Accepting response for query: %##s (%s)", 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) + { + debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c); + AcceptableResponse = mDNSfalse; + } + } } } else if (llintf && llintf->IgnoreIPv4LL && m->rec.r.resrec.rrtype == kDNSType_A) @@ -9016,7 +9256,7 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // This can cause some badly written applications to freeze for a long time if they // attempt to connect to an IPv4 link-local destination address and then wait for // that connection attempt to time out before trying other candidate addresses. - + // To mask this client bug, we suppress acceptance of IPv4 link-local address // records on interfaces where we know the OS will be unwilling even to attempt // communication with those IPv4 link-local destination addresses. @@ -9103,13 +9343,36 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, // If we're probing for this record, we just failed else if (rr->resrec.RecordType == kDNSRecordTypeUnique) { + // At this point in the code, we're probing for uniqueness. + // We've sent at least one probe (rr->ProbeCount < DefaultProbeCountForTypeUnique) + // but we haven't completed probing yet (rr->resrec.RecordType == kDNSRecordTypeUnique). // Before we call deregister, check if this is a packet we registered with the sleep proxy. 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); + // This may be a conflict due to stale packets on the network. Delay probing by a second. + // If there are conflicts after 3 such attempts, then it is a true conflict. + if (m->DelayConflictProcessing) + { + m->DelayConflictProcessing--; + LogMsg("Possible spurious conflict for %s. Attempt %d at suppressing probes for one second", + ARDisplayString(m, rr), (MAX_CONFLICT_PROCESSING_DELAYS - m->DelayConflictProcessing)); + rr->ProbeCount = DefaultProbeCountForTypeUnique + 1; + rr->AnnounceCount = InitialAnnounceCount; + m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond); + InitializeLastAPTime(m, rr); + RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate + } + else + { + LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr)); + m->mDNSStats.NameConflicts++; +#if APPLE_OSX_mDNSResponder + // See if this record was also registered with any D2D plugins. + D2D_stop_advertising_record(rr); +#endif + mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); + } + } } // We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the @@ -9121,6 +9384,9 @@ mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m, { LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr)); m->mDNSStats.KnownUniqueNameConflicts++; +#if APPLE_OSX_mDNSResponder + D2D_stop_advertising_record(rr); +#endif mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict); } else @@ -9289,7 +9555,7 @@ exit: continue; } } - + // For Unicast (null InterfaceID) the resolver IDs should also match if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) && (r1->resrec.InterfaceID || (id1 == id2)) && @@ -9313,7 +9579,7 @@ exit: // to give us an aged TTL to correct for how long it has held the record, // so our received TTLs are expected to vary in that case - // We also suppress log message in the case of SRV records that are recieved + // We also suppress log message in the case of SRV records that are received // with a TTL of 4500 that are already cached with a TTL of 120 seconds, since // this behavior was observed for a number of discoveryd based AppleTV's in iOS 8 // GM builds. @@ -9388,7 +9654,7 @@ exit: // Note: We need to do this before we call CacheRecordDeferredAdd as this // might start the verification process which needs these NSEC records if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) - { + { LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); FreeNSECRecords(m, NSECRecords); } @@ -9408,7 +9674,7 @@ exit: { LogInfo("mDNSCoreReceieveResponse: Updating NSEC records in %s", CRDisplayString(m, NSECCachePtr)); if (!AddNSECSForCacheRecord(m, NSECRecords, NSECCachePtr, rcode)) - { + { LogInfo("mDNSCoreReceiveResponse: AddNSECSForCacheRecord failed to add NSEC for %s", CRDisplayString(m, NSECCachePtr)); FreeNSECRecords(m, NSECRecords); } @@ -9488,8 +9754,8 @@ mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus re LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar)); if (ar->WakeUp.HMAC.l[0]) { - SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password); // Send one wakeup magic packet - ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken + SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password, mDNSfalse); // Send one wakeup magic packet + ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC); // Schedule all other records with the same owner to be woken } mDNS_Unlock(m); } @@ -9519,7 +9785,7 @@ mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *et } else if (*ptr == ':') { - if (colons >=5 || val > 255) + if (colons >=5) { LogMsg("GetValueForMACAddr: Address malformed colons %d val %d", colons, val); return mDNSNULL; @@ -9704,32 +9970,33 @@ mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *valu mDNSexport mDNSBool mDNSValidKeepAliveRecord(AuthRecord *rr) { - mDNSAddr laddr, raddr; - mDNSEthAddr eth; - mDNSIPPort lport, rport; - mDNSu32 timeout, seq, ack; - mDNSu16 win; + mDNSAddr laddr, raddr; + mDNSEthAddr eth; + mDNSIPPort lport, rport; + mDNSu32 timeout, seq, ack; + mDNSu16 win; - if (!mDNS_KeepaliveRecord(&rr->resrec)) - { - return mDNSfalse; - } + if (!mDNS_KeepaliveRecord(&rr->resrec)) + { + return mDNSfalse; + } - timeout = seq = ack = 0; - win = 0; - laddr = raddr = zeroAddr; - lport = rport = zeroIPPort; + timeout = seq = ack = 0; + win = 0; + laddr = raddr = zeroAddr; + lport = rport = zeroIPPort; + eth = zeroEthAddr; - mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); + mDNS_ExtractKeepaliveInfo(rr, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win); - if (mDNSAddressIsZero(&laddr) || mDNSIPPortIsZero(lport) || - mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(rport) || - mDNSEthAddressIsZero(eth)) - { - return mDNSfalse; - } + if (mDNSAddressIsZero(&laddr) || mDNSIPPortIsZero(lport) || + mDNSAddressIsZero(&raddr) || mDNSIPPortIsZero(rport) || + mDNSEthAddressIsZero(eth)) + { + return mDNSfalse; + } - return mDNStrue; + return mDNStrue; } @@ -9760,7 +10027,7 @@ mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSA raddr->type = mDNSAddrType_IPv4; ptr = GetValueForIPv4Addr(ptr, limit, &raddr->ip.v4); } - if (param == 'H') + else if (param == 'H') { laddr->type = mDNSAddrType_IPv6; ptr = GetValueForIPv6Addr(ptr, limit, &laddr->ip.v6); @@ -10106,6 +10373,35 @@ mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m, mDNS_SendKeepalives(m); } +mDNSlocal mDNSu32 mDNSGenerateOwnerOptForInterface(mDNS *const m, const mDNSInterfaceID InterfaceID, DNSMessage *msg) +{ + mDNSu8 *ptr = msg->data; + mDNSu8 *end = mDNSNULL; + mDNSu32 length = 0; + AuthRecord opt; + + mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL); + opt.resrec.rrclass = NormalMaxDNSMessageData; + opt.resrec.rdlength = sizeof(rdataOPT); + opt.resrec.rdestimate = sizeof(rdataOPT); + + NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID); + SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]); + + LogSPS("Generated OPT record : %s", ARDisplayString(m, &opt)); + end = PutResourceRecord(msg, ptr, &msg->h.numAdditionals, &opt.resrec); + if (end != mDNSNULL) + { + // Put all the integer values in IETF byte-order (MSB first, LSB second) + SwapDNSHeaderBytes(msg); + length = (end - msg->data); + } + else + LogSPS("mDNSGenerateOwnerOptForInterface: Failed to generate owner OPT record"); + + return length; +} + mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID) { if (InterfaceID) @@ -10166,6 +10462,18 @@ mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg ifname = InterfaceNameForID(m, InterfaceID); mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr)); mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname); + + // Store the Owner OPT record for this interface. + // Configd may use the OPT record if it detects a conflict with the BSP when the system wakes up + DNSMessage optMsg; + int length = 0; + InitializeDNSMessage(&optMsg.h, zeroID, ResponseFlags); + length = mDNSGenerateOwnerOptForInterface(m, InterfaceID, &optMsg); + if (length != 0) + { + length += sizeof(DNSMessageHeader); + mDNSPlatformStoreOwnerOptRecord(ifname, &optMsg, length); + } } // 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. @@ -10309,17 +10617,21 @@ mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *co 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)", - msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); if (mDNS_LoggingEnabled) { - int i = 0; - while (i<end - (mDNSu8 *)pkt) - { - char buffer[128]; - char *p = buffer + mDNS_snprintf(buffer, sizeof(buffer), "%04X", i); - do if (i<end - (mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]);while (++i & 15); - LogInfo("%s", buffer); + static int msgCount = 0; + if (msgCount < 1000) { + msgCount++; + int i = 0; + LogInfo("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)", + msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID); + while (i<end - (mDNSu8 *)pkt) + { + char buffer[128]; + char *p = buffer + mDNS_snprintf(buffer, sizeof(buffer), "%04X", i); + do if (i<end - (mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]);while (++i & 15); + LogInfo("%s", buffer); + } } } } @@ -10435,15 +10747,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; + // No need to close old q->LocalSocket first -- duplicate questions can't have their own sockets q->state = question->state; - // q->tcp = question->tcp; + // q->tcp = question->tcp; q->ReqLease = question->ReqLease; q->expire = question->expire; q->ntries = question->ntries; @@ -10451,7 +10759,7 @@ mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const questi question->LocalSocket = mDNSNULL; question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question - // question->tcp = mDNSNULL; + // question->tcp = mDNSNULL; if (q->LocalSocket) debugf("UpdateQuestionDuplicates transferred LocalSocket pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); @@ -10657,7 +10965,7 @@ mDNSlocal mDNSBool DNSServerMatch(DNSServer *d, mDNSInterfaceID InterfaceID, mDN // // 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. // @@ -10715,7 +11023,7 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) // match the scoped entries by mistake. // // Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout - + // Skip DNSServers that are InterfaceID Scoped but have no valid interfaceid set OR DNSServers that are ServiceID Scoped but have no valid serviceid set if ((curr->scoped == kScopeInterfaceID && curr->interface == mDNSInterface_Any) || (curr->scoped == kScopeServiceID && curr->serviceID <= 0)) { @@ -10724,7 +11032,8 @@ mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question) } currcount = CountLabels(&curr->domain); - if ((!DEQuery || !curr->cellIntf) && DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) + if ((!curr->cellIntf || (!DEQuery && !(question->flags & kDNSServiceFlagsDenyCellular))) && + DNSServerMatch(curr, question->InterfaceID, question->ServiceID)) { bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen); @@ -10963,7 +11272,7 @@ mDNSlocal mDNSBool ShouldSuppressUnicastQuery(mDNS *const m, DNSQuestion *q, DNS 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)) { @@ -11037,6 +11346,12 @@ mDNSlocal mDNSBool ShouldSuppressDotLocalQuery(mDNS *const m, DNSQuestion *q) mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, DNSQuestion *q) { + if (q->InterfaceID == mDNSInterface_LocalOnly) + { + LogInfo("ShouldSuppressQuery: LocalOnly query not suppressed for %##s, qtype %s", q->qname.c, DNSTypeName(q->qtype)); + return mDNSfalse; + } + 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)); @@ -11310,7 +11625,7 @@ mDNSlocal void RestartUnicastQuestions(mDNS *const m) { 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); } @@ -11339,7 +11654,7 @@ mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const question) } // If no question->Target specified, clear TargetPort - if (!question->Target.type) + if (!question->Target.type) question->TargetPort = zeroIPPort; if (!ValidateDomainName(&question->qname)) @@ -11349,14 +11664,14 @@ mDNSlocal mStatus ValidateParameters(mDNS *const m, DNSQuestion *const 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) + if (question->InterfaceID && !LocalOnlyOrP2PInterface(question->InterfaceID) && question->InterfaceID != mDNSInterface_Unicast) { NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID); if (!intf) LogInfo("ValidateParameters: Note: InterfaceID %d for question %##s (%s) not currently found in active interface list", (uint32_t)question->InterfaceID, question->qname.c, DNSTypeName(question->qtype)); } - + return(mStatus_NoError); } @@ -11367,19 +11682,19 @@ 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->triedAllServersOnce = 0; + question->noServerResponse = 0; question->StopTime = 0; #if TARGET_OS_EMBEDDED mDNSPlatformMemZero(&question->metrics, sizeof(question->metrics)); #endif // Need not initialize the DNS Configuration for Local Only OR P2P Questions - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + if (LocalOnlyOrP2PInterface(question->InterfaceID)) 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 @@ -11402,7 +11717,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort)); } else - { + { if (question->TimeoutQuestion) question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond); } @@ -11411,7 +11726,7 @@ mDNSlocal void InitDNSConfig(mDNS *const m, DNSQuestion *const question) 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); + SetNextQueryTime(m,question); } // InitCommonState() is called by mDNS_StartQuery_internal() to initialize the common(uDNS/mDNS) internal @@ -11420,7 +11735,7 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) { mDNSBool purge; int i; - mDNSBool isCellBlocked = mDNSfalse; + mDNSBool isBlocked = mDNSfalse; // 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 @@ -11468,15 +11783,21 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) question->LOAddressAnswers = 0; question->FlappingInterface1 = mDNSNULL; question->FlappingInterface2 = mDNSNULL; - - // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetDNSRoutePolicy() - // since we would already have the question->ServiceID in that case. - if (!(question->flags & kDNSServiceFlagsServiceIndex)) - mDNSPlatformGetDNSRoutePolicy(m, question, &isCellBlocked); - else - LogInfo("InitCommonState: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] is already set by client", question->qname.c, + + // if kDNSServiceFlagsServiceIndex flag is SET by the client, then do NOT call mDNSPlatformGetDNSRoutePolicy() + // since we would already have the question->ServiceID in that case. + if (!(question->flags & kDNSServiceFlagsServiceIndex)) + { +#if APPLE_OSX_mDNSResponder + mDNSPlatformGetDNSRoutePolicy(m, question, &isBlocked); +#else + question->ServiceID = -1; +#endif + } + else + LogInfo("InitCommonState: Query for %##s (%s), PID[%d], EUID[%d], ServiceID[%d] is already set by client", question->qname.c, DNSTypeName(question->qtype), question->pid, question->euid, question->ServiceID); - + InitDNSConfig(m, question); question->AuthInfo = GetAuthInfoForQuestion(m, question); @@ -11486,7 +11807,7 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) // If ServiceID is 0 or the policy disallows making DNS requests, // set DisallowPID - question->DisallowPID = (question->ServiceID == 0 || (isCellBlocked && question->qDNSServer && question->qDNSServer->cellIntf)); + question->DisallowPID = (question->ServiceID == 0 || isBlocked); 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); @@ -11494,47 +11815,27 @@ mDNSlocal mDNSBool InitCommonState(mDNS *const m, DNSQuestion *const question) 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 + question->RequestUnicast = kDefaultRequestUnicastCount; #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) + // Set the QU bit in the first query for the following options. + if ((question->flags & kDNSServiceFlagsUnicastResponse) || (question->flags & kDNSServiceFlagsThresholdFinder)) { - question->RequestUnicast = SET_QU_IN_FIRST_FOUR_QUERIES; + question->RequestUnicast = SET_QU_IN_FIRST_QUERY; 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->CNAMEReferrals = 0; question->WakeOnResolveCount = 0; if (question->WakeOnResolve) - { + { question->WakeOnResolveCount = InitialWakeOnResolveCount; purge = mDNStrue; - } + } for (i=0; i<DupSuppressInfoSize; i++) question->DupSuppress[i].InterfaceID = mDNSNULL; @@ -11561,7 +11862,7 @@ mDNSlocal void InitWABState(DNSQuestion *const question) // 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->LocalSocket = mDNSNULL; question->unansweredQueries = 0; question->nta = mDNSNULL; question->servAddr = zeroAddr; @@ -11593,6 +11894,11 @@ mDNSlocal void InitLLQState(DNSQuestion *const question) question->id = zeroOpaque64; } +mDNSlocal void InitDNSPNState(DNSQuestion *const question) +{ + question->dnsPushState = DNSPUSH_INIT; +} + // 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) @@ -11680,19 +11986,30 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu vStatus = ValidateParameters(m, question); if (vStatus) return(vStatus); - + +#ifdef USE_LIBIDN + // If the TLD includes high-ascii bytes, assume it will need to be converted to Punycode. + // (In the future the root name servers may answer UTF-8 queries directly, but for now they do not.) + if (IsHighASCIILabel(LastLabel(&question->qname))) + { + domainname newname; + if (PerformNextPunycodeConversion(question, &newname)) + AssignDomainName(&question->qname, &newname); + } +#endif // USE_LIBIDN + question->TargetQID = #ifndef UNICAST_DISABLED (question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) : #endif // UNICAST_DISABLED zeroID; - debugf("mDNS_StartQuery_internal: %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - + 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) + if (LocalOnlyOrP2PInterface(question->InterfaceID)) q = &m->LocalOnlyQuestions; - while (*q && *q != question) + while (*q && *q != question) q=&(*q)->next; if (*q) @@ -11702,7 +12019,6 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu return(mStatus_AlreadyRegistered); } *q = question; - // Intialize the question. The only ordering constraint we have today is that // InitDNSSECProxyState should be called after the DNS server is selected (in @@ -11712,23 +12028,24 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu purge = InitCommonState(m, question); InitWABState(question); InitLLQState(question); + InitDNSPNState(question); InitDNSSECProxyState(m, question); // 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->DuplicateOf) + question->AuthInfo = question->DuplicateOf->AuthInfo; - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) + if (LocalOnlyOrP2PInterface(question->InterfaceID)) { - if (!m->NewLocalOnlyQuestions) + if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question; } else { - if (!m->NewQuestions) + if (!m->NewQuestions) m->NewQuestions = question; // If the question's id is non-zero, then it's Wide Area @@ -11743,13 +12060,22 @@ mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const qu } else { -#if TARGET_OS_WATCH - m->NumAllInterfaceQuestions++; - LogInfo("mDNS_StartQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", - m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); - if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) - m->NetworkChanged = m->timenow; -#endif +#if BONJOUR_ON_DEMAND + m->NumAllInterfaceQuestions++; + LogInfo("mDNS_StartQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + { + m->NextBonjourDisableTime = 0; + if (m->BonjourEnabled == 0) + { + // Enable Bonjour immediately by scheduling network changed processing where + // we will join the multicast group on each active interface. + m->BonjourEnabled = 1; + m->NetworkChanged = m->timenow; + } + } +#endif // BONJOUR_ON_DEMAND if (purge) { LogInfo("mDNS_StartQuery_internal: Purging for %##s", question->qname.c); @@ -11786,7 +12112,8 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que //LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype)); - if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) qp = &m->LocalOnlyQuestions; + if (LocalOnlyOrP2PInterface(question->InterfaceID)) + qp = &m->LocalOnlyQuestions; while (*qp && *qp != question) qp=&(*qp)->next; if (*qp) *qp = (*qp)->next; else @@ -11798,29 +12125,36 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que return(mStatus_BadReferenceErr); } -#if TARGET_OS_WATCH - if (question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_P2P && mDNSOpaque16IsZero(question->TargetQID)) +#if BONJOUR_ON_DEMAND + if (!LocalOnlyOrP2PInterface(question->InterfaceID) && mDNSOpaque16IsZero(question->TargetQID)) { - if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) - m->NetworkChanged = m->timenow; - m->NumAllInterfaceQuestions--; - LogInfo("mDNS_StopQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", - m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); + if (m->NumAllInterfaceRecords + m->NumAllInterfaceQuestions == 1) + m->NextBonjourDisableTime = NonZeroTime(m->timenow + (BONJOUR_DISABLE_DELAY * mDNSPlatformOneSecond)); + m->NumAllInterfaceQuestions--; + LogInfo("mDNS_StopQuery_internal: NumAllInterfaceRecords %d NumAllInterfaceQuestions %d %##s (%s)", + m->NumAllInterfaceRecords, m->NumAllInterfaceQuestions, question->qname.c, DNSTypeName(question->qtype)); } -#endif +#endif // BONJOUR_ON_DEMAND #if TARGET_OS_EMBEDDED - if (Question_uDNS(question) && !question->metrics.answered) + if (Question_uDNS(question) && !question->metrics.answered && (question->metrics.querySendCount > 0)) { - uDNSMetrics * metrics; const domainname * queryName; - mDNSBool isForCellular; + mDNSBool isForCell; + mDNSu32 durationMs; - metrics = &question->metrics; - queryName = metrics->originalQName ? metrics->originalQName : &question->qname; - isForCellular = (question->qDNSServer && question->qDNSServer->cellIntf); + queryName = question->metrics.originalQName ? question->metrics.originalQName : &question->qname; + isForCell = (question->qDNSServer && question->qDNSServer->cellIntf); - MetricsUpdateUDNSStats(queryName, mDNSfalse, metrics->querySendCount, 0, isForCellular); + if (question->metrics.querySendCount > 0) + { + durationMs = ((m->timenow - question->metrics.firstQueryTime) * 1000) / mDNSPlatformOneSecond; + } + else + { + durationMs = 0; + } + MetricsUpdateUDNSQueryStats(queryName, question->qtype, mDNSNULL, question->metrics.querySendCount, durationMs, isForCell); } #endif // Take care to cut question from list *before* calling UpdateQuestionDuplicates @@ -11835,16 +12169,31 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que if (rr->CRActiveQuestion == question) { DNSQuestion *q; - // Checking for ActiveQuestion filters questions that are suppressed also - // as suppressed questions are not active - for (q = m->Questions; q; q=q->next) // Scan our list of questions - if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) - break; - if (q) + DNSQuestion *replacement = mDNSNULL; + // If we find an active question that is answered by this cached record, use it as the cache record's + // CRActiveQuestion replacement. If there are no such questions, but there's at least one unsuppressed inactive + // question that is answered by this cache record, then use an inactive one to not forgo generating RMV events + // via CacheRecordRmv() when the cache record expires. + for (q = m->Questions; q && (q != m->NewQuestions); q = q->next) + { + if (!q->DuplicateOf && !QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q)) + { + if (q->ThisQInterval > 0) + { + replacement = q; + break; + } + else if (!replacement) + { + replacement = q; + } + } + } + if (replacement) debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question " - "CurrentAnswers %d, SuppressQuery %d", q, CRDisplayString(m,rr), question->CurrentAnswers, q->CurrentAnswers, q->SuppressQuery); - rr->CRActiveQuestion = q; // Question used to be active; new value may or may not be null - if (!q) m->rrcache_active--; // If no longer active, decrement rrcache_active count + "CurrentAnswers %d, SuppressQuery %d", replacement, CRDisplayString(m,rr), question->CurrentAnswers, replacement->CurrentAnswers, replacement->SuppressQuery); + rr->CRActiveQuestion = replacement; // Question used to be active; new value may or may not be null + if (!replacement) m->rrcache_active--; // If no longer active, decrement rrcache_active count } } @@ -11927,6 +12276,15 @@ mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const que question->tcp = mDNSNULL; } } + else if (question->dnsPushState == DNSPUSH_ESTABLISHED) + { + if (question->tcp) + { + UnSubscribeToDNSPushNotificationServer(m, q); + question->tcp->question = mDNSNULL; + question->tcp = mDNSNULL; + } + } #if APPLE_OSX_mDNSResponder UpdateAutoTunnelDomainStatuses(m); #endif @@ -12043,10 +12401,8 @@ mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const qu question->LongLived = mDNStrue; question->ExpectUnique = mDNSfalse; question->ForceMCast = ForceMCast; - question->ReturnIntermed = mDNSfalse; + question->ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0; question->SuppressUnusable = mDNSfalse; - question->DenyOnCellInterface = mDNSfalse; - question->DenyOnExpInterface = mDNSfalse; question->SearchListIndex = 0; question->AppendSearchDomains = 0; question->RetryWithSearchDomains = mDNSfalse; @@ -12087,295 +12443,6 @@ mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, return(status); } -mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m) -{ - NetworkInterfaceInfo *intf; - for (intf = m->HostInterfaces; intf; intf = intf->next) - if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue); - return(mDNSfalse); -} - -mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) -{ - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - mDNSBool PortChanged = !mDNSSameIPPort(query->info->port, answer->rdata->u.srv.port); - if (!AddRecord) return; - if (answer->rrtype != kDNSType_SRV) return; - - query->info->port = answer->rdata->u.srv.port; - - // If this is our first answer, then set the GotSRV flag and start the address query - if (!query->GotSRV) - { - query->GotSRV = mDNStrue; - query->qAv4.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); - query->qAv6.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); - mDNS_StartQuery(m, &query->qAv4); - // Only do the AAAA query if this machine actually has IPv6 active - if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); - } - // If this is not our first answer, only re-issue the address query if the target host name has changed - else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) || - !SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target)) - { - mDNS_StopQuery(m, &query->qAv4); - if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6); - if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged) - { - // If we get here, it means: - // 1. This is not our first SRV answer - // 2. The interface ID is different, but the target host and port are the same - // This implies that we're seeing the exact same SRV record on more than one interface, so we should - // make our address queries at least as broad as the original SRV query so that we catch all the answers. - query->qAv4.InterfaceID = query->qSRV.InterfaceID; // Will be mDNSInterface_Any, or a specific interface - query->qAv6.InterfaceID = query->qSRV.InterfaceID; - } - else - { - query->qAv4.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target); - query->qAv6.InterfaceID = answer->InterfaceID; - AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target); - } - debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query->qAv4.qname.c, DNSTypeName(query->qAv4.qtype)); - mDNS_StartQuery(m, &query->qAv4); - // Only do the AAAA query if this machine actually has IPv6 active - if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6); - } - else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged) - { - if (++query->Answers >= 100) - debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u", - query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c, - mDNSVal16(answer->rdata->u.srv.port)); - query->ServiceInfoQueryCallback(m, query); - } - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. -} - -mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) -{ - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - if (!AddRecord) return; - if (answer->rrtype != kDNSType_TXT) return; - if (answer->rdlength > sizeof(query->info->TXTinfo)) return; - - query->GotTXT = mDNStrue; - query->info->TXTlen = answer->rdlength; - query->info->TXTinfo[0] = 0; // In case answer->rdlength is zero - mDNSPlatformMemCopy(query->info->TXTinfo, answer->rdata->u.txt.c, answer->rdlength); - - verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD); - - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. - if (query->ServiceInfoQueryCallback && query->GotADD) - { - if (++query->Answers >= 100) - debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...", - query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c); - query->ServiceInfoQueryCallback(m, query); - } -} - -mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord) -{ - ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext; - //LogInfo("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer)); - if (!AddRecord) return; - - if (answer->rrtype == kDNSType_A) - { - query->info->ip.type = mDNSAddrType_IPv4; - query->info->ip.ip.v4 = answer->rdata->u.ipv4; - } - else if (answer->rrtype == kDNSType_AAAA) - { - query->info->ip.type = mDNSAddrType_IPv6; - query->info->ip.ip.v6 = answer->rdata->u.ipv6; - } - else - { - debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype)); - return; - } - - query->GotADD = mDNStrue; - query->info->InterfaceID = answer->InterfaceID; - - verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT); - - // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's - // callback function is allowed to do anything, including deleting this query and freeing its memory. - if (query->ServiceInfoQueryCallback && query->GotTXT) - { - if (++query->Answers >= 100) - debugf(answer->rrtype == kDNSType_A ? - "**** WARNING **** have given %lu answers for %##s (A) %.4a" : - "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", - query->Answers, query->qSRV.qname.c, &answer->rdata->u.data); - query->ServiceInfoQueryCallback(m, query); - } -} - -// On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure -// If the query is not interface-specific, then InterfaceID may be zero -// Each time the Callback is invoked, the remainder of the fields will have been filled in -// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response -mDNSexport mStatus mDNS_StartResolveService(mDNS *const m, - ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context) -{ - mStatus status; - mDNS_Lock(m); - - query->qSRV.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qSRV.InterfaceID = info->InterfaceID; - query->qSRV.flags = 0; - query->qSRV.Target = zeroAddr; - AssignDomainName(&query->qSRV.qname, &info->name); - query->qSRV.qtype = kDNSType_SRV; - query->qSRV.qclass = kDNSClass_IN; - query->qSRV.LongLived = mDNSfalse; - query->qSRV.ExpectUnique = mDNStrue; - query->qSRV.ForceMCast = mDNSfalse; - query->qSRV.ReturnIntermed = mDNSfalse; - query->qSRV.SuppressUnusable = mDNSfalse; - query->qSRV.DenyOnCellInterface = mDNSfalse; - query->qSRV.DenyOnExpInterface = mDNSfalse; - query->qSRV.SearchListIndex = 0; - query->qSRV.AppendSearchDomains = 0; - query->qSRV.RetryWithSearchDomains = mDNSfalse; - query->qSRV.TimeoutQuestion = 0; - query->qSRV.WakeOnResolve = 0; - 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; - - query->qTXT.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qTXT.InterfaceID = info->InterfaceID; - query->qTXT.flags = 0; - query->qTXT.Target = zeroAddr; - AssignDomainName(&query->qTXT.qname, &info->name); - query->qTXT.qtype = kDNSType_TXT; - query->qTXT.qclass = kDNSClass_IN; - query->qTXT.LongLived = mDNSfalse; - query->qTXT.ExpectUnique = mDNStrue; - query->qTXT.ForceMCast = mDNSfalse; - query->qTXT.ReturnIntermed = mDNSfalse; - query->qTXT.SuppressUnusable = mDNSfalse; - query->qTXT.DenyOnCellInterface = mDNSfalse; - query->qTXT.DenyOnExpInterface = mDNSfalse; - query->qTXT.SearchListIndex = 0; - query->qTXT.AppendSearchDomains = 0; - query->qTXT.RetryWithSearchDomains = mDNSfalse; - query->qTXT.TimeoutQuestion = 0; - query->qTXT.WakeOnResolve = 0; - 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; - - query->qAv4.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qAv4.InterfaceID = info->InterfaceID; - query->qAv4.flags = 0; - query->qAv4.Target = zeroAddr; - query->qAv4.qname.c[0] = 0; - query->qAv4.qtype = kDNSType_A; - query->qAv4.qclass = kDNSClass_IN; - query->qAv4.LongLived = mDNSfalse; - query->qAv4.ExpectUnique = mDNStrue; - query->qAv4.ForceMCast = mDNSfalse; - query->qAv4.ReturnIntermed = mDNSfalse; - query->qAv4.SuppressUnusable = mDNSfalse; - query->qAv4.DenyOnCellInterface = mDNSfalse; - query->qAv4.DenyOnExpInterface = mDNSfalse; - query->qAv4.SearchListIndex = 0; - query->qAv4.AppendSearchDomains = 0; - query->qAv4.RetryWithSearchDomains = mDNSfalse; - query->qAv4.TimeoutQuestion = 0; - query->qAv4.WakeOnResolve = 0; - 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; - - query->qAv6.ThisQInterval = -1; // So that mDNS_StopResolveService() knows whether to cancel this question - query->qAv6.InterfaceID = info->InterfaceID; - query->qAv6.flags = 0; - query->qAv6.Target = zeroAddr; - query->qAv6.qname.c[0] = 0; - query->qAv6.qtype = kDNSType_AAAA; - query->qAv6.qclass = kDNSClass_IN; - query->qAv6.LongLived = mDNSfalse; - query->qAv6.ExpectUnique = mDNStrue; - query->qAv6.ForceMCast = mDNSfalse; - query->qAv6.ReturnIntermed = mDNSfalse; - query->qAv6.SuppressUnusable = mDNSfalse; - query->qAv6.DenyOnCellInterface = mDNSfalse; - query->qAv6.DenyOnExpInterface = mDNSfalse; - query->qAv6.SearchListIndex = 0; - query->qAv6.AppendSearchDomains = 0; - query->qAv6.RetryWithSearchDomains = mDNSfalse; - query->qAv6.TimeoutQuestion = 0; - 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; - - query->GotSRV = mDNSfalse; - query->GotTXT = mDNSfalse; - query->GotADD = mDNSfalse; - query->Answers = 0; - - query->info = info; - query->ServiceInfoQueryCallback = Callback; - query->ServiceInfoQueryContext = Context; - -// info->name = Must already be set up by client -// info->interface = Must already be set up by client - info->ip = zeroAddr; - info->port = zeroIPPort; - info->TXTlen = 0; - - // We use mDNS_StartQuery_internal here because we're already holding the lock - status = mDNS_StartQuery_internal(m, &query->qSRV); - if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT); - if (status != mStatus_NoError) mDNS_StopResolveService(m, query); - - mDNS_Unlock(m); - return(status); -} - -mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q) -{ - mDNS_Lock(m); - // We use mDNS_StopQuery_internal here because we're already holding the lock - if (q->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qSRV); - if (q->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qTXT); - if (q->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv4); - if (q->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv6); - mDNS_Unlock(m); -} mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom, const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context) @@ -12390,8 +12457,6 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->ForceMCast = mDNSfalse; question->ReturnIntermed = mDNSfalse; question->SuppressUnusable = mDNSfalse; - question->DenyOnCellInterface = mDNSfalse; - question->DenyOnExpInterface = mDNSfalse; question->SearchListIndex = 0; question->AppendSearchDomains = 0; question->RetryWithSearchDomains = mDNSfalse; @@ -12404,7 +12469,7 @@ mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, m question->qnameOrig = mDNSNULL; question->AnonInfo = mDNSNULL; question->pid = mDNSPlatformGetPID(); - question->euid = 0; + question->euid = 0; question->QuestionCallback = Callback; question->QuestionContext = Context; if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr); @@ -12615,6 +12680,10 @@ mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set) return; } +#if APPLE_OSX_mDNSResponder + D2D_stop_advertising_interface(set); +#endif // APPLE_OSX_mDNSResponder + // Unregister these records. // When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it. @@ -12623,11 +12692,6 @@ 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) @@ -12656,6 +12720,23 @@ mDNSlocal void DeadvertiseAllInterfaceRecords(mDNS *const m) } } +// Change target host name for record. +mDNSlocal void UpdateTargetHostName(mDNS *const m, AuthRecord *const rr) +{ +#if APPLE_OSX_mDNSResponder + // If this record was also registered with any D2D plugins, stop advertising + // the version with the old host name. + D2D_stop_advertising_record(rr); +#endif + + SetTargetToHostName(m, rr); + +#if APPLE_OSX_mDNSResponder + // Advertise the record with the updated host name with the D2D plugins if appropriate. + D2D_start_advertising_record(rr); +#endif +} + mDNSexport void mDNS_SetFQDN(mDNS *const m) { domainname newmname; @@ -12676,8 +12757,8 @@ mDNSexport void mDNS_SetFQDN(mDNS *const m) } // 3. Make sure that any AutoTarget SRV records (and the like) get updated - for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); - for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr); + for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->AutoTarget) UpdateTargetHostName(m, rr); + for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) UpdateTargetHostName(m, rr); mDNS_Unlock(m); } @@ -12766,7 +12847,7 @@ mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set) // 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++) { @@ -12916,6 +12997,7 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s } LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay); + if (m->SuppressProbes == 0 || m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0) m->SuppressProbes = NonZeroTime(m->timenow + probedelay); @@ -12942,14 +13024,8 @@ mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *s if (!q->ThisQInterval || q->ThisQInterval > initial) { - q->ThisQInterval = initial; - -#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->ThisQInterval = initial; + q->RequestUnicast = kDefaultRequestUnicastCount; } q->LastQTime = m->timenow - q->ThisQInterval + qdelay; q->RecentAnswerPkts = 0; @@ -13235,13 +13311,17 @@ mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result) } +// Derive AuthRecType from the coreFlag* values. +// Note, this is not using the external flags values, kDNSServiceFlags*, defined in dns_sd.h. +// It should be changed to do so once the use of coreFlag* is completely replaced with +// the use the kDNSServiceFlags* definitions within mDNSResponder. mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) { AuthRecType artype; if (InterfaceID == mDNSInterface_LocalOnly) artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) + else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE) artype = AuthRecordP2P; else if ((InterfaceID == mDNSInterface_Any) && (flags & coreFlagIncludeP2P) && (flags & coreFlagIncludeAWDL)) @@ -13256,6 +13336,18 @@ mDNSlocal AuthRecType setAuthRecType(mDNSInterfaceID InterfaceID, mDNSu32 flags) return artype; } +// Used to derive the original D2D specific flags specified by the client in the registration +// when we don't have access to the original flag (kDNSServiceFlags*) values. +mDNSexport mDNSu32 deriveD2DFlagsFromAuthRecType(AuthRecType authRecType) +{ + mDNSu32 flags = 0; + if ((authRecType == AuthRecordAnyIncludeP2P) || (authRecType == AuthRecordAnyIncludeAWDLandP2P)) + flags |= kDNSServiceFlagsIncludeP2P; + else if ((authRecType == AuthRecordAnyIncludeAWDL) || (authRecType == AuthRecordAnyIncludeAWDLandP2P)) + flags |= kDNSServiceFlagsIncludeAWDL; + return flags; +} + // Note: // Name is first label of domain name (any dots in the name are actual dots, not label separators) // Type is service type (e.g. "_ipp._tcp.") @@ -13310,8 +13402,8 @@ mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr, if (mDNSIPPortIsZero(port)) return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, flags)); - // If the client is registering an oversized TXT record, - // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it + // If the caller is registering an oversized TXT record, + // it is the caller's responsibility to allocate a ServiceRecordSet structure that is large enough for it if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen) sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen; @@ -13349,7 +13441,7 @@ 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. @@ -13409,6 +13501,7 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, mStatus status; AuthRecType artype; mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID; + ResourceRecord *rr; artype = setAuthRecType(InterfaceID, flags); @@ -13418,6 +13511,37 @@ mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name); mDNS_Lock(m); + rr = mDNSNULL; + if (extra->r.resrec.rrtype == kDNSType_TXT) + { + if (sr->RR_TXT.resrec.RecordType & kDNSRecordTypeUniqueMask) rr = &sr->RR_TXT.resrec; + } + else if (extra->r.resrec.rrtype == kDNSType_SRV) + { + if (sr->RR_SRV.resrec.RecordType & kDNSRecordTypeUniqueMask) rr = &sr->RR_SRV.resrec; + } + + if (!rr) + { + ExtraResourceRecord *srExtra; + + for (srExtra = sr->Extras; srExtra; srExtra = srExtra->next) + { + if ((srExtra->r.resrec.rrtype == extra->r.resrec.rrtype) && (srExtra->r.resrec.RecordType & kDNSRecordTypeUniqueMask)) + { + rr = &srExtra->r.resrec; + break; + } + } + } + + if (rr && (extra->r.resrec.rroriginalttl != rr->rroriginalttl)) + { + LogMsg("mDNS_AddRecordToService: Correcting TTL from %4d to %4d for %s", + extra->r.resrec.rroriginalttl, rr->rroriginalttl, RRDisplayString(m, &extra->r.resrec)); + extra->r.resrec.rroriginalttl = rr->rroriginalttl; + } + e = &sr->Extras; while (*e) e = &(*e)->next; @@ -13593,7 +13717,7 @@ mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, if (InterfaceID == mDNSInterface_LocalOnly) artype = AuthRecordLocalOnly; - else if (InterfaceID == mDNSInterface_P2P) + else if (InterfaceID == mDNSInterface_P2P || InterfaceID == mDNSInterface_BLE) artype = AuthRecordP2P; else artype = AuthRecordAny; @@ -13820,9 +13944,9 @@ mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, } else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID); - else if (msg == msg4) + else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa, sha); - else if (msg == msg5) + else if (msg == msg5) SendNDP(m, NDP_Adv, 0, rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth); } } @@ -14250,6 +14374,14 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->NextScheduledSPS = timenow + 0x78000000; m->NextScheduledKA = timenow + 0x78000000; m->NextScheduledStopTime = timenow + 0x78000000; + m->NextBLEServiceTime = 0; // zero indicates inactive + +#if BONJOUR_ON_DEMAND + m->NextBonjourDisableTime = 0; // Timer active when non zero. + m->BonjourEnabled = 0; // Set when Bonjour on Demand is enabled and Bonjour is currently enabled. +#endif // BONJOUR_ON_DEMAND + + m->DelayConflictProcessing = MAX_CONFLICT_PROCESSING_DELAYS; m->RandomQueryDelay = 0; m->RandomReconfirmDelay = 0; m->PktNum = 0; @@ -14334,19 +14466,11 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->WABBrowseQueriesCount = 0; m->WABLBrowseQueriesCount = 0; m->WABRegQueriesCount = 0; -#if TARGET_OS_EMBEDDED || TARGET_OS_WATCH m->AutoTargetServices = 0; -#else - m->AutoTargetServices = 1; -#endif -#if TARGET_OS_WATCH + +#if BONJOUR_ON_DEMAND m->NumAllInterfaceRecords = 0; m->NumAllInterfaceQuestions = 0; -#else - // Initialize to 1 for these targets to prevent not joining multicast group for interfaces when - // both of these values are zero. - m->NumAllInterfaceRecords = 1; - m->NumAllInterfaceQuestions = 1; #endif // NAT traversal fields m->LLQNAT.clientCallback = mDNSNULL; @@ -14386,6 +14510,8 @@ mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p, m->SPSBrowseCallback = mDNSNULL; m->ProxyRecords = 0; + m->DNSPushServers = mDNSNULL; + m->DNSPushZones = mDNSNULL; #endif #if APPLE_OSX_mDNSResponder @@ -14612,6 +14738,23 @@ mDNSlocal void SetConfigState(mDNS *const m, mDNSBool delete) } } +mDNSlocal void SetDynDNSHostNameIfChanged(mDNS *const m, domainname *const fqdn) +{ + // Did our FQDN change? + if (!SameDomainName(fqdn, &m->FQDN)) + { + if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN); + + AssignDomainName(&m->FQDN, fqdn); + + if (m->FQDN.c[0]) + { + mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); + mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL); + } + } +} + mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) { mDNSu32 slot; @@ -14642,6 +14785,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) SetConfigState(m, mDNStrue); if (!mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL, mDNStrue)) { + SetDynDNSHostNameIfChanged(m, &fqdn); SetConfigState(m, mDNSfalse); mDNS_Unlock(m); LogInfo("uDNS_SetupDNSConfig: No configuration change"); @@ -14818,6 +14962,14 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse); } } + + // If a cache record's DNSServer pointer is NULL, but its active question got a DNSServer in this DNS configuration + // update, then use its DNSServer. This way, the active question and its duplicates don't miss out on RMV events. + if (!cr->resrec.rDNSServer && cr->CRActiveQuestion && cr->CRActiveQuestion->qDNSServer) + { + cr->resrec.rDNSServer = cr->CRActiveQuestion->qDNSServer; + LogInfo("uDNS_SetupDNSConfig: Using active question's DNS server %#a for cache record %s", &cr->resrec.rDNSServer->addr, CRDisplayString(m, cr)); + } } while (*p) @@ -14848,7 +15000,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) if (qptr->qDNSServer == ptr) { - LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) poining to DNSServer Address %#a" + LogMsg("uDNS_SetupDNSConfig: ERROR!! Cache Record %s Active question %##s (%s) (scope:%p) pointing to DNSServer Address %#a" " to be freed", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), qptr->InterfaceID, &ptr->addr); qptr->validDNSServers = zeroOpaque64; qptr->qDNSServer = mDNSNULL; @@ -14858,7 +15010,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) { LogInfo("uDNS_SetupDNSConfig: Cache Record %s, Active question %##s (%s) (scope:%p), pointing to DNSServer %#a (to be deleted)," " resetting to question's DNSServer Address %#a", CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), - qptr->InterfaceID, &ptr->addr, (qptr->qDNSServer ? &qptr->qDNSServer->addr : mDNSNULL)); + qptr->InterfaceID, &ptr->addr, (qptr->qDNSServer) ? &qptr->qDNSServer->addr : mDNSNULL); cr->resrec.rDNSServer = qptr->qDNSServer; } } @@ -14905,19 +15057,7 @@ mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m) RestartRecordGetZoneData(m); } - // Did our FQDN change? - if (!SameDomainName(&fqdn, &m->FQDN)) - { - if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN); - - AssignDomainName(&m->FQDN, &fqdn); - - if (m->FQDN.c[0]) - { - mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1); - mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL); - } - } + SetDynDNSHostNameIfChanged(m, &fqdn); mDNS_Unlock(m); diff --git a/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h b/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h index 48f8280d..3fd654eb 100755 --- a/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h +++ b/mDNSResponder/mDNSCore/mDNSEmbeddedAPI.h @@ -96,9 +96,9 @@ extern "C" { #ifdef LIMITED_RESOURCES_TARGET // Don't support jumbo frames // 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total -#define AbsoluteMaxDNSMessageData 1440 +#define AbsoluteMaxDNSMessageData 1440 // StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes) -#define MaximumRDSize 264 +#define MaximumRDSize 264 #endif // *************************************************************************** @@ -297,20 +297,20 @@ typedef mDNSOpaque48 mDNSEthAddr; // An Ethernet address is a six-byte opa #define bit_clr_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] &= ~(1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) #define bit_get_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] & (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY)))) -enum +typedef enum { mDNSAddrType_None = 0, mDNSAddrType_IPv4 = 4, mDNSAddrType_IPv6 = 6, mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording -}; +} mDNSAddr_Type; -enum +typedef enum { mDNSTransport_None = 0, mDNSTransport_UDP = 1, mDNSTransport_TCP = 2 -}; +} mDNSTransport_Type; typedef struct { @@ -364,7 +364,8 @@ enum mStatus_NoRouter = -65566, mStatus_PollingMode = -65567, mStatus_Timeout = -65568, - // -65568 to -65786 currently unused; available for allocation + mStatus_HostUnreachErr = -65569, + // -65570 to -65786 currently unused; available for allocation // tcp connection status mStatus_ConnPending = -65787, @@ -808,7 +809,7 @@ typedef struct TrustAnchor struct TrustAnchor *next; int digestLen; mDNSu32 validFrom; - mDNSu32 validUntil; + mDNSu32 validUntil; domainname zone; rdataDS rds; } TrustAnchor; @@ -875,10 +876,10 @@ typedef packedstruct // 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 +#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 +#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. @@ -1307,14 +1308,6 @@ struct NATTraversalInfo_struct enum { - DNSServer_Untested = 0, - DNSServer_Passed = 1, - DNSServer_Failed = 2, - DNSServer_Disabled = 3 -}; - -enum -{ DNSServer_FlagDelete = 0x1, DNSServer_FlagNew = 0x2, #if APPLE_OSX_mDNSResponder @@ -1342,8 +1335,9 @@ 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 + kScopeServiceID = 2, // Service specific DNS server used only by questions // have a matching serviceID + kScopesMaxCount = 3 // Max count for scopes enum }; // Note: DNSSECAware is set if we are able to get a valid response to @@ -1360,10 +1354,7 @@ typedef struct DNSServer mDNSs32 serviceID; mDNSAddr addr; mDNSIPPort port; - mDNSOpaque16 testid; mDNSu32 flags; // Set when we're planning to delete this from the list - 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 mDNSu32 scoped; // See the scoped enum above @@ -1606,8 +1597,11 @@ struct AuthRecord_struct #define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Unicast || (Q)->ProxyQuestion || \ ((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))) +// AuthRecordLocalOnly records are registered using mDNSInterface_LocalOnly and +// AuthRecordP2P records are created by D2DServiceFound events. Both record types are kept on the same list. #define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P) +// All other auth records, not including those defined as RRLocalOnly(). #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 @@ -1802,6 +1796,25 @@ enum enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 }; +// DNS Push Notification +typedef enum +{ + DNSPUSH_NOERROR = 0, + DNSPUSH_FORMERR = 1, + DNSPUSH_SERVFAIL = 2, + DNSPUSH_NOTIMP = 4, + DNSPUSH_REFUSED = 5 +} DNSPUSH_ErrorCode; + +typedef enum { + DNSPUSH_INIT = 1, + DNSPUSH_NOSERVER = 2, + DNSPUSH_SERVERFOUND = 3, + DNSPUSH_ESTABLISHED = 4 +} DNSPush_State; + + + #define HMAC_LEN 64 #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5c @@ -1893,6 +1906,10 @@ typedef struct mDNSBool answered; // Has this question been answered? } uDNSMetrics; + +extern mDNSu32 curr_num_regservices; // tracks the current number of services registered +extern mDNSu32 max_num_regservices; // tracks the max number of simultaneous services registered by the device + #endif struct DNSQuestion_struct @@ -1969,6 +1986,12 @@ 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 + + // DNS Push Notification fields. These fields are only meaningful when LongLived flag is set + DNSPush_State dnsPushState; // The state of the DNS push notification negotiation + mDNSAddr dnsPushServerAddr; // Address of the system acting as the DNS Push Server + mDNSIPPort dnsPushServerPort; // Port on which the DNS Push Server is being advertised. + mDNSOpaque64 id; // DNS Proxy fields @@ -1976,7 +1999,7 @@ struct DNSQuestion_struct // 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. @@ -1991,8 +2014,6 @@ 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 DenyOnCellInterface; // Set by client to suppress uDNS queries on cellular interface - mDNSBool DenyOnExpInterface; // Set by client to suppress uDNS queries on expensive interface 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 @@ -2014,43 +2035,7 @@ struct DNSQuestion_struct #endif }; -typedef struct -{ - // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() - // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. - domainname name; - mDNSInterfaceID InterfaceID; // ID of the interface the response was received on - mDNSAddr ip; // Remote (destination) IP address where this service can be accessed - mDNSIPPort port; // Port where this service can be accessed - mDNSu16 TXTlen; - mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) -} ServiceInfo; - -// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() -typedef struct ServiceInfoQuery_struct ServiceInfoQuery; -typedef void mDNSServiceInfoQueryCallback (mDNS *const m, ServiceInfoQuery *query); -struct ServiceInfoQuery_struct -{ - // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. - // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); - // all required data is passed as parameters to that function. - // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information - // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may - // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. - DNSQuestion qSRV; - DNSQuestion qTXT; - DNSQuestion qAv4; - DNSQuestion qAv6; - mDNSu8 GotSRV; - mDNSu8 GotTXT; - mDNSu8 GotADD; - mDNSu32 Answers; - ServiceInfo *info; - mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; - void *ServiceInfoQueryContext; -}; - -typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ } ZoneService; +typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ, ZoneServiceDNSPush } ZoneService; typedef void ZoneDataCallback (mDNS *const m, mStatus err, const ZoneData *result); @@ -2276,6 +2261,26 @@ typedef struct extern void LogMDNSStatistics(mDNS *const m); +typedef struct mDNS_DNSPushNotificationServer DNSPushNotificationServer; +typedef struct mDNS_DNSPushNotificationZone DNSPushNotificationZone; + +struct mDNS_DNSPushNotificationServer +{ + mDNSAddr serverAddr; // Server Address + tcpInfo_t *connection; // TCP Connection pointer + mDNSu32 numberOfQuestions; // Number of questions for this server + DNSPushNotificationServer *next; +} ; + +struct mDNS_DNSPushNotificationZone +{ + domainname zoneName; + DNSPushNotificationServer *servers; // DNS Push Notification Servers for this zone + mDNSu32 numberOfQuestions; // Number of questions for this zone + DNSPushNotificationZone *next; +} ; + + struct mDNS_struct { // Internal state fields. These hold the main internal state of mDNSCore; @@ -2320,6 +2325,11 @@ struct mDNS_struct mDNSs32 NextScheduledNATOp; // Next time to send NAT-traversal packets mDNSs32 NextScheduledSPS; // Next time to purge expiring Sleep Proxy records mDNSs32 NextScheduledKA; // Next time to send Keepalive packets (SPS) +#if BONJOUR_ON_DEMAND + mDNSs32 NextBonjourDisableTime; // Next time to leave multicast group if Bonjour on Demand is enabled + mDNSu8 BonjourEnabled; // Non zero if Bonjour is currently enabled by the Bonjour on Demand logic +#endif // BONJOUR_ON_DEMAND + mDNSs32 DelayConflictProcessing; // To prevent spurious confilcts due to stale packets on the wire/air. 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 @@ -2348,6 +2358,7 @@ struct mDNS_struct mDNSs32 NextScheduledStopTime; // Next time to stop a question + mDNSs32 NextBLEServiceTime; // Next time to call the BLE discovery management layer. Non zero when active. // These fields only required for mDNS Searcher... DNSQuestion *Questions; // List of all registered questions, active and inactive @@ -2384,7 +2395,7 @@ struct mDNS_struct mDNSs32 ProbeFailTime; mDNSu32 NumFailedProbes; mDNSs32 SuppressProbes; - Platform_t mDNS_plat; // Why is this here in the “only required for mDNS Responder” section? -- SC + Platform_t mDNS_plat; // Why is this here in the “only required for mDNS Responder” section? -- SC // Unicast-specific data mDNSs32 NextuDNSEvent; // uDNS next event @@ -2440,6 +2451,10 @@ struct mDNS_struct mDNSu8 *UPnPRouterAddressString; // holds both the router's address and port mDNSu8 *UPnPSOAPAddressString; // holds both address and port for SOAP messages + // DNS Push Notification fields + DNSPushNotificationServer *DNSPushServers; // DNS Push Notification Servers + DNSPushNotificationZone *DNSPushZones; + // Sleep Proxy client fields AuthRecord *SPSRRSet; // To help the client keep track of the records registered with the sleep proxy @@ -2472,8 +2487,13 @@ struct mDNS_struct int notifyToken; int uds_listener_skt; // Listening socket for incoming UDS clients. This should not be here -- it's private to uds_daemon.c and nothing to do with mDNSCore -- SC mDNSu32 AutoTargetServices; // # of services that have AutoTarget set - mDNSu32 NumAllInterfaceRecords; // Right now we count *all* multicast records here. Later we may want to change to count interface-specific records separately. (This count includes records on the DuplicateRecords list too.) - mDNSu32 NumAllInterfaceQuestions; // Right now we count *all* multicast questions here. Later we may want to change to count interface-specific questions separately. + +#if BONJOUR_ON_DEMAND + // Counters used in Bonjour on Demand logic. + mDNSu32 NumAllInterfaceRecords; // Right now we count *all* multicast records here. Later we may want to change to count interface-specific records separately. (This count includes records on the DuplicateRecords list too.) + mDNSu32 NumAllInterfaceQuestions; // Right now we count *all* multicast questions here. Later we may want to change to count interface-specific questions separately. +#endif // BONJOUR_ON_DEMAND + DNSSECStatistics DNSSECStats; mDNSStatistics mDNSStats; @@ -2501,6 +2521,9 @@ 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 mDNSInterfaceID mDNSInterface_BLE; // Special value + +#define LocalOnlyOrP2PInterface(INTERFACE) ((INTERFACE == mDNSInterface_LocalOnly) || (INTERFACE == mDNSInterface_P2P) || (INTERFACE == mDNSInterface_BLE)) extern const mDNSIPPort DiscardPort; extern const mDNSIPPort SSHPort; @@ -2542,6 +2565,8 @@ extern const mDNSOpaque16 DNSSecQFlags; extern const mDNSOpaque16 ResponseFlags; extern const mDNSOpaque16 UpdateReqFlags; extern const mDNSOpaque16 UpdateRespFlags; +extern const mDNSOpaque16 SubscribeFlags; +extern const mDNSOpaque16 UnSubscribeFlags; extern const mDNSOpaque64 zeroOpaque64; @@ -2606,7 +2631,7 @@ mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v) // Every client should call mDNS_Init, passing in storage for the mDNS object and the mDNS_PlatformSupport object. // // Clients that are only advertising services should use mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize. -// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, mDNS_StartResolveService, etc.) +// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, etc.) // need to provide storage for the resource record cache, or the query calls will return 'mStatus_NoCache'. // The rrcachestorage parameter is the address of memory for the resource record cache, and // the rrcachesize parameter is the number of entries in the CacheRecord array passed in. @@ -2719,11 +2744,6 @@ typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_De // mDNS_RegisterService is a single call to register the set of resource records associated with a given named service. // -// mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery, -// to find the IP address, port number, and demultiplexing information for a given named service. -// As with mDNS_StartQuery, it executes asynchronously, and calls the ServiceInfoQueryCallback when the answer is -// found. After the service is resolved, the client should call mDNS_StopResolveService to complete the transaction. -// The client can also call mDNS_StopResolveService at any time to abort the transaction. // // mDNS_AddRecordToService adds an additional record to a Service Record Set. This record may be deregistered // via mDNS_RemoveRecordFromService, or by deregistering the service. mDNS_RemoveRecordFromService is passed a @@ -2753,6 +2773,7 @@ enum coreFlagWakeOnly = 0x8 // Service won't be registered with sleep proxy }; +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, @@ -2780,8 +2801,6 @@ extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question, mDNSQuestionCallback *Callback, void *Context); #define mDNS_StopBrowse mDNS_StopQuery -extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); -extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); typedef enum { @@ -2824,7 +2843,7 @@ extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question); // because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size. // This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid. #define AssignDomainName(DST, SRC) do { mDNSu16 len__ = DomainNameLength((SRC)); \ - if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__);else (DST)->c[0] = 0;} while(0) + if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__); else (DST)->c[0] = 0; } while(0) // Comparison functions #define SameDomainLabelCS(A,B) ((A)[0] == (B)[0] && mDNSPlatformMemSame((A)+1, (B)+1, (A)[0])) @@ -2906,7 +2925,7 @@ extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel // then the output will be truncated by one character to allow space for the terminating null. // Unlike standard C vsnprintf/snprintf, they return the number of characters *actually* written, // not the number of characters that *would* have been printed were buflen unlimited. -extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg); +extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg) IS_A_PRINTF_STYLE_FUNCTION(3,0); extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4); extern mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id); extern char *DNSTypeName(mDNSu16 rrtype); @@ -3095,14 +3114,14 @@ extern mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCache extern mStatus mDNSPlatformInit (mDNS *const m); extern void mDNSPlatformClose (mDNS *const m); extern mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end, - mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, + 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); extern void mDNSPlatformStrCopy ( void *dst, const void *src); +extern mDNSu32 mDNSPlatformStrLCopy ( void *dst, const void *src, mDNSu32 len); extern mDNSu32 mDNSPlatformStrLen ( const void *src); extern void mDNSPlatformMemCopy ( void *dst, const void *src, mDNSu32 len); extern mDNSBool mDNSPlatformMemSame (const void *dst, const void *src, mDNSu32 len); @@ -3199,7 +3218,8 @@ extern void mDNSPlatformSendKeepalive(mDNSAddr *sadd, mDNSAddr *dadd, mDNS 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); -extern mStatus mDNSPlatformClearSPSMACAddr(void); +extern mStatus mDNSPlatformClearSPSData(void); +extern mStatus mDNSPlatformStoreOwnerOptRecord(char *ifname, DNSMessage *msg, int length); // mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd extern mStatus mDNSPlatformTLSSetupCerts(void); @@ -3220,7 +3240,7 @@ extern void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID In extern mDNSBool mDNSPlatformInterfaceIsD2D(mDNSInterfaceID InterfaceID); extern mDNSBool mDNSPlatformInterfaceIsAWDL(const NetworkInterfaceInfo *intf); extern mDNSBool mDNSPlatformValidRecordForQuestion(const ResourceRecord *const rr, const DNSQuestion *const q); -extern mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf); +extern mDNSBool mDNSPlatformValidRecordForInterface(const AuthRecord *rr, mDNSInterfaceID InterfaceID); extern mDNSBool mDNSPlatformValidQuestionForInterface(DNSQuestion *q, const NetworkInterfaceInfo *intf); extern void mDNSPlatformFormatTime(unsigned long t, mDNSu8 *buf, int bufsize); @@ -3315,7 +3335,7 @@ 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); +extern void UpdateRMAC(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); @@ -3353,16 +3373,13 @@ 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 void mDNSPlatformSleepAssertion(mDNS *const m, double timeout); -#endif - extern void mDNSPlatformGetDNSRoutePolicy(mDNS *const m, DNSQuestion *q, mDNSBool *isBlocked); -extern void mDNSPlatformSetuDNSSocktOpt(UDPSocket *src, const mDNSAddr *dst, DNSQuestion *q); +#endif +extern void mDNSPlatformSetSocktOpt(void *sock, mDNSTransport_Type transType, mDNSAddr_Type addrType, DNSQuestion *q); extern mDNSs32 mDNSPlatformGetPID(void); extern mDNSBool mDNSValidKeepAliveRecord(AuthRecord *rr); - + // *************************************************************************** #if 0 #pragma mark - @@ -3578,18 +3595,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) <= 864) ? 1 : -1]; + char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 894) ? 1 : -1]; - char sizecheck_ZoneData [(sizeof(ZoneData) <= 1700) ? 1 : -1]; + char sizecheck_ZoneData [(sizeof(ZoneData) <= 1730) ? 1 : -1]; char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 200) ? 1 : -1]; char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3050) ? 1 : -1]; - char sizecheck_DNSServer [(sizeof(DNSServer) <= 340) ? 1 : -1]; - char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 7184) ? 1 : -1]; + char sizecheck_DNSServer [(sizeof(DNSServer) <= 330) ? 1 : -1]; + char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <= 7272) ? 1 : -1]; char sizecheck_ServiceRecordSet [(sizeof(ServiceRecordSet) <= 5540) ? 1 : -1]; char sizecheck_DomainAuthInfo [(sizeof(DomainAuthInfo) <= 7888) ? 1 : -1]; - char sizecheck_ServiceInfoQuery [(sizeof(ServiceInfoQuery) <= 3488) ? 1 : -1]; #if APPLE_OSX_mDNSResponder - char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1208) ? 1 : -1]; + char sizecheck_ClientTunnel [(sizeof(ClientTunnel) <= 1230) ? 1 : -1]; #endif }; @@ -3599,10 +3615,57 @@ 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); +extern void D2D_start_advertising_record(AuthRecord *ar); +extern void D2D_stop_advertising_record(AuthRecord *ar); +#else +#define D2D_start_advertising_interface(X) +#define D2D_stop_advertising_interface(X) +#define D2D_start_advertising_record(X) +#define D2D_stop_advertising_record(X) #endif // *************************************************************************** +#ifdef __rtems__ +typedef struct +{ + // Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService() + // When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network. + domainname name; + mDNSInterfaceID InterfaceID; // ID of the interface the response was received on + mDNSAddr ip; // Remote (destination) IP address where this service can be accessed + mDNSIPPort port; // Port where this service can be accessed + mDNSu16 TXTlen; + mDNSu8 TXTinfo[2048]; // Additional demultiplexing information (e.g. LPR queue name) +} ServiceInfo; + +// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute() +typedef struct ServiceInfoQuery_struct ServiceInfoQuery; +typedef void mDNSServiceInfoQueryCallback (mDNS *const m, ServiceInfoQuery *query); +struct ServiceInfoQuery_struct +{ + // Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them. + // No fields need to be set up by the client prior to calling mDNS_StartResolveService(); + // all required data is passed as parameters to that function. + // The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information + // and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may + // dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure. + DNSQuestion qSRV; + DNSQuestion qTXT; + DNSQuestion qAv4; + DNSQuestion qAv6; + mDNSu8 GotSRV; + mDNSu8 GotTXT; + mDNSu8 GotADD; + mDNSu32 Answers; + ServiceInfo *info; + mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback; + void *ServiceInfoQueryContext; +}; + +extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context); +extern void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query); +#endif /* __rtems__ */ #ifdef __cplusplus } #endif diff --git a/mDNSResponder/mDNSCore/nsec3.c b/mDNSResponder/mDNSCore/nsec3.c index c16a42d5..4e9e8c82 100644 --- a/mDNSResponder/mDNSCore/nsec3.c +++ b/mDNSResponder/mDNSCore/nsec3.c @@ -238,7 +238,7 @@ mDNSlocal mDNSBool NSEC3Find(mDNS *const m, NSEC3FindValues val, CacheRecord *nc name = SkipLeadingLabels(origName, i); if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen)) { - LogMsg("NSEC3Find: NSEC3HashName failed for ##s", name->c); + LogMsg("NSEC3Find: NSEC3HashName failed for %##s", name->c); continue; } @@ -708,7 +708,7 @@ mDNSexport CacheRecord *NSEC3RecordIsDelegation(mDNS *const m, domainname *name, if (!NSEC3HashName(name, nsec3, mDNSNULL, 0, hashName, &hlen)) { - LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for ##s", name->c); + LogMsg("NSEC3RecordIsDelegation: NSEC3HashName failed for %##s", name->c); return mDNSNULL; } diff --git a/mDNSResponder/mDNSCore/uDNS.c b/mDNSResponder/mDNSCore/uDNS.c index 3ba88b5c..694c745c 100755 --- a/mDNSResponder/mDNSCore/uDNS.c +++ b/mDNSResponder/mDNSCore/uDNS.c @@ -124,7 +124,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons return mDNSNULL; } - if (!d) + if (!d) d = (const domainname *)""; 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", @@ -135,11 +135,11 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons 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)->serviceID == serviceID && (*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 && + 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)) + 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; @@ -192,8 +192,6 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (*p)->addr = *addr; (*p)->port = port; (*p)->flags = DNSServer_FlagNew; - (*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed; - (*p)->lasttest = m->timenow - INIT_UCAST_POLL_INTERVAL; (*p)->timeout = timeout; (*p)->cellIntf = cellIntf; (*p)->req_A = reqA; @@ -208,11 +206,13 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (*p)->next = mDNSNULL; } } - (*p)->penaltyTime = 0; - // We always update the ID (not just when we allocate a new instance) because we could - // be adding a new non-scoped resolver with a new ID and we want all the non-scoped - // resolvers belong to the same group. - (*p)->resGroupID = resGroupID; + if (*p) { + (*p)->penaltyTime = 0; + // We always update the ID (not just when we allocate a new instance) because we could + // be adding a new non-scoped resolver with a new ID and we want all the non-scoped + // resolvers belong to the same group. + (*p)->resGroupID = resGroupID; + } return(*p); } @@ -224,7 +224,7 @@ mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 re DNSServer *new; DNSServer *orig = q->qDNSServer; mDNSu8 rcode = '\0'; - + mDNS_CheckLock(m); LogInfo("PenalizeDNSServer: Penalizing DNS server %#a question for question %p %##s (%s) SuppressUnusable %d", @@ -234,11 +234,11 @@ mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 re // return the error, then return the first error. if (mDNSOpaque16IsZero(q->responseFlags)) q->responseFlags = responseFlags; - + rcode = (mDNSu8)(responseFlags.b[1] & kDNSFlag1_RC_Mask); // After we reset the qDNSServer to NULL, we could get more SERV_FAILS that might end up - // peanlizing again. + // penalizing again. if (!q->qDNSServer) goto end; @@ -482,7 +482,7 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) { mStatus err = mStatus_NoError; - + if (!m->NATTraversals) { m->retryGetAddr = NonZeroTime(m->timenow + 0x78000000); @@ -497,7 +497,7 @@ mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) 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)) { @@ -509,7 +509,7 @@ mDNSlocal mStatus uDNS_RequestAddress(mDNS *m) 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_ @@ -554,7 +554,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP 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)) { @@ -567,7 +567,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP 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; @@ -577,7 +577,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP 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); } @@ -604,31 +604,31 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP 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); - + 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); @@ -654,7 +654,7 @@ mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info, mDNSBool useP 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_ @@ -727,7 +727,7 @@ mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv 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: @@ -924,7 +924,7 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra { traversal->NATLease = 0; traversal->retryInterval = 0; - + // 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 @@ -940,7 +940,7 @@ mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *tra // would have requested an IPv4 address. traversal->RequestedPort = zeroIPPort; traversal->NewAddress = zerov4Addr; - + uDNS_SendNATMsg(m, traversal, traversal->lastSuccessfulProtocol != NATTProtocolNATPMP); } @@ -1573,6 +1573,7 @@ mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, con } if (!info->sock) { LogMsg("MakeTCPConn: unable to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); } + mDNSPlatformSetSocktOpt(info->sock, mDNSTransport_TCP, Addr->type, question); err = mDNSPlatformTCPConnect(info->sock, Addr, Port, hostname, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info); // Probably suboptimal here. @@ -1703,6 +1704,7 @@ mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) } } + // forward declaration so GetServiceTarget can do reverse lookup if needed mDNSlocal void GetStaticHostname(mDNS *m); @@ -1754,11 +1756,13 @@ mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE = (const domainname*)"\x mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x0F_dns-update-tls" "\x04_tcp"; mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE = (const domainname*)"\x0E_dns-query-tls" "\x04_tcp"; mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE = (const domainname*)"\x0C_dns-llq-tls" "\x04_tcp"; +mDNSlocal const domainname *DNS_PUSH_NOTIFICATION_SERVICE_TYPE = (const domainname*)"\x0C_dns-push-tls" "\x04_tcp"; #define ZoneDataSRV(X) ( \ - (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ - (X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \ - (X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : (const domainname*)"") + (X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \ + (X)->ZoneService == ZoneServiceQuery ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE : (const domainname*)"" ) : \ + (X)->ZoneService == ZoneServiceLLQ ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE : PUBLIC_LLQ_SERVICE_TYPE ) : \ + (X)->ZoneService == ZoneServiceDNSPush ? DNS_PUSH_NOTIFICATION_SERVICE_TYPE : (const domainname*)"") // Forward reference: GetZoneData_StartQuery references GetZoneData_QuestionCallback, and // GetZoneData_QuestionCallback calls GetZoneData_StartQuery @@ -1886,7 +1890,7 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.InterfaceID = mDNSInterface_Any; zd->question.flags = 0; zd->question.Target = zeroAddr; - //zd->question.qname.c[0] = 0; // Already set + //zd->question.qname.c[0] = 0; // Already set zd->question.qtype = qtype; zd->question.qclass = kDNSClass_IN; zd->question.LongLived = mDNSfalse; @@ -1894,8 +1898,6 @@ mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qt zd->question.ForceMCast = mDNSfalse; zd->question.ReturnIntermed = mDNStrue; zd->question.SuppressUnusable = mDNSfalse; - zd->question.DenyOnCellInterface = mDNSfalse; - zd->question.DenyOnExpInterface = mDNSfalse; zd->question.SearchListIndex = 0; zd->question.AppendSearchDomains = 0; zd->question.RetryWithSearchDomains = mDNSfalse; @@ -2171,7 +2173,7 @@ mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr) else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; } //LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s", - // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); + // rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr)); if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo); rr->NATinfo.Protocol = protocol; @@ -2584,8 +2586,6 @@ mDNSlocal void GetStaticHostname(mDNS *m) q->ForceMCast = mDNSfalse; q->ReturnIntermed = mDNStrue; q->SuppressUnusable = mDNSfalse; - q->DenyOnCellInterface = mDNSfalse; - q->DenyOnExpInterface = mDNSfalse; q->SearchListIndex = 0; q->AppendSearchDomains = 0; q->RetryWithSearchDomains = mDNSfalse; @@ -2645,12 +2645,30 @@ mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) // below could free the memory, and we have to make sure we don't touch hi fields after that. mDNSBool f4 = hi->arv4.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv4.state != regState_Unregistered; mDNSBool f6 = hi->arv6.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv6.state != regState_Unregistered; - if (f4) LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn); - if (f6) LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn); *ptr = (*ptr)->next; // unlink - if (f4) mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal); - 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 (f4 || f6) + { + if (f4) + { + LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn); + mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal); + } + if (f6) + { + LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn); + mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal); + } + // When both deregistrations complete we'll free the memory in the mStatus_MemFree callback + } + else + { + if (hi->natinfo.clientContext) + { + mDNS_StopNATOperation_internal(m, &hi->natinfo); + hi->natinfo.clientContext = mDNSNULL; + } + mDNSPlatformMemFree(hi); + } } mDNS_CheckLock(m); m->NextSRVUpdate = NonZeroTime(m->timenow); @@ -3621,13 +3639,13 @@ mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interface // 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; + NATTraversalInfo *n; LogInfo("NAT-PMP version unsupported message received"); for (n = m->NATTraversals; n; n=n->next) { @@ -3635,7 +3653,7 @@ mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interface // and update the state variables uDNS_SendNATMsg(m, n, mDNSfalse); } - + m->NextScheduledNATOp = m->timenow; return; @@ -3649,7 +3667,7 @@ mDNSlocal void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID Interface 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]); @@ -3726,16 +3744,16 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, 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); @@ -3774,11 +3792,11 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, 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)) @@ -3786,7 +3804,7 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, 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]) { @@ -3825,7 +3843,7 @@ mDNSlocal void uDNS_ReceivePCPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, { 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)) @@ -3858,135 +3876,6 @@ mDNSexport void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID LogMsg("uDNS_ReceiveNATPacket: packet with version %u (expected %u or %u)", pkt[0], PCP_VERS, NATMAP_VERS); } -// <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs -// <rdar://problem/4288449> Add check to avoid crashing NAT gateways that have buggy DNS relay code -// -// We know of bugs in home NAT gateways that cause them to crash if they receive certain DNS queries. -// The DNS queries that make them crash are perfectly legal DNS queries, but even if they weren't, -// the gateway shouldn't crash -- in today's world of viruses and network attacks, software has to -// be written assuming that a malicious attacker could send them any packet, properly-formed or not. -// Still, we don't want to be crashing people's home gateways, so we go out of our way to avoid -// the queries that crash them. -// -// Some examples: -// -// 1. Any query where the name ends in ".in-addr.arpa." and the text before this is 32 or more bytes. -// The query type does not need to be PTR -- the gateway will crash for any query type. -// e.g. "ping long-name-crashes-the-buggy-router.in-addr.arpa" will crash one of these. -// -// 2. Any query that results in a large response with the TC bit set. -// -// 3. Any PTR query that doesn't begin with four decimal numbers. -// These gateways appear to assume that the only possible PTR query is a reverse-mapping query -// (e.g. "1.0.168.192.in-addr.arpa") and if they ever get a PTR query where the first four -// labels are not all decimal numbers in the range 0-255, they handle that by crashing. -// These gateways also ignore the remainder of the name following the four decimal numbers -// -- whether or not it actually says in-addr.arpa, they just make up an answer anyway. -// -// The challenge therefore is to craft a query that will discern whether the DNS server -// is one of these buggy ones, without crashing it. Furthermore we don't want our test -// queries making it all the way to the root name servers, putting extra load on those -// name servers and giving Apple a bad reputation. To this end we send this query: -// dig -t ptr 1.0.0.127.dnsbugtest.1.0.0.127.in-addr.arpa. -// -// The text preceding the ".in-addr.arpa." is under 32 bytes, so it won't cause crash (1). -// It will not yield a large response with the TC bit set, so it won't cause crash (2). -// It starts with four decimal numbers, so it won't cause crash (3). -// The name falls within the "1.0.0.127.in-addr.arpa." domain, the reverse-mapping name for the local -// loopback address, and therefore the query will black-hole at the first properly-configured DNS server -// it reaches, making it highly unlikely that this query will make it all the way to the root. -// -// Finally, the correct response to this query is NXDOMAIN or a similar error, but the -// gateways that ignore the remainder of the name following the four decimal numbers -// give themselves away by actually returning a result for this nonsense query. - -mDNSlocal const domainname *DNSRelayTestQuestion = (const domainname*) - "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest" - "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa"; - -// See comments above for DNSRelayTestQuestion -// If this is the kind of query that has the risk of crashing buggy DNS servers, we do a test question first -mDNSlocal mDNSBool NoTestQuery(DNSQuestion *q) -{ - int i; - mDNSu8 *p = q->qname.c; - if (q->AuthInfo) return(mDNStrue); // Don't need a test query for private queries sent directly to authoritative server over TLS/TCP - if (q->qtype != kDNSType_PTR) return(mDNStrue); // Don't need a test query for any non-PTR queries - for (i=0; i<4; i++) // If qname does not begin with num.num.num.num, can't skip the test query - { - if (p[0] < 1 || p[0] > 3) return(mDNSfalse); - if ( p[1] < '0' || p[1] > '9' ) return(mDNSfalse); - if (p[0] >= 2 && (p[2] < '0' || p[2] > '9')) return(mDNSfalse); - if (p[0] >= 3 && (p[3] < '0' || p[3] > '9')) return(mDNSfalse); - p += 1 + p[0]; - } - // If remainder of qname is ".in-addr.arpa.", this is a vanilla reverse-mapping query and - // we can safely do it without needing a test query first, otherwise we need the test query. - return(SameDomainName((domainname*)p, (const domainname*)"\x7" "in-addr" "\x4" "arpa")); -} - -// Returns mDNStrue if response was handled -mDNSlocal mDNSBool uDNS_ReceiveTestQuestionResponse(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, - const mDNSAddr *const srcaddr, const mDNSIPPort srcport) -{ - const mDNSu8 *ptr = msg->data; - DNSQuestion pktq; - DNSServer *s; - mDNSu32 result = 0; - - // 1. Find out if this is an answer to one of our test questions - if (msg->h.numQuestions != 1) return(mDNSfalse); - ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &pktq); - if (!ptr) return(mDNSfalse); - if (pktq.qtype != kDNSType_PTR || pktq.qclass != kDNSClass_IN) return(mDNSfalse); - if (!SameDomainName(&pktq.qname, DNSRelayTestQuestion)) return(mDNSfalse); - - // 2. If the DNS relay gave us a positive response, then it's got buggy firmware - // else, if the DNS relay gave us an error or no-answer response, it passed our test - if ((msg->h.flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0) - result = DNSServer_Failed; - else - result = DNSServer_Passed; - - // 3. Find occurrences of this server in our list, and mark them appropriately - for (s = m->DNSServers; s; s = s->next) - { - mDNSBool matchaddr = (s->teststate != result && mDNSSameAddress(srcaddr, &s->addr) && mDNSSameIPPort(srcport, s->port)); - mDNSBool matchid = (s->teststate == DNSServer_Untested && mDNSSameOpaque16(msg->h.id, s->testid)); - if (matchaddr || matchid) - { - DNSQuestion *q; - s->teststate = result; - if (result == DNSServer_Passed) - { - LogInfo("DNS Server %#a:%d (%#a:%d) %d passed%s", - &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), - matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); - } - else - { - LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a:%d (%#a:%d) %d%s", - &s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid), - matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent"); - } - - // If this server has just changed state from DNSServer_Untested to DNSServer_Passed, then retrigger any waiting questions. - // We use the NoTestQuery() test so that we only retrigger questions that were actually blocked waiting for this test to complete. - if (result == DNSServer_Passed) // Unblock any questions that were waiting for this result - for (q = m->Questions; q; q=q->next) - if (q->qDNSServer == s && !NoTestQuery(q)) - { - q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; - q->unansweredQueries = 0; - q->LastQTime = m->timenow - q->ThisQInterval; - m->NextScheduledQuery = m->timenow; - } - } - } - - return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doesn't need to process this packet further -} - // Called from mDNSCoreReceive with the lock held mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport) { @@ -4015,7 +3904,6 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS if (QR_OP == StdR) { //if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return; - if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return; for (qptr = m->Questions; qptr; qptr = qptr->next) if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW) { @@ -4037,7 +3925,7 @@ mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNS mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond; mDNSu32 random = mDNSRandom((mDNSs32)lease * mDNSPlatformOneSecond/10); - //rcode = kDNSFlag1_RC_ServFail; // Simulate server failure (rcode 2) + //rcode = kDNSFlag1_RC_ServFail; // Simulate server failure (rcode 2) // Walk through all the records that matches the messageID. There could be multiple // records if we had sent them in a group @@ -4200,6 +4088,40 @@ mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneI mDNS_Unlock(m); } +mDNSexport void DNSPushNotificationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) +{ + DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext; + mDNS_Lock(m); + + // If we get here it means that the GetZoneData operation has completed. + // We hold on to the zone data if it is AutoTunnel as we use the hostname + // in zoneInfo during the TLS connection setup. + q->servAddr = zeroAddr; + q->servPort = zeroIPPort; + if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0]) + { + q->dnsPushState = DNSPUSH_SERVERFOUND; + q->dnsPushServerAddr = zoneInfo->Addr; + q->dnsPushServerPort = zoneInfo->Port; + q->ntries = 0; + LogInfo("DNSPushNotificationGotZoneData %#a:%d", &q->dnsPushServerAddr, mDNSVal16(q->dnsPushServerPort)); + SubscribeToDNSPushNotificationServer(m,q); + } + else + { + StartLLQPolling(m,q); + if (err == mStatus_NoSuchNameErr) + { + // this actually failed, so mark it by setting address to all ones + q->servAddr.type = mDNSAddrType_IPv4; + q->servAddr.ip.v4 = onesIPv4Addr; + q->dnsPushState = DNSPUSH_NOSERVER; + } + } + mDNS_Unlock(m); +} + + // Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1) mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) { @@ -4467,7 +4389,7 @@ mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr) if (!rr->nta) { LogMsg("SendRecordDeregistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; } err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name), mDNSfalse); if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %d", err); - //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this + //if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this } SetRecordRetry(m, rr, 0); return; @@ -4690,17 +4612,35 @@ mDNSlocal void handle_unanswered_query(mDNS *const m) if (!q->qDNSServer->req_DO) { - q->ValidationState = DNSSECValNotRequired; + 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", + LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a", q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr); } } } +mDNSlocal void uDNS_HandleLLQState(mDNS *const m, DNSQuestion *q) +{ + // First attempt to use DNS Push Notification. + if (q->dnsPushState == DNSPUSH_INIT) + DiscoverDNSPushNotificationServer(m, q); + switch (q->state) + { + case LLQ_InitialRequest: startLLQHandshake(m, q); break; + case LLQ_SecondaryRequest: + // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step + if (PrivateQuery(q)) startLLQHandshake(m, q); + else sendChallengeResponse(m, q, mDNSNULL); + break; + case LLQ_Established: sendLLQRefresh(m, q); break; + case LLQ_Poll: break; // Do nothing (handled below) + } +} + // 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) @@ -4710,22 +4650,10 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) if (q->LongLived) { - switch (q->state) - { - case LLQ_InitialRequest: startLLQHandshake(m, q); break; - case LLQ_SecondaryRequest: - // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step - if (PrivateQuery(q)) - startLLQHandshake(m, q); - else - sendChallengeResponse(m, q, mDNSNULL); - break; - case LLQ_Established: sendLLQRefresh(m, q); break; - case LLQ_Poll: break; // Do nothing (handled below) - } + uDNS_HandleLLQState(m,q); } - handle_unanswered_query(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)) { @@ -4773,36 +4701,25 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) for (qptr = q->next ; qptr; qptr = qptr->next) if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; } } - if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled) + if (q->qDNSServer) { - mDNSu8 *end = m->omsg.data; + mDNSu8 *end; mStatus err = mStatus_NoError; mDNSBool private = mDNSfalse; InitializeDNSMessage(&m->omsg.h, q->TargetQID, (DNSSECQuestion(q) ? DNSSecQFlags : uQueryFlags)); - if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q)) - { - end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); - if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) - { - 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 + end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (DNSSECQuestion(q) && !q->qDNSServer->cellIntf) { - LogInfo("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port)); - q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep; - q->qDNSServer->lasttest = m->timenow; - end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN); - q->qDNSServer->testid = m->omsg.h.id; + 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); - if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q))) + if (end > m->omsg.data) { //LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype)); if (private) @@ -4820,7 +4737,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort); if (q->LocalSocket) - mDNSPlatformSetuDNSSocktOpt(q->LocalSocket, &q->qDNSServer->addr, q); + mDNSPlatformSetSocktOpt(q->LocalSocket, mDNSTransport_UDP, q->qDNSServer->addr.type, q); } if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time else @@ -4829,6 +4746,11 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) #if TARGET_OS_EMBEDDED if (!err) { + if (q->metrics.answered) + { + q->metrics.querySendCount = 0; + q->metrics.answered = mDNSfalse; + } if (q->metrics.querySendCount++ == 0) { q->metrics.firstQueryTime = m->timenow; @@ -4839,32 +4761,68 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) } } - if (err != mStatus_TransientErr) // if it is not a transient error backoff and DO NOT flood queries unnecessarily + if (err == mStatus_HostUnreachErr) { - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; // Only increase interval if send succeeded - q->unansweredQueries++; - if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) - q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; - if (private && q->state != LLQ_Poll) + DNSServer *newServer; + + LogInfo("uDNS_CheckCurrentQuestion: host unreachable error for DNS server %#a for question [%p] %##s (%s)", + &q->qDNSServer->addr, q, q->qname.c, DNSTypeName(q->qtype)); + + if (!StrictUnicastOrdering) { - // We don't want to retransmit too soon. Hence, we always schedule our first - // retransmisson at 3 seconds rather than one second - if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) - q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; - if (q->ThisQInterval > LLQ_POLL_INTERVAL) - q->ThisQInterval = LLQ_POLL_INTERVAL; - LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME); } - if (q->qDNSServer->cellIntf) + + newServer = GetServerForQuestion(m, q); + DNSServerChangeForQuestion(m, q, newServer); + + if (q->triedAllServersOnce) + { + q->LastQTime = m->timenow; + } + else { - // We don't want to retransmit too soon. Schedule our first retransmisson at - // MIN_UCAST_RETRANS_TIMEOUT seconds. - if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) - q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; + q->ThisQInterval = InitialQuestionInterval; + q->LastQTime = m->timenow - q->ThisQInterval; } - debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); + q->unansweredQueries = 0; + q->noServerResponse = 1; + } + else + { + if (err != mStatus_TransientErr) // if it is not a transient error backoff and DO NOT flood queries unnecessarily + { + // If all DNS Servers are not responding, then we back-off using the multiplier UDNSBackOffMultiplier(*2). + // Only increase interval if send succeeded + + q->ThisQInterval = q->ThisQInterval * UDNSBackOffMultiplier; + if ((q->ThisQInterval > 0) && (q->ThisQInterval < MinQuestionInterval)) // We do not want to retx within 1 sec + q->ThisQInterval = MinQuestionInterval; + + q->unansweredQueries++; + if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL) + q->ThisQInterval = MAX_UCAST_POLL_INTERVAL; + if (private && q->state != LLQ_Poll) + { + // We don't want to retransmit too soon. Hence, we always schedule our first + // retransmisson at 3 seconds rather than one second + if (q->ThisQInterval < (3 * mDNSPlatformOneSecond)) + q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; + if (q->ThisQInterval > LLQ_POLL_INTERVAL) + q->ThisQInterval = LLQ_POLL_INTERVAL; + LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval); + } + if (q->qDNSServer->cellIntf) + { + // We don't want to retransmit too soon. Schedule our first retransmisson at + // MIN_UCAST_RETRANS_TIMEOUT seconds. + if (q->ThisQInterval < MIN_UCAST_RETRANS_TIMEOUT) + q->ThisQInterval = MIN_UCAST_RETRANS_TIMEOUT; + } + debugf("uDNS_CheckCurrentQuestion: Increased ThisQInterval to %d for %##s (%s), cell %d", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer->cellIntf); + } + q->LastQTime = m->timenow; } - q->LastQTime = m->timenow; SetNextQueryTime(m, q); } else @@ -4927,7 +4885,7 @@ mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m) { if (SameNameRecordAnswersQuestion(&rr->resrec, q)) { - LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); + LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr)); mDNS_PurgeCacheResourceRecord(m, rr); } } @@ -5049,7 +5007,7 @@ mDNSexport void CheckNATMappings(mDNS *m) const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&EffectiveAddress) ? mStatus_DoubleNAT : mStatus_NoError; const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort : !mDNSIPv4AddressIsZero(EffectiveAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort; - + if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8) { if (!mDNSSameIPv4Address(cur->ExternalAddress, EffectiveAddress) || @@ -5337,8 +5295,8 @@ mDNSexport void udns_validatelists(void *const v) DNSServer *d; for (d = m->DNSServers; d; d=d->next) - if (d->next == (DNSServer *)~0 || d->teststate > DNSServer_Disabled) - LogMemCorruption("m->DNSServers: %p is garbage (%d)", d, d->teststate); + if (d->next == (DNSServer *)~0) + LogMemCorruption("m->DNSServers: %p is garbage", d); DomainAuthInfo *info; for (info = m->AuthInfoList; info; info = info->next) @@ -5810,101 +5768,327 @@ struct CompileTimeAssertionChecks_uDNS char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1]; }; +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - DNS Push Notification functions +#endif + +mDNSlocal tcpInfo_t * GetTCPConnectionToPushServer(mDNS *m, DNSQuestion *q) +{ + // If we already have a question for this zone and if the server is the same, reuse it + DNSPushNotificationZone *zone = mDNSNULL; + for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next) + { + if (SameDomainName(&q->nta->ChildName, &zone->zoneName)) + { + DNSPushNotificationServer *zoneServer = mDNSNULL; + for (zoneServer = zone->servers; zoneServer != mDNSNULL; zoneServer = zoneServer->next) + { + if (mDNSSameAddress(&q->dnsPushServerAddr, &zoneServer->serverAddr)) + { + zone->numberOfQuestions++; + zoneServer->numberOfQuestions++; + return zoneServer->connection; + } + } + } + } + + // If we have a connection to this server but it is for a differnt zone, create a new zone entry and reuse the connection + DNSPushNotificationServer *server = mDNSNULL; + for (server = m->DNSPushServers; server != mDNSNULL; server = server->next) + { + if (mDNSSameAddress(&q->dnsPushServerAddr, &server->serverAddr)) + { + DNSPushNotificationZone *newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone)); + newZone->numberOfQuestions = 1; + newZone->zoneName = q->nta->ChildName; + newZone->servers = server; + + // Add the new zone to the begining of the list + newZone->next = m->DNSPushZones; + m->DNSPushZones = newZone; + + server->numberOfQuestions++; + return server->connection; + } + } + + // If we do not have any existing connections, create a new connection + DNSPushNotificationServer *newServer = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationServer)); + DNSPushNotificationZone *newZone = mDNSPlatformMemAllocate(sizeof(DNSPushNotificationZone)); + + newServer->numberOfQuestions = 1; + newServer->serverAddr = q->dnsPushServerAddr; + newServer->connection = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->dnsPushServerAddr, q->dnsPushServerPort, &q->nta->Host, q, mDNSNULL); + + newZone->numberOfQuestions = 1; + newZone->zoneName = q->nta->ChildName; + newZone->servers = newServer; + + // Add the new zone to the begining of the list + newZone->next = m->DNSPushZones; + m->DNSPushZones = newZone; + + newServer->next = m->DNSPushServers; + m->DNSPushServers = newServer; + return newServer->connection; +} + +mDNSexport void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + /* Use the same NAT setup as in the LLQ case */ + 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 + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + return; + } + + // 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", + q->qname.c, DNSTypeName(q->qtype), mDNSVal16(m->LLQNAT.ExternalPort), m->LLQNAT.Result); + StartLLQPolling(m, q); // Actually sets up the NAT Auto Tunnel + return; + } + + if (mDNSIPPortIsZero(q->dnsPushServerPort) && q->dnsPushState == DNSPUSH_INIT) + { + LogInfo("SubscribeToDNSPushNotificationServer: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + q->dnsPushServerAddr = zeroAddr; + // We know q->dnsPushServerPort is zero because of check above + if (q->nta) CancelGetZoneData(m, q->nta); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceDNSPush, DNSPushNotificationGotZoneData, q); + return; + } + + if (q->tcp) + { + LogInfo("SubscribeToDNSPushNotificationServer: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + DisposeTCPConn(q->tcp); + q->tcp = mDNSNULL; + } + + if (!q->nta) + { + // Normally we lookup the zone data and then call this function. And we never free the zone data + // for "PrivateQuery". But sometimes this can happen due to some race conditions. When we + // switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT. + // When we poll, we free the zone information as we send the query to the server (See + // PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we + // are still behind Double NAT, we would have returned early in this function. But we could + // have switched to a network with no NATs and we should get the zone data again. + LogInfo("SubscribeToDNSPushNotificationServer: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); + q->nta = StartGetZoneData(m, &q->qname, ZoneServiceDNSPush, DNSPushNotificationGotZoneData, q); + return; + } + else if (!q->nta->Host.c[0]) + { + // This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname + LogMsg("SubscribeToDNSPushNotificationServer: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived); + } + q->tcp = GetTCPConnectionToPushServer(m,q); + // If TCP failed (transient networking glitch) try again in five seconds + q->ThisQInterval = (q->tcp != mDNSNULL) ? q->ThisQInterval = 0 : (mDNSPlatformOneSecond * 5); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); +} + + +mDNSexport void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + mDNSu8 *end = mDNSNULL; + InitializeDNSMessage(&m->omsg.h, zeroID, SubscribeFlags); + end = putQuestion(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (!end) + { + LogMsg("ERROR: SubscribeToDNSPushNotificationServer putQuestion failed"); + return; + } + + mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->dnsPushServerAddr, q->dnsPushServerPort, q->tcp->sock, mDNSNULL, mDNSfalse); + + // update question state + q->dnsPushState = DNSPUSH_ESTABLISHED; + q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond); + q->LastQTime = m->timenow; + SetNextQueryTime(m, q); + +} + +mDNSlocal void reconcileDNSPushConnection(mDNS *m, DNSQuestion *q) +{ + DNSPushNotificationZone *zone; + DNSPushNotificationServer *server; + // Update the counts + for (zone = m->DNSPushZones; zone != mDNSNULL; zone = zone->next) + { + if (SameDomainName(&zone->zoneName, &q->nta->ChildName)) + { + zone->numberOfQuestions--; + for (server = zone->servers; server != mDNSNULL; server = server->next) + { + if (mDNSSameAddress(&server->serverAddr, &q->dnsPushServerAddr)) + server->numberOfQuestions--; + } + } + } + + // Now prune the lists + server = m->DNSPushServers; + DNSPushNotificationServer *nextServer = mDNSNULL; + while(server != mDNSNULL) + { + nextServer = server->next; + if (server->numberOfQuestions <= 0) + { + DisposeTCPConn(server->connection); + if (server == m->DNSPushServers) + m->DNSPushServers = nextServer; + mDNSPlatformMemFree(server); + server = nextServer; + } + else server = server->next; + } + + zone = m->DNSPushZones; + DNSPushNotificationZone *nextZone = mDNSNULL; + while(zone != mDNSNULL) + { + nextZone = zone->next; + if (zone->numberOfQuestions <= 0) + { + if (zone == m->DNSPushZones) + m->DNSPushZones = nextZone; + mDNSPlatformMemFree(zone); + zone = nextZone; + } + else zone = zone->next; + } + +} + +mDNSexport void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + mDNSu8 *end = mDNSNULL; + InitializeDNSMessage(&m->omsg.h, q->TargetQID, UnSubscribeFlags); + end = putQuestion(&m->omsg, end, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass); + if (!end) + { + LogMsg("ERROR: UnSubscribeToDNSPushNotificationServer - putQuestion failed"); + return; + } + + mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->dnsPushServerAddr, q->dnsPushServerPort, q->tcp->sock, mDNSNULL, mDNSfalse); + + reconcileDNSPushConnection(m, q); +} + +#if COMPILER_LIKES_PRAGMA_MARK +#pragma mark - +#endif #else // !UNICAST_DISABLED mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr) { - (void) m; - (void) rr; + (void) m; + (void) rr; - return mDNSNULL; + return mDNSNULL; } mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name) { - (void) m; - (void) name; + (void) m; + (void) name; - return mDNSNULL; + return mDNSNULL; } mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) { - (void) m; - (void) q; + (void) m; + (void) q; - return mDNSNULL; + return mDNSNULL; } mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q) { - (void) m; - (void) q; + (void) m; + (void) q; } mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp) { - (void) tcp; + (void) tcp; } mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) { - (void) m; - (void) traversal; + (void) m; + (void) traversal; - return mStatus_UnsupportedErr; + return mStatus_UnsupportedErr; } mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal) { - (void) m; - (void) traversal; + (void) m; + (void) traversal; - return mStatus_UnsupportedErr; + return mStatus_UnsupportedErr; } mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q) { - (void) m; - (void) 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; + (void) m; + (void) name; + (void) target; + (void) callback; + (void) ZoneDataContext; - return mDNSNULL; + return mDNSNULL; } mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData) { - (void) m; - (void) err; - (void) 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; + (void) m; + (void) msg; + (void) end; + (void) srcaddr; + (void) srcport; + (void) matchQuestion; - return uDNS_LLQ_Not; + return uDNS_LLQ_Not; } mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags) { - (void) m; - (void) q; - (void) responseFlags; + (void) m; + (void) q; + (void) responseFlags; } mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) @@ -5928,7 +6112,7 @@ mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const (void) hostname; (void) port; (void) autoTunnel; - + return mStatus_UnsupportedErr; } @@ -5938,7 +6122,7 @@ mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID (void) InterfaceID; (void) searchIndex; (void) ignoreDotLocal; - + return mDNSNULL; } @@ -5946,7 +6130,7 @@ mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const n { (void) m; (void) name; - + return mDNSNULL; } @@ -5954,7 +6138,7 @@ mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *trave { (void) m; (void) traversal; - + return mStatus_UnsupportedErr; } @@ -5962,7 +6146,7 @@ mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traver { (void) m; (void) traversal; - + return mStatus_UnsupportedErr; } @@ -5983,7 +6167,7 @@ mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, cons (void) reqA; (void) reqAAAA; (void) reqDO; - + return mDNSNULL; } @@ -6034,8 +6218,27 @@ mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks) mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q) { (void)q; - + return mDNSfalse; } +mDNSexport void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + (void)m; + (void)q; +} + +mDNSexport void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + (void)m; + (void)q; +} + +mDNSexport void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q) +{ + (void)m; + (void)q; +} + #endif // !UNICAST_DISABLED + diff --git a/mDNSResponder/mDNSCore/uDNS.h b/mDNSResponder/mDNSCore/uDNS.h index eca8b701..910449f1 100755 --- a/mDNSResponder/mDNSCore/uDNS.h +++ b/mDNSResponder/mDNSCore/uDNS.h @@ -47,9 +47,8 @@ extern "C" { #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) +#define UDNSBackOffMultiplier 2 +#define MinQuestionInterval (1 * 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 @@ -82,6 +81,11 @@ extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo) extern void startLLQHandshake(mDNS *m, DNSQuestion *q); extern void sendLLQRefresh(mDNS *m, DNSQuestion *q); +extern void DNSPushNotificationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo); +extern void DiscoverDNSPushNotificationServer(mDNS *m, DNSQuestion *q); +extern void SubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); +extern void UnSubscribeToDNSPushNotificationServer(mDNS *m, DNSQuestion *q); + extern void SleepRecordRegistrations(mDNS *m); // uDNS_UpdateRecord @@ -144,6 +148,10 @@ extern void uDNS_ReceiveNATPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mD 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, NATTProtocol protocol); +// DNS Push Notification +extern void SubscribeToDNSPushNotification(mDNS *m, DNSQuestion *q); + + #ifdef __cplusplus } #endif |