- const DNSMessage *const response, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSAddr *dstaddr,
- const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
- {
- int i;
- const mDNSu8 *ptr = LocateAnswers(response, end); // We ignore questions (if any) in a DNS response packet
- CacheRecord *CacheFlushRecords = mDNSNULL;
- CacheRecord **cfp = &CacheFlushRecords;
-
- // 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
- // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
- int totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals;
-
- (void)srcaddr; // Currently used only for display in debugging message
-
- verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p TTL %d with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
- srcaddr, dstaddr, InterfaceID, ttl,
- response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,",
- response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,",
- response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,",
- response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
-
- // TTL should be 255
- // In the case of overlayed subnets that aren't using RFC 3442, some packets may incorrectly
- // go to the router first and then come back with a TTL of 254, so we allow that too.
- // Anything lower than 254 is a pretty good sign of an off-net spoofing attack.
- // Also, if we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us
- if (ttl < 254 || (!mDNSAddrIsDNSMulticast(dstaddr) && (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)mDNSPlatformOneSecond))
- {
- debugf("** Ignored apparent spoof mDNS Response from %#-15a to %#-15a TTL %d on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
- srcaddr, dstaddr, ttl, InterfaceID,
- response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,",
- response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,",
- response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,",
- response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
- return;
- }
-
- for (i = 0; i < totalrecords && ptr && ptr < end; i++)
- {
- LargeCacheRecord pkt;
- const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : kDNSRecordTypePacketAdd);
- ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &pkt);
- if (!ptr) break; // Break out of the loop and clean up our CacheFlushRecords list before exiting
-
- // 1. Check that this packet resource record does not conflict with any of ours
- if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
- m->CurrentRecord = m->ResourceRecords;
- while (m->CurrentRecord)
- {
- AuthRecord *rr = m->CurrentRecord;
- m->CurrentRecord = rr->next;
- if (PacketRRMatchesSignature(&pkt.r, rr)) // If interface, name, type (if verified) and class match...
- {
- // ... check to see if rdata is identical
- if (SameRData(&pkt.r.resrec, &rr->resrec))
- {
- // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
- if (pkt.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState)
- {
- // If we were planning to send on this -- and only this -- interface, then we don't need to any more
- if (rr->ImmedAnswer == InterfaceID) rr->ImmedAnswer = mDNSNULL;
- }
- else
- {
- if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; }
- else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
- }
- }
- else
- {
- // else, the packet RR has different rdata -- check to see if this is a conflict
- if (pkt.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &pkt.r))
- {
- debugf("mDNSCoreReceiveResponse: Our Record: %08X %08X %s", rr-> resrec.rdatahash, rr-> resrec.rdnamehash, GetRRDisplayString(m, rr));
- debugf("mDNSCoreReceiveResponse: Pkt Record: %08X %08X %s", pkt.r.resrec.rdatahash, pkt.r.resrec.rdnamehash, GetRRDisplayString(m, &pkt.r));
-
- // If this record is marked DependentOn another record for conflict detection purposes,
- // then *that* record has to be bumped back to probing state to resolve the conflict
- while (rr->DependentOn) rr = rr->DependentOn;
-
- // If we've just whacked this record's ProbeCount, don't need to do it again
- if (rr->ProbeCount <= DefaultProbeCountForTypeUnique)
- {
- // If we'd previously verified this record, put it back to probing state and try again
- if (rr->resrec.RecordType == kDNSRecordTypeVerified)
- {
- debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
- rr->resrec.RecordType = kDNSRecordTypeUnique;
- rr->ProbeCount = DefaultProbeCountForTypeUnique + 1;
- rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique);
- InitializeLastAPTime(m, rr);
- RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate
- }
- // If we're probing for this record, we just failed
- else if (rr->resrec.RecordType == kDNSRecordTypeUnique)
- {
- debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
- 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 same machine giving
- // different answers for the reverse mapping record.)
- // This is simply a misconfiguration, and we don't try to recover from it.
- else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
- {
- debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
- rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
- mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
- }
- else
- debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
- rr->resrec.RecordType, rr->resrec.name.c, DNSTypeName(rr->resrec.rrtype));
- }
- }
- // Else, matching signature, different rdata, but not a considered a conflict.
- // If the packet record has the cache-flush bit set, then we check to see if we have to re-assert our record(s)
- // to rescue them (see note about "multi-homing and bridged networks" at the end of this function).
- else if ((pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2)
- { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
- }
- }
- }
-
- // 2. See if we want to add this packet resource record to our cache
- if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in
- {
- mDNSu32 slot = HashSlot(&pkt.r.resrec.name);
- CacheRecord *rr;
- // 2a. Check if this packet resource record is already in our cache
- for (rr = m->rrcache_hash[slot]; rr; rr=rr->next)
- {
- // If we found this exact resource record, refresh its TTL
- if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&pkt.r.resrec, &rr->resrec))
- {
- if (pkt.r.resrec.rdlength > InlineCacheRDSize)
- verbosedebugf("Found record size %5d interface %p already in cache: %s",
- pkt.r.resrec.rdlength, InterfaceID, GetRRDisplayString(m, &pkt.r));
- rr->TimeRcvd = m->timenow;
-
- if (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
- {
- // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
- if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList)
- { *cfp = rr; cfp = &rr->NextInCFList; }
-
- // If this packet record is marked unique, and our previous cached copy was not, then fix it
- if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
- {
- DNSQuestion *q;
- for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++;
- rr->resrec.RecordType = pkt.r.resrec.RecordType;
- }
- }
-
- if (pkt.r.resrec.rroriginalttl > 0)
- {
- rr->resrec.rroriginalttl = pkt.r.resrec.rroriginalttl;
- rr->UnansweredQueries = 0;
- rr->MPUnansweredQ = 0;
- rr->MPUnansweredKA = 0;
- rr->MPExpectingKA = mDNSfalse;
- }
- else
- {
- // If the packet TTL is zero, that means we're deleting this record.
- // To give other hosts on the network a chance to protest, we push the deletion
- // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
- // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
- // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
- rr->resrec.rroriginalttl = 1;
- rr->UnansweredQueries = MaxUnansweredQueries;
- }
- SetNextCacheCheckTime(m, rr);
- break;
- }
- }
-
- // If packet resource record not in our cache, add it now
- // (unless it is just a deletion of a record we never had, in which case we don't care)
- if (!rr && pkt.r.resrec.rroriginalttl > 0)
- {
- rr = GetFreeCacheRR(m, pkt.r.resrec.rdlength);
- if (!rr) debugf("No cache space to add record for %#s", pkt.r.resrec.name.c);
- else
- {
- RData *saveptr = rr->resrec.rdata; // Save the rr->resrec.rdata pointer
- *rr = pkt.r;
- rr->resrec.rdata = saveptr; // and then restore it after the structure assignment
- if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
- { *cfp = rr; cfp = &rr->NextInCFList; }
- // If this is an oversized record with external storage allocated, copy rdata to external storage
- if (pkt.r.resrec.rdlength > InlineCacheRDSize)
- mDNSPlatformMemCopy(pkt.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + pkt.r.resrec.rdlength);
- rr->next = mDNSNULL; // Clear 'next' pointer
- *(m->rrcache_tail[slot]) = rr; // Append this record to tail of cache slot list
- m->rrcache_tail[slot] = &(rr->next); // Advance tail pointer
- m->rrcache_used[slot]++;
- //debugf("Adding RR %##s to cache (%d)", pkt.r.name.c, m->rrcache_used);
- CacheRecordAdd(m, rr);
- // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us
- SetNextCacheCheckTime(m, rr);
- }
- }
- }
- }
-
- // If we've just received one or more records with their cache flush bits set,
- // then scan that cache slot to see if there are any old stale records we need to flush
- while (CacheFlushRecords)
- {
- CacheRecord *r1 = CacheFlushRecords, *r2;
- CacheFlushRecords = CacheFlushRecords->NextInCFList;
- r1->NextInCFList = mDNSNULL;
- for (r2 = m->rrcache_hash[HashSlot(&r1->resrec.name)]; r2; r2=r2->next)
- if (SameResourceRecordSignature(&r1->resrec, &r2->resrec) && m->timenow - r2->TimeRcvd > mDNSPlatformOneSecond)
- {
- verbosedebugf("Cache flush %p X %p %##s (%s)", r1, r2, r2->resrec.name.c, DNSTypeName(r2->resrec.rrtype));
- // We set stale records to expire in one second.
- // This gives the owner a chance to rescue it if necessary.
- // This is important in the case of multi-homing and bridged networks:
- // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
- // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
- // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
- // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
- // By delaying the deletion by one second, we give X a change to notice that this bridging has
- // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
- // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
- // final expiration queries for this record.
- r2->resrec.rroriginalttl = 1;
- r2->TimeRcvd = m->timenow;
- r2->UnansweredQueries = MaxUnansweredQueries;
- SetNextCacheCheckTime(m, r2);
- }
- }
- }
-
-mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
- const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
- const mDNSInterfaceID InterfaceID, mDNSu8 ttl)
- {
- const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
- const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
- const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
-
- // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
- mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
- msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
- msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
- msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
- msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
-
- if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
-
- // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
- // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
- if (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; }
-
- mDNS_Lock(m);
- if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
- else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, dstaddr, InterfaceID, ttl);
- else debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
-
- // Packet reception often causes a change to the task list:
- // 1. Inbound queries can cause us to need to send responses
- // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
- // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
- // 4. Response packets that answer questions may cause our client to issue new questions
- mDNS_Unlock(m);
- }
+ const DNSMessage *const response, const mDNSu8 *end,
+ const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+ const mDNSInterfaceID InterfaceID)
+{
+ int i;
+ mDNSBool myself;
+ mDNSBool ResponseMCast = dstaddr && mDNSAddrIsDNSMulticast(dstaddr);
+ mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, &myself);
+ DNSQuestion *llqMatch = mDNSNULL;
+ DNSQuestion *unicastQuestion = mDNSNULL;
+ uDNS_LLQType LLQType = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch);
+
+ // "(CacheRecord*)1" is a special (non-zero) end-of-list marker
+ // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList
+ // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling.
+ CacheRecord *CacheFlushRecords = (CacheRecord*)1;
+ CacheRecord **cfp = &CacheFlushRecords;
+ CacheRecord *NSECRecords = mDNSNULL;
+ CacheRecord *NSECCachePtr = mDNSNULL;
+ CacheRecord **nsecp = &NSECRecords;
+ CacheRecord *McastNSEC3Records = mDNSNULL;
+ mDNSBool nseclist;
+ mDNSu8 rcode = '\0';
+ mDNSBool rrsigsCreated = mDNSfalse;
+ mDNSBool DNSSECQuestion = mDNSfalse;
+ mDNSBool recordAccepted = mDNSfalse;
+ NetworkInterfaceInfo *llintf = FirstIPv4LLInterfaceForID(m, InterfaceID);
+
+ // All records in a DNS response packet are treated as equally valid statements of truth. If we want
+ // to guard against spoof responses, then the only credible protection against that is cryptographic
+ // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
+ int firstauthority = response->h.numAnswers;
+ int firstadditional = firstauthority + response->h.numAuthorities;
+ int totalrecords = firstadditional + response->h.numAdditionals;
+ const mDNSu8 *ptr = response->data;
+ DNSServer *uDNSServer = mDNSNULL;
+
+ debugf("Received Response from %#-15a addressed to %#-15a on %p with "
+ "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes LLQType %d",
+ srcaddr, dstaddr, InterfaceID,
+ response->h.numQuestions, response->h.numQuestions == 1 ? ", " : "s,",
+ response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,",
+ response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,",
+ response->h.numAdditionals, response->h.numAdditionals == 1 ? " " : "s", end - response->data, LLQType);
+
+ // According to RFC 2181 <http://www.ietf.org/rfc/rfc2181.txt>
+ // When a DNS client receives a reply with TC
+ // set, it should ignore that response, and query again, using a
+ // mechanism, such as a TCP connection, that will permit larger replies.
+ // It feels wrong to be throwing away data after the network went to all the trouble of delivering it to us, but
+ // delivering some records of the RRSet first and then the remainder a couple of milliseconds later was causing
+ // failures in our Microsoft Active Directory client, which expects to get the entire set of answers at once.
+ // <rdar://problem/6690034> Can't bind to Active Directory
+ // In addition, if the client immediately canceled its query after getting the initial partial response, then we'll
+ // abort our TCP connection, and not complete the operation, and end up with an incomplete RRSet in our cache.
+ // Next time there's a query for this RRSet we'll see answers in our cache, and assume we have the whole RRSet already,
+ // and not even do the TCP query.
+ // Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the entire RRSet.
+ if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC)) return;
+
+ if (LLQType == uDNS_LLQ_Ignore) return;
+
+ // 1. We ignore questions (if any) in mDNS response packets
+ // 2. If this is an LLQ response, we handle it much the same
+ // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this
+ // answer as being the authoritative complete RRSet, and respond by deleting all other
+ // matching cache records that don't appear in this packet.
+ // Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged
+ if (ResponseMCast || LLQType == uDNS_LLQ_Events || (response->h.flags.b[0] & kDNSFlag0_TC))
+ ptr = LocateAnswers(response, end);
+ // Otherwise, for one-shot queries, any answers in our cache that are not also contained
+ // in this response packet are immediately deemed to be invalid.
+ else
+ {
+ mDNSBool failure, returnEarly;
+ rcode = (mDNSu8)(response->h.flags.b[1] & kDNSFlag1_RC_Mask);
+ failure = !(rcode == kDNSFlag1_RC_NoErr || rcode == kDNSFlag1_RC_NXDomain || rcode == kDNSFlag1_RC_NotAuth);
+ returnEarly = mDNSfalse;
+ // We could possibly combine this with the similar loop at the end of this function --
+ // instead of tagging cache records here and then rescuing them if we find them in the answer section,
+ // we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in
+ // which it was received (or refreshed), and then at the end if we find any cache records which
+ // answer questions in this packet's question section, but which aren't tagged with this packet's
+ // packet number, then we deduce they are old and delete them
+ for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++)
+ {
+ DNSQuestion q, *qptr = mDNSNULL;
+ ptr = getQuestion(response, ptr, end, InterfaceID, &q);
+ if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr)))
+ {
+ if (!failure)
+ {
+ CacheRecord *rr;
+ // Remember the unicast question that we found, which we use to make caching
+ // decisions later on in this function
+ const mDNSu32 slot = HashSlot(&q.qname);
+ CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
+ if (!mDNSOpaque16IsZero(response->h.id))
+ {
+ unicastQuestion = qptr;
+ if (qptr->qDNSServer && DNSSECQuestion(qptr))
+ {
+ LogInfo("mDNSCoreReceiveResponse: Setting aware for %##s (%s) on %#a", qptr->qname.c,
+ DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr);
+ qptr->qDNSServer->DNSSECAware = mDNStrue;
+ qptr->qDNSServer->req_DO = mDNStrue;
+ }
+ if (qptr->ValidatingResponse)
+ DNSSECQuestion = mDNStrue;
+ }
+ for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+ if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
+ {
+ debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype),
+ rr->resrec.InterfaceID, CRDisplayString(m, rr));
+ // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm
+ rr->TimeRcvd = m->timenow - TicksTTL(rr) - 1;
+ rr->UnansweredQueries = MaxUnansweredQueries;
+ rr->CRDNSSECQuestion = 0;
+ if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+ {
+ LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for record %s, question %##s (%s)", CRDisplayString(m, rr),
+ unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype));
+ rr->CRDNSSECQuestion = 1;
+ }
+ }
+ }
+ else
+ {
+ if (qptr)
+ {
+ // If we recv any error from the DNSServer for a DNSSEC Query and if we know that the server
+ // is not DNSSEC aware, stop doing DNSSEC for that DNSServer. Note that by setting the
+ // req_DO to false here, the next retransmission for this question will turn off validation
+ // and hence retransmit without the EDNS0/DOK option.
+ if (DNSSECOptionalQuestion(qptr) && qptr->qDNSServer && !qptr->qDNSServer->DNSSECAware)
+ {
+ LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to DNSSEC Query %##s (%s), clear DO flag",
+ qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype));
+ qptr->qDNSServer->req_DO = mDNSfalse;
+ }
+ // For Unicast DNS Queries, penalize the DNSServer
+ else
+ {
+ LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)",
+ qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype));
+ PenalizeDNSServer(m, qptr, response->h.flags);
+ }
+ }
+ returnEarly = mDNStrue;
+ }
+ }
+ }
+ if (returnEarly)
+ {
+ LogInfo("Ignoring %2d Answer%s %2d Authorit%s %2d Additional%s",
+ response->h.numAnswers, response->h.numAnswers == 1 ? ", " : "s,",
+ response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,",
+ response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
+ // not goto exit because we won't have any CacheFlushRecords and we do not want to
+ // generate negative cache entries (we want to query the next server)
+ return;
+ }
+ if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+ {
+ BumpDNSSECStats(m, kStatsActionSet, kStatsTypeMsgSize, (end - response->data));
+ }
+ }
+
+ // Parse the NSEC3 records from the Authority section before we process
+ // the Answer section so that we can cache them along with the proper
+ // cache records we create.
+ if (mDNSOpaque16IsZero(response->h.id))
+ mDNSParseNSEC3Records(m, response, end, InterfaceID, &McastNSEC3Records);
+
+ for (i = 0; i < totalrecords && ptr && ptr < end; i++)
+ {
+ // All responses sent via LL multicast are acceptable for caching
+ // All responses received over our outbound TCP connections are acceptable for caching
+ mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType;
+ // (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.)
+
+ const mDNSu8 RecordType =
+ (i < firstauthority ) ? (mDNSu8)kDNSRecordTypePacketAns :
+ (i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd;
+ ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec);
+ if (!ptr) goto exit; // Break out of the loop and clean up our CacheFlushRecords list before exiting
+
+ if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative)
+ {
+ mDNSCoreResetRecord(m);
+ continue;
+ }
+
+ // We have already parsed the NSEC3 records and cached them approrpriately for
+ // multicast responses.
+ if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype == kDNSType_NSEC3)
+ {
+ mDNSCoreResetRecord(m);
+ continue;
+ }
+ // Don't want to cache OPT or TSIG pseudo-RRs
+ if (m->rec.r.resrec.rrtype == kDNSType_TSIG)
+ {
+ mDNSCoreResetRecord(m);
+ continue;
+ }
+ if (m->rec.r.resrec.rrtype == kDNSType_OPT)
+ {
+ const rdataOPT *opt;
+ const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+ // Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently
+ // delete all our own AuthRecords (which are identified by having zero MAC tags on them).
+ for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++)
+ if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0])
+ {
+ ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords);
+ ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords);
+ }
+ mDNSCoreResetRecord(m);
+ continue;
+ }
+ // if a CNAME record points to itself, then don't add it to the cache
+ if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name))
+ {
+ LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c);
+ mDNSCoreResetRecord(m);
+ continue;
+ }
+
+ // When we receive uDNS LLQ responses, we assume a long cache lifetime --
+ // In the case of active LLQs, we'll get remove events when the records actually do go away
+ // In the case of polling LLQs, we assume the record remains valid until the next poll
+ if (!mDNSOpaque16IsZero(response->h.id))
+ m->rec.r.resrec.rroriginalttl = GetEffectiveTTL(LLQType, m->rec.r.resrec.rroriginalttl);
+
+ // If response was not sent via LL multicast,
+ // then see if it answers a recent query of ours, which would also make it acceptable for caching.
+ if (!ResponseMCast)
+ {
+ if (LLQType)
+ {
+ // For Long Lived queries that are both sent over UDP and Private TCP, LLQType is set.
+ // Even though it is AcceptableResponse, we need a matching DNSServer pointer for the
+ // queries to get ADD/RMV events. To lookup the question, we can't use
+ // ExpectingUnicastResponseForRecord as the port numbers don't match. uDNS_recvLLQRespose
+ // has already matched the question using the 64 bit Id in the packet and we use that here.
+
+ if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer;
+
+ // If this is a DNSSEC question that is also LongLived, don't accept records from the
+ // Additional/Authority section blindly. We need to go through IsAcceptableResponse below
+ // so that NSEC/NSEC3 record are cached in the nseclist if we accept them. This can happen
+ // for both negative responses and wildcard expanded positive responses as both of come
+ // back with NSEC/NSEC3s.
+ if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+ AcceptableResponse = mDNSfalse;
+ }
+ else if (!AcceptableResponse || !dstaddr)
+ {
+ // For responses that come over TCP (Responses that can't fit within UDP) or TLS (Private queries
+ // that are not long lived e.g., AAAA lookup in a Private domain), it is indicated by !dstaddr.
+ // Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that
+ // we create.
+
+ if (!mDNSOpaque16IsZero(response->h.id))
+ {
+ 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;
+ }
+ }
+ }
+ else if (ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records))
+ {
+ recordAccepted = mDNStrue;
+ AcceptableResponse = mDNStrue;
+ LogInfo("mDNSCoreReceiveResponse: Accepting record in response to QU question %s, InterfaceID %p", CRDisplayString(m, &m->rec.r),
+ InterfaceID);
+ }
+ else if (IsDeviceInfoRecord(m->rec.r.resrec.name))
+ {
+ recordAccepted = mDNStrue;
+ AcceptableResponse = mDNStrue;
+ LogInfo("mDNSCoreReceiveResponse: Accepting _device-info record %s, InterfaceID %p",
+ CRDisplayString(m, &m->rec.r), InterfaceID);
+ }
+ }
+ }
+ else if (llintf && llintf->IgnoreIPv4LL && m->rec.r.resrec.rrtype == kDNSType_A)
+ {
+ CacheRecord *const rr = &m->rec.r;
+ RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data;
+
+ // If we are supposed to ignore link-local addresses on this interface, drop
+ // all "A" records that have link-local address in them.
+ if (mDNSv4AddressIsLinkLocal(&rdb->ipv4))
+ {
+ LogInfo("mDNSResponder: Dropping LinkLocal packet %s", CRDisplayString(m, &m->rec.r));
+ mDNSCoreResetRecord(m);
+ continue;
+ }
+ }
+
+ // 1. Check that this packet resource record does not conflict with any of ours
+ if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype != kDNSType_NSEC)
+ {
+ if (m->CurrentRecord)
+ LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ // We accept all multicast responses, and unicast responses resulting from queries we issued
+ // For other unicast responses, this code accepts them only for responses with an
+ // (apparently) local source address that pertain to a record of our own that's in probing state
+ if (!AcceptableResponse && !(ResponseSrcLocal && rr->resrec.RecordType == kDNSRecordTypeUnique)) continue;
+
+ if (PacketRRMatchesSignature(&m->rec.r, rr)) // If interface, name, type (if shared record) and class match...
+ {
+ // ... check to see if type and rdata are identical
+ if (IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec))
+ {
+ // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
+ if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState)
+ {
+ // If we were planning to send on this -- and only this -- interface, then we don't need to any more
+ if (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; }
+ }
+ else
+ {
+ if (rr->ImmedAnswer == mDNSNULL) { rr->ImmedAnswer = InterfaceID; m->NextScheduledResponse = m->timenow; }
+ else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
+ }
+ }
+ // else, the packet RR has different type or different rdata -- check to see if this is a conflict
+ else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r))
+ {
+ LogInfo("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r));
+ LogInfo("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr->resrec.rdatahash, ARDisplayString(m, rr));
+
+ // If this record is marked DependentOn another record for conflict detection purposes,
+ // then *that* record has to be bumped back to probing state to resolve the conflict
+ if (rr->DependentOn)
+ {
+ while (rr->DependentOn) rr = rr->DependentOn;
+ LogInfo("mDNSCoreReceiveResponse: Dep Record: %08lX %s", rr->resrec.rdatahash, ARDisplayString(m, rr));
+ }
+
+ // If we've just whacked this record's ProbeCount, don't need to do it again
+ if (rr->ProbeCount > DefaultProbeCountForTypeUnique)
+ LogInfo("mDNSCoreReceiveResponse: Already reset to Probing: %s", ARDisplayString(m, rr));
+ else if (rr->ProbeCount == DefaultProbeCountForTypeUnique)
+ LogMsg("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr));
+ else
+ {
+ LogMsg("mDNSCoreReceiveResponse: Received from %#a:%d %s", srcaddr, mDNSVal16(srcport), CRDisplayString(m, &m->rec.r));
+ // If we'd previously verified this record, put it back to probing state and try again
+ if (rr->resrec.RecordType == kDNSRecordTypeVerified)
+ {
+ LogMsg("mDNSCoreReceiveResponse: Resetting to Probing: %s", ARDisplayString(m, rr));
+ rr->resrec.RecordType = kDNSRecordTypeUnique;
+ // We set ProbeCount to one more than the usual value so we know we've already touched this record.
+ // This is because our single probe for "example-name.local" could yield a response with (say) two A records and
+ // three AAAA records in it, and we don't want to call RecordProbeFailure() five times and count that as five conflicts.
+ // This special value is recognised and reset to DefaultProbeCountForTypeUnique in SendQueries().
+ rr->ProbeCount = DefaultProbeCountForTypeUnique + 1;
+ rr->AnnounceCount = InitialAnnounceCount;
+ InitializeLastAPTime(m, rr);
+ RecordProbeFailure(m, rr); // Repeated late conflicts also cause us to back off to the slower probing rate
+ }
+ // If we're probing for this record, we just failed
+ else if (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);
+ }
+ }
+ // We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the
+ // same machine giving different answers for the reverse mapping record, or there are two machines on the
+ // network using the same IP address.) This is simply a misconfiguration, and there's nothing we can do
+ // to fix it -- e.g. it's not our job to be trying to change the machine's IP address. We just discard our
+ // record to avoid continued conflicts (as we do for a conflict on our Unique records) and get on with life.
+ else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
+ {
+ LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr));
+ m->mDNSStats.KnownUniqueNameConflicts++;
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
+ }
+ else
+ LogMsg("mDNSCoreReceiveResponse: Unexpected record type %X %s", rr->resrec.RecordType, ARDisplayString(m, rr));
+ }
+ }
+ // Else, matching signature, different type or rdata, but not a considered a conflict.
+ // If the packet record has the cache-flush bit set, then we check to see if we
+ // have any record(s) of the same type that we should re-assert to rescue them
+ // (see note about "multi-homing and bridged networks" at the end of this function).
+ else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype)
+ if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2)
+ { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
+ }
+ }
+ }
+
+ nseclist = mDNSfalse;
+ if (!AcceptableResponse)
+ {
+ AcceptableResponse = IsResponseAcceptable(m, CacheFlushRecords, unicastQuestion, &nseclist);
+ if (AcceptableResponse) m->rec.r.resrec.rDNSServer = uDNSServer;
+ }
+
+ // 2. See if we want to add this packet resource record to our cache
+ // We only try to cache answers if we have a cache to put them in
+ // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query
+ if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r));
+ if (m->rrcache_size && AcceptableResponse)
+ {
+ const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
+ CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
+ CacheRecord *rr = mDNSNULL;
+
+ if (McastNSEC3Records)
+ InitializeAnonInfoForCR(m, &McastNSEC3Records, &m->rec.r);
+
+ // 2a. Check if this packet resource record is already in our cache.
+ //
+ // If this record should go in the nseclist, don't look in the cache for updating it.
+ // They are supposed to be cached under the "nsec" field of the cache record for
+ // validation. Just create the cache record.
+ if (!nseclist)
+ {
+ rr = mDNSCoreReceiveCacheCheck(m, response, LLQType, slot, cg, unicastQuestion, &cfp, &NSECCachePtr, InterfaceID);
+ }
+
+ // If mDNSOppCaching is set (which affects only multicast), enable opportunistic caching in which case we cache
+ // everything that was received over multicast. Otherwise, we are selective about the caching.
+ //
+ // Cache everything that is from ourselves (that's how we answer any questions looking for them). Otherwise call
+ // ExpectingMulticastResponseForRecord which decides whether to cache this record or not.
+ //
+ if (!m->mDNSOppCaching && !rr && !myself && mDNSOpaque16IsZero(response->h.id))
+ {
+ if (!ExpectingMulticastResponseForRecord(m, &m->rec.r, srcaddr, recordAccepted, &McastNSEC3Records))
+ {
+ //LogMsg("mDNSCoreReceiveResponse: discarding %s", CRDisplayString(m, &m->rec.r));
+ mDNSCoreResetRecord(m);
+ continue;
+ }
+ else
+ {
+ recordAccepted = mDNStrue;
+ }
+ }
+
+
+ // If packet resource record not in our cache, add it now
+ // (unless it is just a deletion of a record we never had, in which case we don't care)
+ if (!rr && m->rec.r.resrec.rroriginalttl > 0)
+ {
+ const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events);
+ mDNSs32 delay;
+
+ if (AddToCFList)
+ delay = NonZeroTime(m->timenow + mDNSPlatformOneSecond);
+ else
+ delay = CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot, mDNSNULL);
+
+ // If unique, assume we may have to delay delivery of this 'add' event.
+ // Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd()
+ // to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime()
+ // to schedule an mDNS_Execute task at the appropriate time.
+ rr = CreateNewCacheEntry(m, slot, cg, delay, !nseclist, srcaddr);
+ if (rr)
+ {
+ rr->responseFlags = response->h.flags;
+ // If we are not creating signatures, then we need to inform DNSSEC so that
+ // it does not wait forever. Don't do this if we got NSEC records
+ // as it indicates that this name does not exist.
+ if (rr->resrec.rrtype == kDNSType_RRSIG && !nseclist)
+ {
+ rrsigsCreated = mDNStrue;
+ }
+ // Remember whether we created a cache record in response to a DNSSEC question.
+ // This helps DNSSEC code not to reissue the question to fetch the DNSSEC records.
+ rr->CRDNSSECQuestion = 0;
+ if (unicastQuestion && DNSSECQuestion(unicastQuestion))
+ {
+ LogInfo("mDNSCoreReceiveResponse: CRDNSSECQuestion set for new record %s, question %##s (%s)", CRDisplayString(m, rr),
+ unicastQuestion->qname.c, DNSTypeName(unicastQuestion->qtype));
+ rr->CRDNSSECQuestion = 1;
+ }
+ // NSEC/NSEC3 records and its signatures are cached with the negative cache entry
+ // which we should be creating below. It is also needed in the wildcard
+ // expanded answer case and in that case it is cached along with the answer.
+ if (nseclist)
+ {
+ rr->TimeRcvd = m->timenow;
+ *nsecp = rr;
+ nsecp = &rr->next;
+ }
+ else if (AddToCFList)
+ {
+ *cfp = rr;
+ cfp = &rr->NextInCFList;
+ *cfp = (CacheRecord*)1;
+ }
+ else if (rr->DelayDelivery)
+ {
+ ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery);
+ }
+ }
+ }
+ else
+ {
+ if (rr && rr->resrec.AnonInfo && m->rec.r.resrec.AnonInfo)
+ {
+ CopyAnonInfoForCR(m, rr, &m->rec.r);
+ }
+ }
+ }
+ mDNSCoreResetRecord(m);
+ }
+
+exit:
+ mDNSCoreResetRecord(m);
+
+ // If we've just received one or more records with their cache flush bits set,
+ // then scan that cache slot to see if there are any old stale records we need to flush
+ while (CacheFlushRecords != (CacheRecord*)1)
+ {
+ CacheRecord *r1 = CacheFlushRecords, *r2;
+ const mDNSu32 slot = HashSlot(r1->resrec.name);
+ const CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
+ CacheFlushRecords = CacheFlushRecords->NextInCFList;
+ r1->NextInCFList = mDNSNULL;
+
+ // Look for records in the cache with the same signature as this new one with the cache flush
+ // bit set, and either (a) if they're fresh, just make sure the whole RRSet has the same TTL
+ // (as required by DNS semantics) or (b) if they're old, mark them for deletion in one second.
+ // We make these TTL adjustments *only* for records that still have *more* than one second
+ // remaining to live. Otherwise, a record that we tagged for deletion half a second ago
+ // (and now has half a second remaining) could inadvertently get its life extended, by either
+ // (a) if we got an explicit goodbye packet half a second ago, the record would be considered
+ // "fresh" and would be incorrectly resurrected back to the same TTL as the rest of the RRSet,
+ // or (b) otherwise, the record would not be fully resurrected, but would be reset to expire
+ // in one second, thereby inadvertently delaying its actual expiration, instead of hastening it.
+ // If this were to happen repeatedly, the record's expiration could be deferred indefinitely.
+ // To avoid this, we need to ensure that the cache flushing operation will only act to
+ // *decrease* a record's remaining lifetime, never *increase* it.
+ for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next)
+ {
+ mDNSu16 id1;
+ mDNSu16 id2;
+ if (!r1->resrec.InterfaceID)
+ {
+ id1 = (r1->resrec.rDNSServer ? r1->resrec.rDNSServer->resGroupID : 0);
+ id2 = (r2->resrec.rDNSServer ? r2->resrec.rDNSServer->resGroupID : 0);
+ }
+ else
+ {
+ id1 = id2 = 0;
+ }
+ // When we receive new RRSIGs e.g., for DNSKEY record, we should not flush the old
+ // RRSIGS e.g., for TXT record. To do so, we need to look at the typeCovered field of
+ // the new RRSIG that we received. Process only if the typeCovered matches.
+ if ((r1->resrec.rrtype == r2->resrec.rrtype) && (r1->resrec.rrtype == kDNSType_RRSIG))
+ {
+ rdataRRSig *rrsig1 = (rdataRRSig *)(((RDataBody2 *)(r1->resrec.rdata->u.data))->data);
+ rdataRRSig *rrsig2 = (rdataRRSig *)(((RDataBody2 *)(r2->resrec.rdata->u.data))->data);
+ if (swap16(rrsig1->typeCovered) != swap16(rrsig2->typeCovered))
+ {
+ debugf("mDNSCoreReceiveResponse: Received RRSIG typeCovered %s, found %s, not processing",
+ DNSTypeName(swap16(rrsig1->typeCovered)), DNSTypeName(swap16(rrsig2->typeCovered)));
+ continue;
+ }
+ }
+
+ // For Unicast (null InterfaceID) the resolver IDs should also match
+ if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) &&
+ (r1->resrec.InterfaceID || (id1 == id2)) &&
+ r1->resrec.rrtype == r2->resrec.rrtype &&
+ r1->resrec.rrclass == r2->resrec.rrclass)
+ {
+ // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
+ // else, if record is old, mark it to be flushed
+ if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond)
+ {
+ // If we find mismatched TTLs in an RRSet, correct them.
+ // We only do this for records with a TTL of 2 or higher. It's possible to have a
+ // goodbye announcement with the cache flush bit set (or a case-change on record rdata,
+ // which we treat as a goodbye followed by an addition) and in that case it would be
+ // inappropriate to synchronize all the other records to a TTL of 0 (or 1).
+ // We suppress the message for the specific case of correcting from 240 to 60 for type TXT,
+ // because certain early Bonjour devices are known to have this specific mismatch, and
+ // there's no point filling syslog with messages about something we already know about.
+ // We also don't log this for uDNS responses, since a caching name server is obliged
+ // 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
+ if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1)
+ {
+ if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) &&
+ mDNSOpaque16IsZero(response->h.id))
+ LogInfo("Correcting TTL from %4d to %4d for %s",
+ r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2));
+ r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
+ }
+ r2->TimeRcvd = m->timenow;
+ }
+ else // else, if record is old, mark it to be flushed
+ {
+ verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1));
+ verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2));
+ // We set stale records to expire in one second.
+ // This gives the owner a chance to rescue it if necessary.
+ // This is important in the case of multi-homing and bridged networks:
+ // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
+ // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
+ // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
+ // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
+ // By delaying the deletion by one second, we give X a change to notice that this bridging has
+ // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
+
+ // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
+ // final expiration queries for this record.
+
+ // If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache
+ // flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual
+ // one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates.
+ // <rdar://problem/5636422> Updating TXT records is too slow
+ // We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above,
+ // which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0.
+ if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries)
+ {
+ LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2));
+ r2->resrec.rroriginalttl = 0;
+ }
+ else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond)
+ {
+ // We only set a record to expire in one second if it currently has *more* than a second to live
+ // If it's already due to expire in a second or less, we just leave it alone
+ r2->resrec.rroriginalttl = 1;
+ r2->UnansweredQueries = MaxUnansweredQueries;
+ r2->TimeRcvd = m->timenow - 1;
+ // We use (m->timenow - 1) instead of m->timenow, because we use that to identify records
+ // that we marked for deletion via an explicit DE record
+ }
+ }
+ SetNextCacheCheckTimeForRecord(m, r2);
+ }
+ }
+
+ if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to
+ {
+ // If we had a unicast question for this response with at least one positive answer and we
+ // have NSECRecords, it is most likely a wildcard expanded answer. Cache the NSEC and its
+ // signatures along with the cache record which will be used for validation later. If
+ // we rescued a few records earlier in this function, then NSECCachePtr would be set. In that
+ // use that instead.
+ if (response->h.numAnswers && unicastQuestion && NSECRecords)
+ {
+ if (!NSECCachePtr)
+ {
+ LogInfo("mDNSCoreReceiveResponse: Updating NSECCachePtr to %s", CRDisplayString(m, r1));
+ NSECCachePtr = r1;
+ }
+ // 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);
+ }
+ NSECRecords = mDNSNULL;
+ NSECCachePtr = mDNSNULL;
+ }
+ r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot, mDNSNULL);
+ // If no longer delaying, deliver answer now, else schedule delivery for the appropriate time
+ if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
+ else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery);
+ }
+ }
+
+ // If we have not consumed the NSEC records yet e.g., just refreshing the cache,
+ // update them now for future validations.
+ if (NSECRecords && NSECCachePtr)
+ {
+ 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);
+ }
+ NSECRecords = mDNSNULL;
+ NSECCachePtr = mDNSNULL;
+ }
+
+ // If there is at least one answer and we did not create RRSIGs and there was a
+ // ValidatingResponse question waiting for this response, give a hint that no RRSIGs
+ // were created. We don't need to give a hint:
+ //
+ // - if we have no answers, the mDNSCoreReceiveNoUnicastAnswers below should
+ // generate a negative response
+ //
+ // - if we have NSECRecords, it means we might have a potential proof for
+ // non-existence of name that we are looking for
+ //
+ if (response->h.numAnswers && !rrsigsCreated && DNSSECQuestion && !NSECRecords)
+ mDNSCoreReceiveNoDNSSECAnswers(m, response, end, dstaddr, dstport, InterfaceID);
+
+ // See if we need to generate negative cache entries for unanswered unicast questions
+ mDNSCoreReceiveNoUnicastAnswers(m, response, end, dstaddr, dstport, InterfaceID, LLQType, rcode, NSECRecords);
+
+ if (McastNSEC3Records)
+ {
+ debugf("mDNSCoreReceiveResponse: McastNSEC3Records not used");
+ FreeNSECRecords(m, McastNSEC3Records);
+ }
+}
+
+// ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing
+// multiple wakeup magic packets to be sent if appropriate, and all records to be ultimately freed after a few seconds.
+// ScheduleWakeup is called on mDNS record conflicts, ARP conflicts, NDP conflicts, or reception of trigger traffic
+// that warrants waking the sleeping host.
+// ScheduleWakeup must be called with the lock held (ScheduleWakeupForList uses mDNS_Deregister_internal)
+
+mDNSlocal void ScheduleWakeupForList(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e, AuthRecord *const thelist)
+{
+ // We need to use the m->CurrentRecord mechanism here when dealing with DuplicateRecords list as
+ // mDNS_Deregister_internal deregisters duplicate records immediately as they are not used
+ // to send wakeups or goodbyes. See the comment in that function for more details. To keep it
+ // simple, we use the same mechanism for both lists.
+ if (!e->l[0])
+ {
+ LogMsg("ScheduleWakeupForList ERROR: Target HMAC is zero");
+ return;
+ }
+ m->CurrentRecord = thelist;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *const rr = m->CurrentRecord;
+ if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e))
+ {
+ LogInfo("ScheduleWakeupForList: Scheduling wakeup packets for %s", ARDisplayString(m, rr));
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+ }
+ if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+ m->CurrentRecord = rr->next;
+ }
+}
+
+mDNSlocal void ScheduleWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e)
+{
+ if (!e->l[0]) { LogMsg("ScheduleWakeup ERROR: Target HMAC is zero"); return; }
+ ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords);
+ ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords);
+}
+
+mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus result)
+{
+ if (result && result != mStatus_MemFree)
+ LogInfo("SPS Callback %d %s", result, ARDisplayString(m, ar));
+
+ if (result == mStatus_NameConflict)
+ {
+ mDNS_Lock(m);
+ 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
+ }
+ mDNS_Unlock(m);
+ }
+
+ if (result == mStatus_NameConflict || result == mStatus_MemFree)
+ {
+ m->ProxyRecords--;
+ mDNSPlatformMemFree(ar);
+ mDNS_UpdateAllowSleep(m);
+ }
+}
+
+mDNSlocal mDNSu8 *GetValueForMACAddr(mDNSu8 *ptr, mDNSu8 *limit, mDNSEthAddr *eth)
+{
+ int i;
+ mDNSs8 hval = 0;
+ int colons = 0;
+ mDNSu8 val = 0;
+
+ for (i = 0; ptr < limit && *ptr != ' ' && i < 17; i++, ptr++)
+ {
+ hval = HexVal(*ptr);
+ if (hval != -1)
+ {
+ val <<= 4;
+ val |= hval;
+ }
+ else if (*ptr == ':')
+ {
+ eth->b[colons] = val;
+ colons++;
+ val = 0;
+ }
+ }
+ if (colons != 5)
+ {
+ LogMsg("GetValueForMACAddr: Address malformed colons %d", colons);
+ return mDNSNULL;
+ }
+ eth->b[colons] = val;
+ return ptr;
+}
+
+mDNSlocal mDNSu8 *GetValueForIPv6Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv6Addr *v6)
+{
+ int hval;
+ int value;
+ int numBytes;
+ int digitsProcessed;
+ int zeroFillStart;
+ int numColons;
+ mDNSu8 v6addr[16];
+
+ // RFC 3513: Section 2.2 specifies IPv6 presentation format. The following parsing
+ // handles both (1) and (2) and does not handle embedded IPv4 addresses.
+ //
+ // First forms a address in "v6addr", then expands to fill the zeroes in and returns
+ // the result in "v6"
+
+ numColons = numBytes = value = digitsProcessed = zeroFillStart = 0;
+ while (ptr < limit && *ptr != ' ')
+ {
+ hval = HexVal(*ptr);
+ if (hval != -1)
+ {
+ value <<= 4;
+ value |= hval;
+ digitsProcessed = 1;
+ }
+ else if (*ptr == ':')
+ {
+ if (!digitsProcessed)
+ {
+ // If we have already seen a "::", we should not see one more. Handle the special
+ // case of "::"
+ if (numColons)
+ {
+ // if we never filled any bytes and the next character is space (we have reached the end)
+ // we are done
+ if (!numBytes && (ptr + 1) < limit && *(ptr + 1) == ' ')
+ {
+ mDNSPlatformMemZero(v6->b, 16);
+ return ptr + 1;
+ }
+ LogMsg("GetValueForIPv6Addr: zeroFillStart non-zero %d", zeroFillStart);
+ return mDNSNULL;
+ }
+
+ // We processed "::". We need to fill zeroes later. For now, mark the
+ // point where we will start filling zeroes from.
+ zeroFillStart = numBytes;
+ numColons++;
+ }
+ else if ((ptr + 1) < limit && *(ptr + 1) == ' ')
+ {
+ // We have a trailing ":" i.e., no more characters after ":"
+ LogMsg("GetValueForIPv6Addr: Trailing colon");
+ return mDNSNULL;
+ }
+ else
+ {
+ // For a fully expanded IPv6 address, we fill the 14th and 15th byte outside of this while
+ // loop below as there is no ":" at the end. Hence, the last two bytes that can possibly
+ // filled here is 12 and 13.
+ if (numBytes > 13) { LogMsg("GetValueForIPv6Addr:1: numBytes is %d", numBytes); return mDNSNULL; }
+
+ v6addr[numBytes++] = (mDNSu8) ((value >> 8) & 0xFF);
+ v6addr[numBytes++] = (mDNSu8) (value & 0xFF);
+ digitsProcessed = value = 0;
+
+ // Make sure that we did not fill the 13th and 14th byte above
+ if (numBytes > 14) { LogMsg("GetValueForIPv6Addr:2: numBytes is %d", numBytes); return mDNSNULL; }
+ }
+ }
+ ptr++;
+ }
+
+ // We should be processing the last set of bytes following the last ":" here
+ if (!digitsProcessed)
+ {
+ LogMsg("GetValueForIPv6Addr: no trailing bytes after colon, numBytes is %d", numBytes);
+ return mDNSNULL;
+ }
+
+ if (numBytes > 14) { LogMsg("GetValueForIPv6Addr:3: numBytes is %d", numBytes); return mDNSNULL; }
+ v6addr[numBytes++] = (mDNSu8) ((value >> 8) & 0xFF);
+ v6addr[numBytes++] = (mDNSu8) (value & 0xFF);
+
+ if (zeroFillStart)
+ {
+ int i, j, n;
+ for (i = 0; i < zeroFillStart; i++)
+ v6->b[i] = v6addr[i];
+ for (j = i, n = 0; n < 16 - numBytes; j++, n++)
+ v6->b[j] = 0;
+ for (; j < 16; i++, j++)
+ v6->b[j] = v6addr[i];
+ }
+ else if (numBytes == 16)
+ mDNSPlatformMemCopy(v6->b, v6addr, 16);
+ else
+ {
+ LogMsg("GetValueForIPv6addr: Not enough bytes for IPv6 address, numBytes is %d", numBytes);
+ return mDNSNULL;
+ }
+ return ptr;
+}
+
+mDNSlocal mDNSu8 *GetValueForIPv4Addr(mDNSu8 *ptr, mDNSu8 *limit, mDNSv4Addr *v4)
+{
+ mDNSu32 val;
+ int dots = 0;
+ val = 0;
+
+ for ( ; ptr < limit && *ptr != ' '; ptr++)
+ {
+ if (*ptr >= '0' && *ptr <= '9')
+ val = val * 10 + *ptr - '0';
+ else if (*ptr == '.')
+ {
+ v4->b[dots++] = val;
+ val = 0;
+ }
+ else
+ {
+ // We have a zero at the end and if we reached that, then we are done.
+ if (*ptr == 0 && ptr == limit - 1 && dots == 3)
+ {
+ v4->b[dots] = val;
+ return ptr + 1;
+ }
+ else { LogMsg("GetValueForIPv4Addr: something wrong ptr(%p) %c, limit %p, dots %d", ptr, *ptr, limit, dots); return mDNSNULL; }
+ }
+ }
+ if (dots != 3) { LogMsg("GetValueForIPv4Addr: Address malformed dots %d", dots); return mDNSNULL; }
+ v4->b[dots] = val;
+ return ptr;
+}
+
+mDNSlocal mDNSu8 *GetValueForKeepalive(mDNSu8 *ptr, mDNSu8 *limit, mDNSu32 *value)
+{
+ mDNSu32 val;
+
+ val = 0;
+ for ( ; ptr < limit && *ptr != ' '; ptr++)
+ {
+ if (*ptr < '0' || *ptr > '9')
+ {
+ // We have a zero at the end and if we reached that, then we are done.
+ if (*ptr == 0 && ptr == limit - 1)
+ {
+ *value = val;
+ return ptr + 1;
+ }
+ else { LogMsg("GetValueForKeepalive: *ptr %d, ptr %p, limit %p, ptr +1 %d", *ptr, ptr, limit, *(ptr + 1)); return mDNSNULL; }
+ }
+ val = val * 10 + *ptr - '0';
+ }
+ *value = val;
+ return ptr;
+}
+
+mDNSlocal void mDNS_ExtractKeepaliveInfo(AuthRecord *ar, mDNSu32 *timeout, mDNSAddr *laddr, mDNSAddr *raddr, mDNSEthAddr *eth, mDNSu32 *seq,
+ mDNSu32 *ack, mDNSIPPort *lport, mDNSIPPort *rport, mDNSu16 *win)
+{
+ if (ar->resrec.rrtype != kDNSType_NULL)
+ return;
+
+ if (mDNS_KeepaliveRecord(&ar->resrec))
+ {
+ int len = ar->resrec.rdlength;
+ mDNSu8 *ptr = &ar->resrec.rdata->u.txt.c[1];
+ mDNSu8 *limit = ptr + len - 1; // Exclude the first byte that is the length
+ mDNSu32 value = 0;
+
+ while (ptr < limit)
+ {
+ mDNSu8 param = *ptr;
+ ptr += 2; // Skip the letter and the "="
+ if (param == 'h')
+ {
+ laddr->type = mDNSAddrType_IPv4;
+ ptr = GetValueForIPv4Addr(ptr, limit, &laddr->ip.v4);
+ }
+ else if (param == 'd')
+ {
+ raddr->type = mDNSAddrType_IPv4;
+ ptr = GetValueForIPv4Addr(ptr, limit, &raddr->ip.v4);
+ }
+ if (param == 'H')
+ {
+ laddr->type = mDNSAddrType_IPv6;
+ ptr = GetValueForIPv6Addr(ptr, limit, &laddr->ip.v6);
+ }
+ else if (param == 'D')
+ {
+ raddr->type = mDNSAddrType_IPv6;
+ ptr = GetValueForIPv6Addr(ptr, limit, &raddr->ip.v6);
+ }
+ else if (param == 'm')
+ {
+ ptr = GetValueForMACAddr(ptr, limit, eth);
+ }
+ else
+ {
+ ptr = GetValueForKeepalive(ptr, limit, &value);
+ }
+ if (!ptr) { LogMsg("mDNS_ExtractKeepaliveInfo: Cannot parse\n"); return; }
+
+ // Extract everything in network order so that it is easy for sending a keepalive and also
+ // for matching incoming TCP packets
+ switch (param)
+ {
+ case 't':
+ *timeout = value;
+ //if (*timeout < 120) *timeout = 120;
+ break;
+ case 'h':
+ case 'H':
+ case 'd':
+ case 'D':
+ case 'm':
+ case 'i':
+ case 'c':
+ break;
+ case 'l':
+ lport->NotAnInteger = swap16((mDNSu16)value);
+ break;
+ case 'r':
+ rport->NotAnInteger = swap16((mDNSu16)value);
+ break;
+ case 's':
+ *seq = swap32(value);
+ break;
+ case 'a':
+ *ack = swap32(value);
+ break;
+ case 'w':
+ *win = swap16((mDNSu16)value);
+ break;
+ default:
+ LogMsg("mDNS_ExtractKeepaliveInfo: unknown value %c\n", param);
+ ptr = limit;
+ break;
+ }
+ ptr++; // skip the space
+ }
+ }
+}
+
+// Matches the proxied auth records to the incoming TCP packet and returns the match and its sequence and ack in "rseq" and "rack" so that
+// the clients need not retrieve this information from the auth record again.
+mDNSlocal AuthRecord* mDNS_MatchKeepaliveInfo(mDNS *const m, const mDNSAddr* pladdr, const mDNSAddr* praddr, const mDNSIPPort plport,
+ const mDNSIPPort prport, mDNSu32 *rseq, mDNSu32 *rack)
+{
+ AuthRecord *ar;
+ mDNSAddr laddr, raddr;
+ mDNSEthAddr eth;
+ mDNSIPPort lport, rport;
+ mDNSu32 timeout, seq, ack;
+ mDNSu16 win;
+
+ for (ar = m->ResourceRecords; ar; ar=ar->next)
+ {
+ timeout = seq = ack = 0;
+ win = 0;
+ laddr = raddr = zeroAddr;
+ lport = rport = zeroIPPort;
+
+ if (!ar->WakeUp.HMAC.l[0]) continue;
+
+ mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win);
+
+ // Did we parse correctly ?
+ if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
+ {
+ debugf("mDNS_MatchKeepaliveInfo: not a valid record %s for keepalive", ARDisplayString(m, ar));
+ continue;
+ }
+
+ debugf("mDNS_MatchKeepaliveInfo: laddr %#a pladdr %#a, raddr %#a praddr %#a, lport %d plport %d, rport %d prport %d",
+ &laddr, pladdr, &raddr, praddr, mDNSVal16(lport), mDNSVal16(plport), mDNSVal16(rport), mDNSVal16(prport));
+
+ // Does it match the incoming TCP packet ?
+ if (mDNSSameAddress(&laddr, pladdr) && mDNSSameAddress(&raddr, praddr) && mDNSSameIPPort(lport, plport) && mDNSSameIPPort(rport, prport))
+ {
+ // returning in network order
+ *rseq = seq;
+ *rack = ack;
+ return ar;
+ }
+ }
+ return mDNSNULL;
+}
+
+mDNSlocal void mDNS_SendKeepalives(mDNS *const m)
+{
+ AuthRecord *ar;
+
+ for (ar = m->ResourceRecords; ar; ar=ar->next)
+ {
+ mDNSu32 timeout, seq, ack;
+ mDNSu16 win;
+ mDNSAddr laddr, raddr;
+ mDNSEthAddr eth;
+ mDNSIPPort lport, rport;
+
+ timeout = seq = ack = 0;
+ win = 0;
+
+ laddr = raddr = zeroAddr;
+ lport = rport = zeroIPPort;
+
+ if (!ar->WakeUp.HMAC.l[0]) continue;
+
+ mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win);
+
+ if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
+ {
+ debugf("mDNS_SendKeepalives: not a valid record %s for keepalive", ARDisplayString(m, ar));
+ continue;
+ }
+ LogMsg("mDNS_SendKeepalives: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport));
+
+ // When we receive a proxy update, we set KATimeExpire to zero so that we always send a keepalive
+ // immediately (to detect any potential problems). After that we always set it to a non-zero value.
+ if (!ar->KATimeExpire || (m->timenow - ar->KATimeExpire >= 0))
+ {
+ mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win);
+ ar->KATimeExpire = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond);
+ }
+ if (m->NextScheduledKA - ar->KATimeExpire > 0)
+ m->NextScheduledKA = ar->KATimeExpire;
+ }
+}
+
+mDNSlocal void mDNS_SendKeepaliveACK(mDNS *const m, AuthRecord *ar)
+{
+ if (ar != mDNSNULL)
+ {
+ LogInfo("mDNS_SendKeepalivesACK: AuthRecord is NULL");
+ return;
+ }
+ mDNSu32 timeout, seq, ack;
+ mDNSu16 win;
+ mDNSAddr laddr, raddr;
+ mDNSEthAddr eth;
+ mDNSIPPort lport, rport;
+
+ timeout = seq = ack = 0;
+ win = 0;
+
+ laddr = raddr = zeroAddr;
+ lport = rport = zeroIPPort;
+
+ mDNS_ExtractKeepaliveInfo(ar, &timeout, &laddr, &raddr, ð, &seq, &ack, &lport, &rport, &win);
+
+ if (!timeout || mDNSAddressIsZero(&laddr) || mDNSAddressIsZero(&raddr) || !seq || !ack || mDNSIPPortIsZero(lport) || mDNSIPPortIsZero(rport) || !win)
+ {
+ LogInfo("mDNS_SendKeepaliveACK: not a valid record %s for keepalive", ARDisplayString(m, ar));
+ return;
+ }
+ LogMsg("mDNS_SendKeepaliveACK: laddr %#a raddr %#a lport %d rport %d", &laddr, &raddr, mDNSVal16(lport), mDNSVal16(rport));
+ mDNSPlatformSendKeepalive(&laddr, &raddr, &lport, &rport, seq, ack, win);
+}
+
+mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
+ const DNSMessage *const msg, const mDNSu8 *end,
+ const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+ const mDNSInterfaceID InterfaceID)
+{
+ int i;
+ AuthRecord opt;
+ mDNSu8 *p = m->omsg.data;
+ OwnerOptData owner = zeroOwner; // Need to zero this, so we'll know if this Update packet was missing its Owner option
+ mDNSu32 updatelease = 0;
+ const mDNSu8 *ptr;
+
+ LogSPS("Received Update from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
+ "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes",
+ 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);
+
+ if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return;
+
+ if (mDNS_PacketLoggingEnabled)
+ DumpPacket(m, mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
+
+ ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space);
+ if (ptr)
+ {
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
+ if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
+ {
+ const rdataOPT *o;
+ const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+ for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++)
+ {
+ if (o->opt == kDNSOpt_Lease) updatelease = o->u.updatelease;
+ else if (o->opt == kDNSOpt_Owner && o->u.owner.vers == 0) owner = o->u.owner;
+ }
+ }
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ }
+
+ InitializeDNSMessage(&m->omsg.h, msg->h.id, UpdateRespFlags);
+
+ if (!updatelease || !owner.HMAC.l[0])
+ {
+ static int msgs = 0;
+ if (msgs < 100)
+ {
+ msgs++;
+ LogMsg("Refusing sleep proxy registration from %#a:%d:%s%s", srcaddr, mDNSVal16(srcport),
+ !updatelease ? " No lease" : "", !owner.HMAC.l[0] ? " No owner" : "");
+ }
+ m->omsg.h.flags.b[1] |= kDNSFlag1_RC_FormErr;
+ }
+ else if (m->ProxyRecords + msg->h.mDNS_numUpdates > MAX_PROXY_RECORDS)
+ {
+ static int msgs = 0;
+ if (msgs < 100)
+ {
+ msgs++;
+ LogMsg("Refusing sleep proxy registration from %#a:%d: Too many records %d + %d = %d > %d", srcaddr, mDNSVal16(srcport),
+ m->ProxyRecords, msg->h.mDNS_numUpdates, m->ProxyRecords + msg->h.mDNS_numUpdates, MAX_PROXY_RECORDS);
+ }
+ m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused;
+ }
+ else
+ {
+ LogSPS("Received Update for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq);
+
+ if (updatelease > 24 * 60 * 60)
+ updatelease = 24 * 60 * 60;
+
+ if (updatelease > 0x40000000UL / mDNSPlatformOneSecond)
+ updatelease = 0x40000000UL / mDNSPlatformOneSecond;
+
+ ptr = LocateAuthorities(msg, end);
+
+ // Clear any stale TCP keepalive records that may exist
+ ClearKeepaliveProxyRecords(m, &owner, m->DuplicateRecords, InterfaceID);
+ ClearKeepaliveProxyRecords(m, &owner, m->ResourceRecords, InterfaceID);
+
+ for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++)
+ {
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
+ if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative)
+ {
+ mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec);
+ AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem);
+ if (!ar)
+ {
+ m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused;
+ break;
+ }
+ else
+ {
+ mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared;
+ m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet;
+ // All stale keepalive records have been flushed prior to this loop.
+ if (!mDNS_KeepaliveRecord(&m->rec.r.resrec))
+ {
+ ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords); // Make sure we don't have any old stale duplicates of this record
+ ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords);
+ }
+ mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar);
+ AssignDomainName(&ar->namestorage, m->rec.r.resrec.name);
+ ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse);
+ ar->resrec.rdata->MaxRDLength = RDLengthMem;
+ mDNSPlatformMemCopy(ar->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, RDLengthMem);
+ ar->ForceMCast = mDNStrue;
+ ar->WakeUp = owner;
+ if (m->rec.r.resrec.rrtype == kDNSType_PTR)
+ {
+ mDNSs32 t = ReverseMapDomainType(m->rec.r.resrec.name);
+ if (t == mDNSAddrType_IPv4) GetIPv4FromName(&ar->AddressProxy, m->rec.r.resrec.name);
+ else if (t == mDNSAddrType_IPv6) GetIPv6FromName(&ar->AddressProxy, m->rec.r.resrec.name);
+ debugf("mDNSCoreReceiveUpdate: PTR %d %d %#a %s", t, ar->AddressProxy.type, &ar->AddressProxy, ARDisplayString(m, ar));
+ if (ar->AddressProxy.type) SetSPSProxyListChanged(InterfaceID);
+ }
+ ar->TimeRcvd = m->timenow;
+ ar->TimeExpire = m->timenow + updatelease * mDNSPlatformOneSecond;
+ if (m->NextScheduledSPS - ar->TimeExpire > 0)
+ m->NextScheduledSPS = ar->TimeExpire;
+ ar->KATimeExpire = 0;
+ mDNS_Register_internal(m, ar);
+
+ m->ProxyRecords++;
+ mDNS_UpdateAllowSleep(m);
+ LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar));
+ }
+ }
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ }
+
+ if (m->omsg.h.flags.b[1] & kDNSFlag1_RC_Mask)
+ {
+ LogMsg("Refusing sleep proxy registration from %#a:%d: Out of memory", srcaddr, mDNSVal16(srcport));
+ ClearProxyRecords(m, &owner, m->DuplicateRecords);
+ ClearProxyRecords(m, &owner, m->ResourceRecords);
+ }
+ else
+ {
+ mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+ opt.resrec.rrclass = NormalMaxDNSMessageData;
+ opt.resrec.rdlength = sizeof(rdataOPT); // One option in this OPT record
+ opt.resrec.rdestimate = sizeof(rdataOPT);
+ opt.resrec.rdata->u.opt[0].opt = kDNSOpt_Lease;
+ opt.resrec.rdata->u.opt[0].u.updatelease = updatelease;
+ p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
+ }
+ }
+
+ if (p) mDNSSendDNSMessage(m, &m->omsg, p, InterfaceID, m->SPSSocket, srcaddr, srcport, mDNSNULL, mDNSNULL, mDNSfalse);
+ mDNS_SendKeepalives(m);
+}
+
+mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
+{
+ if (InterfaceID)
+ {
+ mDNSu32 updatelease = 60 * 60; // If SPS fails to indicate lease time, assume one hour
+ const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
+ if (ptr)
+ {
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
+ if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
+ {
+ const rdataOPT *o;
+ const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+ for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++)
+ if (o->opt == kDNSOpt_Lease)
+ {
+ updatelease = o->u.updatelease;
+ LogSPS("Sleep Proxy granted lease time %4d seconds, updateid %d, InterfaceID %p", updatelease, mDNSVal16(msg->h.id), InterfaceID);
+ }
+ }
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ }
+
+ if (m->CurrentRecord)
+ LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *const rr = m->CurrentRecord;
+ if (rr->resrec.InterfaceID == InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
+ if (mDNSSameOpaque16(rr->updateid, msg->h.id))
+ {
+ // We successfully completed this record's registration on this "InterfaceID". Clear that bit.
+ // Clear the updateid when we are done sending on all interfaces.
+ mDNSu32 scopeid = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNStrue);
+ if (scopeid < (sizeof(rr->updateIntID) * mDNSNBBY))
+ bit_clr_opaque64(rr->updateIntID, scopeid);
+ if (mDNSOpaque64IsZero(&rr->updateIntID))
+ rr->updateid = zeroID;
+ rr->expire = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond);
+ LogSPS("Sleep Proxy %s record %5d 0x%x 0x%x (%d) %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, rr->updateIntID.l[1], rr->updateIntID.l[0], mDNSVal16(rr->updateid), ARDisplayString(m,rr));
+ if (rr->WakeUp.HMAC.l[0])
+ {
+ rr->WakeUp.HMAC = zeroEthAddr; // Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
+ rr->RequireGoodbye = mDNSfalse; // and we don't want to send goodbye for it
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+ }
+ }
+ // Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+ // new records could have been added to the end of the list as a result of that call.
+ if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+ m->CurrentRecord = rr->next;
+ }
+
+ // Update the dynamic store with the IP Address and MAC address of the sleep proxy
+ char *ifname = InterfaceNameForID(m, InterfaceID);
+ mDNSAddr spsaddr;
+ mDNSPlatformMemCopy(&spsaddr, srcaddr, sizeof (mDNSAddr));
+ mDNSPlatformStoreSPSMACAddr(&spsaddr, ifname);
+ }
+ // If we were waiting to go to sleep, then this SPS registration or wide-area record deletion
+ // may have been the thing we were waiting for, so schedule another check to see if we can sleep now.
+ if (m->SleepLimit) m->NextScheduledSPRetry = m->timenow;
+}
+
+mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr,
+ const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver)
+{
+ if (cr == &m->rec.r && m->rec.r.resrec.RecordType)
+ {
+ LogMsg("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
+#if ForceAlerts
+ *(long*)0 = 0;
+#endif
+ }
+
+ // Create empty resource record
+ cr->resrec.RecordType = kDNSRecordTypePacketNegative;
+ cr->resrec.InterfaceID = InterfaceID;
+ cr->resrec.rDNSServer = dnsserver;
+ cr->resrec.name = name; // Will be updated to point to cg->name when we call CreateNewCacheEntry
+ cr->resrec.rrtype = rrtype;
+ cr->resrec.rrclass = rrclass;
+ cr->resrec.rroriginalttl = ttl_seconds;
+ cr->resrec.rdlength = 0;
+ cr->resrec.rdestimate = 0;
+ cr->resrec.namehash = namehash;
+ cr->resrec.rdatahash = 0;
+ cr->resrec.rdata = (RData*)&cr->smallrdatastorage;
+ cr->resrec.rdata->MaxRDLength = 0;
+
+ cr->NextInKAList = mDNSNULL;
+ cr->TimeRcvd = m->timenow;
+ cr->DelayDelivery = 0;
+ cr->NextRequiredQuery = m->timenow;
+ cr->LastUsed = m->timenow;
+ cr->CRActiveQuestion = mDNSNULL;
+ cr->UnansweredQueries = 0;
+ cr->LastUnansweredTime = 0;
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+ cr->MPUnansweredQ = 0;
+ cr->MPLastUnansweredQT = 0;
+ cr->MPUnansweredKA = 0;
+ cr->MPExpectingKA = mDNSfalse;
+#endif
+ cr->NextInCFList = mDNSNULL;
+ cr->nsec = mDNSNULL;
+ cr->soa = mDNSNULL;
+ cr->CRDNSSECQuestion = 0;
+ // Initialize to the basic one and the caller can set it to more
+ // specific based on the response if any
+ cr->responseFlags = ResponseFlags;
+}
+
+mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
+ const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport,
+ const mDNSInterfaceID InterfaceID)
+{
+ mDNSInterfaceID ifid = InterfaceID;
+ DNSMessage *msg = (DNSMessage *)pkt;
+ const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 UpdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_Update;
+ const mDNSu8 UpdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
+ mDNSu8 QR_OP;
+ mDNSu8 *ptr = mDNSNULL;
+ mDNSBool TLS = (dstaddr == (mDNSAddr *)1); // For debug logs: dstaddr = 0 means TCP; dstaddr = 1 means TLS
+ if (TLS) dstaddr = mDNSNULL;
+
+#ifndef UNICAST_DISABLED
+ if (mDNSSameAddress(srcaddr, &m->Router))
+ {
+#ifdef _LEGACY_NAT_TRAVERSAL_
+ if (mDNSSameIPPort(srcport, SSDPPort) || (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port)))
+ {
+ mDNS_Lock(m);
+ LNT_ConfigureRouterInfo(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
+ mDNS_Unlock(m);
+ return;
+ }
+#endif
+ if (mDNSSameIPPort(srcport, NATPMPPort))
+ {
+ mDNS_Lock(m);
+ uDNS_ReceiveNATPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
+ mDNS_Unlock(m);
+ return;
+ }
+ }
+#ifdef _LEGACY_NAT_TRAVERSAL_
+ else if (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port)) { debugf("Ignoring SSDP response from %#a:%d", srcaddr, mDNSVal16(srcport)); return; }
+#endif
+
+#endif
+ if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader))
+ {
+ LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
+ return;
+ }
+ QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+ // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+ ptr = (mDNSu8 *)&msg->h.numQuestions;
+ msg->h.numQuestions = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
+ msg->h.numAnswers = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
+ msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
+ msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
+
+ if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
+
+ // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
+ // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
+ if (srcaddr && !mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; }
+
+ mDNS_Lock(m);
+ m->PktNum++;
+ if (mDNSOpaque16IsZero(msg->h.id))
+ {
+ m->MPktNum++;
+#if APPLE_OSX_mDNSResponder
+ // Track the number of multicast packets received from a source outside our subnet.
+ // Check the destination address to avoid accounting for spurious packets that
+ // comes in with message id zero.
+ if (!mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr, mDNSNULL) &&
+ mDNSAddressIsAllDNSLinkGroup(dstaddr))
+ {
+ m->RemoteSubnet++;
+ }
+#endif // #if APPLE_OSX_mDNSResponder
+ }
+
+#ifndef UNICAST_DISABLED
+ if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR)))
+ if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses
+ {
+ ifid = mDNSInterface_Any;
+ if (mDNS_PacketLoggingEnabled)
+ DumpPacket(m, mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
+ uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport);
+ // Note: mDNSCore also needs to get access to received unicast responses
+ }
+#endif
+ if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
+ else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
+ else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
+ else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end, 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);
+ }
+ }
+ }
+ // Packet reception often causes a change to the task list:
+ // 1. Inbound queries can cause us to need to send responses
+ // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
+ // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
+ // 4. Response packets that answer questions may cause our client to issue new questions
+ mDNS_Unlock(m);
+}