+mDNSlocal void handle_unanswered_query(mDNS *const m)
+{
+ DNSQuestion *q = m->CurrentQuestion;
+
+ if (q->unansweredQueries >= MAX_DNSSEC_UNANSWERED_QUERIES && DNSSECOptionalQuestion(q))
+ {
+ // If we are not receiving any responses for DNSSEC question, it could be due to
+ // a broken middlebox or a DNS server that does not understand the EDNS0/DOK option that
+ // silently drops the packets. Also as per RFC 5625 there are certain buggy DNS Proxies
+ // that are known to drop these pkts. To handle this, we turn off sending the EDNS0/DOK
+ // option if we have not received any responses indicating that the server or
+ // the middlebox is DNSSEC aware. If we receive at least one response to a DNSSEC
+ // question, we don't turn off validation. Also, we wait for MAX_DNSSEC_RETRANSMISSIONS
+ // before turning off validation to accomodate packet loss.
+ //
+ // Note: req_DO affects only DNSSEC_VALIDATION_SECURE_OPTIONAL questions;
+ // DNSSEC_VALIDATION_SECURE questions ignores req_DO.
+
+ if (q->qDNSServer && !q->qDNSServer->DNSSECAware && q->qDNSServer->req_DO)
+ {
+ q->qDNSServer->retransDO++;
+ if (q->qDNSServer->retransDO == MAX_DNSSEC_RETRANSMISSIONS)
+ {
+ LogInfo("handle_unanswered_query: setting req_DO false for %#a", &q->qDNSServer->addr);
+ q->qDNSServer->req_DO = mDNSfalse;
+ }
+ }
+
+ if (!q->qDNSServer->req_DO)
+ {
+ q->ValidationState = DNSSECValNotRequired;
+ q->ValidationRequired = DNSSEC_VALIDATION_NONE;
+
+ if (q->ProxyQuestion)
+ q->ProxyDNSSECOK = mDNSfalse;
+ LogInfo("handle_unanswered_query: unanswered query for %##s (%s), so turned off validation for %#a",
+ q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr);
+ }
+ }
+}
+
+// The question to be checked is not passed in as an explicit parameter;
+// instead it is implicit that the question to be checked is m->CurrentQuestion.
+mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
+{
+ DNSQuestion *q = m->CurrentQuestion;
+ if (m->timenow - NextQSendTime(q) < 0) return;
+
+ 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)
+ }
+ }
+
+ 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))
+ {
+ if (q->unansweredQueries >= MAX_UCAST_UNANSWERED_QUERIES)
+ {
+ DNSServer *orig = q->qDNSServer;
+ if (orig)
+ LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)",
+ q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c);
+
+ PenalizeDNSServer(m, q, zeroID);
+ q->noServerResponse = 1;
+ }
+ // There are two cases here.
+ //
+ // 1. We have only one DNS server for this question. It is not responding even after we sent MAX_UCAST_UNANSWERED_QUERIES.
+ // In that case, we need to keep retrying till we get a response. But we need to backoff as we retry. We set
+ // noServerResponse in the block above and below we do not touch the question interval. When we come here, we
+ // already waited for the response. We need to send another query right at this moment. We do that below by
+ // reinitializing dns servers and reissuing the query.
+ //
+ // 2. We have more than one DNS server. If at least one server did not respond, we would have set noServerResponse
+ // either now (the last server in the list) or before (non-last server in the list). In either case, if we have
+ // reached the end of DNS server list, we need to try again from the beginning. Ideally we should try just the
+ // servers that did not respond, but for simplicity we try all the servers. Once we reached the end of list, we
+ // set triedAllServersOnce so that we don't try all the servers aggressively. See PenalizeDNSServer.
+ if (!q->qDNSServer && q->noServerResponse)
+ {
+ DNSServer *new;
+ DNSQuestion *qptr;
+ q->triedAllServersOnce = 1;
+ // Re-initialize all DNS servers for this question. If we have a DNSServer, DNSServerChangeForQuestion will
+ // handle all the work including setting the new DNS server.
+ SetValidDNSServers(m, q);
+ new = GetServerForQuestion(m, q);
+ if (new)
+ {
+ LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%d ThisQInterval %d",
+ q, q->qname.c, DNSTypeName(q->qtype), new ? &new->addr : mDNSNULL, mDNSVal16(new ? new->port : zeroIPPort), q->ThisQInterval);
+ DNSServerChangeForQuestion(m, q, new);
+ }
+ 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)
+ {
+ mDNSu8 *end = m->omsg.data;
+ 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
+ {
+ 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 (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q)))
+ {
+ //LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype));
+ if (private)
+ {
+ if (q->nta) CancelGetZoneData(m, q->nta);
+ q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q);
+ if (q->state == LLQ_Poll) q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep;
+ }
+ else
+ {
+ debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d",
+ q, q->qname.c, DNSTypeName(q->qtype),
+ q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries);
+ if (!q->LocalSocket)
+ {
+ q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+ if (q->LocalSocket)
+ mDNSPlatformSetDelegatePID(q->LocalSocket, &q->qDNSServer->addr, q);
+ }
+ if (!q->LocalSocket) err = mStatus_NoMemoryErr; // If failed to make socket (should be very rare), we'll try again next time
+ else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL, q->UseBackgroundTrafficClass);
+ }
+ }
+
+ if (err != mStatus_TransientErr) // if it is not a transient error backoff and DO NOT flood queries unnecessarily
+ {
+ 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)
+ {
+ // 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;
+ SetNextQueryTime(m, q);
+ }
+ else
+ {
+ // If we have no server for this query, or the only server is a disabled one, then we deliver
+ // a transient failure indication to the client. This is important for things like iPhone
+ // where we want to return timely feedback to the user when no network is available.
+ // After calling MakeNegativeCacheRecord() we store the resulting record in the
+ // cache so that it will be visible to other clients asking the same question.
+ // (When we have a group of identical questions, only the active representative of the group gets
+ // passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire --
+ // but we want *all* of the questions to get answer callbacks.)
+ CacheRecord *rr;
+ const mDNSu32 slot = HashSlot(&q->qname);
+ CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+
+ if (!q->qDNSServer)
+ {
+ if (!mDNSOpaque64IsZero(&q->validDNSServers))
+ LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x for question %##s (%s)",
+ q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype));
+ // If we reached the end of list while picking DNS servers, then we don't want to deactivate the
+ // question. Try after 60 seconds. We find this by looking for valid DNSServers for this question,
+ // if we find any, then we must have tried them before we came here. This avoids maintaining
+ // another state variable to see if we had valid DNS servers for this question.
+ SetValidDNSServers(m, q);
+ if (mDNSOpaque64IsZero(&q->validDNSServers))
+ {
+ LogInfo("uDNS_CheckCurrentQuestion: no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ q->ThisQInterval = 0;
+ }
+ else
+ {
+ DNSQuestion *qptr;
+ // Pretend that we sent this question. As this is an ActiveQuestion, the NextScheduledQuery should
+ // be set properly. Also, we need to properly backoff in cases where we don't set the question to
+ // MaxQuestionInterval when we answer the question e.g., LongLived, we need to keep backing off
+ q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep;
+ q->LastQTime = m->timenow;
+ SetNextQueryTime(m, q);
+ // Pick a new DNS server now. Otherwise, when the cache is 80% of its expiry, we will try
+ // to send a query and come back to the same place here and log the above message.
+ q->qDNSServer = GetServerForQuestion(m, q);
+ for (qptr = q->next ; qptr; qptr = qptr->next)
+ if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; }
+ LogInfo("uDNS_checkCurrentQuestion: Tried all DNS servers, retry question %p SuppressUnusable %d %##s (%s) with DNS Server %#a:%d after 60 seconds, ThisQInterval %d",
+ q, q->SuppressUnusable, q->qname.c, DNSTypeName(q->qtype),
+ q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->ThisQInterval);
+ }
+ }
+ else
+ {
+ q->ThisQInterval = 0;
+ LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c);
+ }
+
+ if (cg)
+ {
+ for (rr = cg->members; rr; rr=rr->next)
+ {
+ if (SameNameRecordAnswersQuestion(&rr->resrec, q))
+ {
+ LogInfo("uDNS_CheckCurrentQuestion: Purged resourcerecord %s", CRDisplayString(m, rr));
+ mDNS_PurgeCacheResourceRecord(m, rr);
+ }
+ }
+ }
+ // For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers
+ // don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try
+ // every fifteen minutes in that case
+ MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (DomainEnumQuery(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer);
+ q->unansweredQueries = 0;
+ if (!mDNSOpaque16IsZero(q->responseFlags))
+ m->rec.r.responseFlags = q->responseFlags;
+ // We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list.
+ // To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we
+ // momentarily defer generating answer callbacks until mDNS_Execute time.
+ CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow), mDNStrue, mDNSNULL);
+ ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow));
+ m->rec.r.responseFlags = zeroID;
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ // MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it
+ }
+ }
+}
+
+mDNSexport void CheckNATMappings(mDNS *m)
+{
+ mStatus err = mStatus_NoError;
+ mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4);
+ mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4);
+ m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF;
+
+ if (HaveRoutable) m->ExtAddress = m->AdvertisedV4.ip.v4;
+
+ if (m->NATTraversals && rfc1918) // Do we need to open a socket to receive multicast announcements from router?
+ {
+ if (m->NATMcastRecvskt == mDNSNULL) // If we are behind a NAT and the socket hasn't been opened yet, open it
+ {
+ // we need to log a message if we can't get our socket, but only the first time (after success)
+ static mDNSBool needLog = mDNStrue;
+ m->NATMcastRecvskt = mDNSPlatformUDPSocket(m, NATPMPAnnouncementPort);
+ if (!m->NATMcastRecvskt)
+ {
+ if (needLog)
+ {
+ LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for PCP & NAT-PMP announcements");
+ needLog = mDNSfalse;
+ }
+ }
+ else
+ needLog = mDNStrue;
+ }
+ }
+ else // else, we don't want to listen for announcements, so close them if they're open
+ {
+ if (m->NATMcastRecvskt) { mDNSPlatformUDPClose(m->NATMcastRecvskt); m->NATMcastRecvskt = mDNSNULL; }
+ if (m->SSDPSocket) { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
+ }
+
+ uDNS_RequestAddress(m);
+
+ if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use");
+ m->CurrentNATTraversal = m->NATTraversals;
+
+ while (m->CurrentNATTraversal)
+ {
+ NATTraversalInfo *cur = m->CurrentNATTraversal;
+ mDNSv4Addr EffectiveAddress = HaveRoutable ? m->AdvertisedV4.ip.v4 : cur->NewAddress;
+ m->CurrentNATTraversal = m->CurrentNATTraversal->next;
+
+ if (HaveRoutable) // If not RFC 1918 address, our own address and port are effectively our external address and port
+ {
+ cur->ExpiryTime = 0;
+ cur->NewResult = mStatus_NoError;
+ }
+ else // Check if it's time to send port mapping packet(s)
+ {
+ if (m->timenow - cur->retryPortMap >= 0) // Time to send a mapping request for this packet
+ {
+ if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0) // Mapping has expired
+ {
+ cur->ExpiryTime = 0;
+ cur->retryInterval = NATMAP_INIT_RETRY;
+ }
+
+ err = uDNS_SendNATMsg(m, cur, mDNStrue); // Will also do UPnP discovery for us, if necessary
+
+ if (cur->ExpiryTime) // If have active mapping then set next renewal time halfway to expiry
+ NATSetNextRenewalTime(m, cur);
+ else // else no mapping; use exponential backoff sequence
+ {
+ if (cur->retryInterval < NATMAP_INIT_RETRY ) cur->retryInterval = NATMAP_INIT_RETRY;
+ else if (cur->retryInterval < NATMAP_MAX_RETRY_INTERVAL / 2) cur->retryInterval *= 2;
+ else cur->retryInterval = NATMAP_MAX_RETRY_INTERVAL;
+ cur->retryPortMap = m->timenow + cur->retryInterval;
+ }
+ }
+
+ if (m->NextScheduledNATOp - cur->retryPortMap > 0)
+ {
+ m->NextScheduledNATOp = cur->retryPortMap;
+ }
+ }
+
+ // Notify the client if necessary. We invoke the callback if:
+ // (1) We have an effective address,
+ // or we've tried and failed a couple of times to discover it
+ // AND
+ // (2) the client requested the address only,
+ // or the client won't need a mapping because we have a routable address,
+ // or the client has an expiry time and therefore a successful mapping,
+ // or we've tried and failed a couple of times (see "Time line" below)
+ // AND
+ // (3) we have new data to give the client that's changed since the last callback
+ //
+ // Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send
+ // At this point we've sent three requests without an answer, we've just sent our fourth request,
+ // retryInterval is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds),
+ // so we return an error result to the caller.
+ if (!mDNSIPv4AddressIsZero(EffectiveAddress) || cur->retryInterval > NATMAP_INIT_RETRY * 8)
+ {
+ 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) ||
+ !mDNSSameIPPort (cur->ExternalPort, ExternalPort) ||
+ cur->Result != EffectiveResult)
+ {
+ //LogMsg("NAT callback %d %d %d", cur->Protocol, cur->ExpiryTime, cur->retryInterval);
+ if (cur->Protocol && mDNSIPPortIsZero(ExternalPort) && !mDNSIPv4AddressIsZero(m->Router.ip.v4))
+ {
+ if (!EffectiveResult)
+ LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
+ cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
+ else
+ LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
+ cur, &m->Router, &EffectiveAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
+ }
+
+ cur->ExternalAddress = EffectiveAddress;
+ cur->ExternalPort = ExternalPort;
+ cur->Lifetime = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ?
+ (cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0;
+ cur->Result = EffectiveResult;
+ mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
+ if (cur->clientCallback)
+ cur->clientCallback(m, cur);
+ mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
+ // MUST NOT touch cur after invoking the callback
+ }
+ }
+ }
+ }
+}
+
+mDNSlocal mDNSs32 CheckRecordUpdates(mDNS *m)
+{
+ AuthRecord *rr;
+ mDNSs32 nextevent = m->timenow + 0x3FFFFFFF;
+
+ CheckGroupRecordUpdates(m);
+
+ for (rr = m->ResourceRecords; rr; rr = rr->next)
+ {
+ if (!AuthRecord_uDNS(rr)) continue;
+ if (rr->state == regState_NoTarget) {debugf("CheckRecordUpdates: Record %##s in NoTarget", rr->resrec.name->c); continue;}
+ // While we are waiting for the port mapping, we have nothing to do. The port mapping callback
+ // will take care of this
+ if (rr->state == regState_NATMap) {debugf("CheckRecordUpdates: Record %##s in NATMap", rr->resrec.name->c); continue;}
+ if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending ||
+ rr->state == regState_Refresh || rr->state == regState_Registered)
+ {
+ if (rr->LastAPTime + rr->ThisAPInterval - m->timenow <= 0)
+ {
+ if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+ if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
+ {
+ // Zero out the updateid so that if we have a pending response from the server, it won't
+ // be accepted as a valid response. If we accept the response, we might free the new "nta"
+ if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); }
+ rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr);
+
+ // We have just started the GetZoneData. We need to wait for it to finish. SetRecordRetry here
+ // schedules the update timer to fire in the future.
+ //
+ // There are three cases.
+ //
+ // 1) When the updates are sent the first time, the first retry is intended to be at three seconds
+ // in the future. But by calling SetRecordRetry here we set it to nine seconds. But it does not
+ // matter because when the answer comes back, RecordRegistrationGotZoneData resets the interval
+ // back to INIT_RECORD_REG_INTERVAL. This also gives enough time for the query.
+ //
+ // 2) In the case of update errors (updateError), this causes further backoff as
+ // RecordRegistrationGotZoneData does not reset the timer. This is intentional as in the case of
+ // errors, we don't want to update aggressively.
+ //
+ // 3) We might be refreshing the update. This is very similar to case (1). RecordRegistrationGotZoneData
+ // resets it back to INIT_RECORD_REG_INTERVAL.
+ //
+ SetRecordRetry(m, rr, 0);
+ }
+ else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr);
+ else SendRecordRegistration(m, rr);
+ }
+ }
+ if (nextevent - (rr->LastAPTime + rr->ThisAPInterval) > 0)
+ nextevent = (rr->LastAPTime + rr->ThisAPInterval);
+ }
+ return nextevent;
+}
+
+mDNSexport void uDNS_Tasks(mDNS *const m)
+{
+ mDNSs32 nexte;
+ DNSServer *d;
+
+ m->NextuDNSEvent = m->timenow + 0x3FFFFFFF;
+
+ nexte = CheckRecordUpdates(m);
+ if (m->NextuDNSEvent - nexte > 0)
+ m->NextuDNSEvent = nexte;
+
+ for (d = m->DNSServers; d; d=d->next)
+ if (d->penaltyTime)
+ {
+ if (m->timenow - d->penaltyTime >= 0)
+ {
+ LogInfo("DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port));
+ d->penaltyTime = 0;
+ }
+ else
+ if (m->NextuDNSEvent - d->penaltyTime > 0)
+ m->NextuDNSEvent = d->penaltyTime;
+ }
+
+ if (m->CurrentQuestion)
+ LogMsg("uDNS_Tasks ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ {
+ DNSQuestion *const q = m->CurrentQuestion;
+ if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID))
+ {
+ uDNS_CheckCurrentQuestion(m);
+ if (q == m->CurrentQuestion)
+ if (m->NextuDNSEvent - NextQSendTime(q) > 0)
+ m->NextuDNSEvent = NextQSendTime(q);
+ }
+ // If m->CurrentQuestion wasn't modified out from under us, advance it now
+ // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion()
+ // depends on having m->CurrentQuestion point to the right question
+ if (m->CurrentQuestion == q)
+ m->CurrentQuestion = q->next;
+ }
+ m->CurrentQuestion = mDNSNULL;
+}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Startup, Shutdown, and Sleep
+#endif
+
+mDNSexport void SleepRecordRegistrations(mDNS *m)
+{
+ AuthRecord *rr;
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ {
+ if (AuthRecord_uDNS(rr))
+ {
+ // Zero out the updateid so that if we have a pending response from the server, it won't
+ // be accepted as a valid response.
+ if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; }
+
+ if (rr->NATinfo.clientContext)
+ {
+ mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+ rr->NATinfo.clientContext = mDNSNULL;
+ }
+ // We are waiting to update the resource record. The original data of the record is
+ // in OrigRData and the updated value is in InFlightRData. Free the old and the new
+ // one will be registered when we come back.
+ if (rr->state == regState_UpdatePending)
+ {
+ // act as if the update succeeded, since we're about to delete the name anyway
+ rr->state = regState_Registered;
+ // deallocate old RData
+ if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen);
+ SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
+ rr->OrigRData = mDNSNULL;
+ rr->InFlightRData = mDNSNULL;
+ }
+
+ // If we have not begun the registration process i.e., never sent a registration packet,
+ // then uDNS_DeregisterRecord will not send a deregistration
+ uDNS_DeregisterRecord(m, rr);
+
+ // When we wake, we call ActivateUnicastRegistration which starts at StartGetZoneData
+ }
+ }
+}
+
+mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID)
+{
+ SearchListElem **p;
+ SearchListElem *tmp = mDNSNULL;
+
+ // Check to see if we already have this domain in our list
+ for (p = &SearchList; *p; p = &(*p)->next)
+ if (((*p)->InterfaceID == InterfaceID) && SameDomainName(&(*p)->domain, domain))
+ {
+ // If domain is already in list, and marked for deletion, unmark the delete
+ // Be careful not to touch the other flags that may be present
+ LogInfo("mDNS_AddSearchDomain already in list %##s", domain->c);
+ if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE;
+ tmp = *p;
+ *p = tmp->next;
+ tmp->next = mDNSNULL;
+ break;
+ }
+
+
+ // move to end of list so that we maintain the same order
+ while (*p) p = &(*p)->next;
+
+ if (tmp) *p = tmp;
+ else
+ {
+ // if domain not in list, add to list, mark as add (1)
+ *p = mDNSPlatformMemAllocate(sizeof(SearchListElem));
+ if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; }
+ mDNSPlatformMemZero(*p, sizeof(SearchListElem));
+ AssignDomainName(&(*p)->domain, domain);
+ (*p)->next = mDNSNULL;
+ (*p)->InterfaceID = InterfaceID;
+ LogInfo("mDNS_AddSearchDomain created new %##s, InterfaceID %p", domain->c, InterfaceID);
+ }
+}
+
+mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+{
+ (void)m; // unused
+ if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext);
+}
+
+mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+{
+ SearchListElem *slElem = question->QuestionContext;
+ mStatus err;
+ const char *name;
+
+ if (answer->rrtype != kDNSType_PTR) return;
+ if (answer->RecordType == kDNSRecordTypePacketNegative) return;
+ if (answer->InterfaceID == mDNSInterface_LocalOnly) return;
+
+ if (question == &slElem->BrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse];
+ else if (question == &slElem->DefBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault];
+ else if (question == &slElem->AutomaticBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic];
+ else if (question == &slElem->RegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration];
+ else if (question == &slElem->DefRegisterQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault];
+ else { LogMsg("FoundDomain - unknown question"); return; }
+
+ LogInfo("FoundDomain: %p %s %s Q %##s A %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", name, question->qname.c, RRDisplayString(m, answer));
+
+ if (AddRecord)
+ {
+ ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem));
+ if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; }
+ mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem);
+ MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name);
+ AppendDNSNameString (&arElem->ar.namestorage, "local");
+ AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name);
+ LogInfo("FoundDomain: Registering %s", ARDisplayString(m, &arElem->ar));
+ err = mDNS_Register(m, &arElem->ar);
+ if (err) { LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); mDNSPlatformMemFree(arElem); return; }
+ arElem->next = slElem->AuthRecs;
+ slElem->AuthRecs = arElem;
+ }
+ else
+ {
+ ARListElem **ptr = &slElem->AuthRecs;
+ while (*ptr)
+ {
+ if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, &answer->rdata->u.name))
+ {
+ ARListElem *dereg = *ptr;
+ *ptr = (*ptr)->next;
+ LogInfo("FoundDomain: Deregistering %s", ARDisplayString(m, &dereg->ar));
+ err = mDNS_Deregister(m, &dereg->ar);
+ if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err);
+ // Memory will be freed in the FreeARElemCallback
+ }
+ else
+ ptr = &(*ptr)->next;
+ }
+ }
+}
+
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+mDNSexport void udns_validatelists(void *const v)
+{
+ mDNS *const m = v;
+
+ NATTraversalInfo *n;
+ for (n = m->NATTraversals; n; n=n->next)
+ if (n->next == (NATTraversalInfo *)~0 || n->clientCallback == (NATTraversalClientCallback) ~0)
+ LogMemCorruption("m->NATTraversals: %p is garbage", n);
+
+ 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);
+
+ DomainAuthInfo *info;
+ for (info = m->AuthInfoList; info; info = info->next)
+ if (info->next == (DomainAuthInfo *)~0)
+ LogMemCorruption("m->AuthInfoList: %p is garbage", info);
+
+ HostnameInfo *hi;
+ for (hi = m->Hostnames; hi; hi = hi->next)
+ if (hi->next == (HostnameInfo *)~0 || hi->StatusCallback == (mDNSRecordCallback*)~0)
+ LogMemCorruption("m->Hostnames: %p is garbage", n);
+
+ SearchListElem *ptr;
+ for (ptr = SearchList; ptr; ptr = ptr->next)
+ if (ptr->next == (SearchListElem *)~0 || ptr->AuthRecs == (void*)~0)
+ LogMemCorruption("SearchList: %p is garbage (%X)", ptr, ptr->AuthRecs);
+}
+#endif
+
+// This should probably move to the UDS daemon -- the concept of legacy clients and automatic registration / automatic browsing
+// is really a UDS API issue, not something intrinsic to uDNS
+
+mDNSlocal void uDNS_DeleteWABQueries(mDNS *const m, SearchListElem *ptr, int delete)
+{
+ const char *name1 = mDNSNULL;
+ const char *name2 = mDNSNULL;
+ ARListElem **arList = &ptr->AuthRecs;
+ domainname namestorage1, namestorage2;
+ mStatus err;
+
+ // "delete" parameter indicates the type of query.
+ switch (delete)
+ {
+ case UDNS_WAB_BROWSE_QUERY:
+ mDNS_StopGetDomains(m, &ptr->BrowseQ);
+ mDNS_StopGetDomains(m, &ptr->DefBrowseQ);
+ name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse];
+ name2 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault];
+ break;
+ case UDNS_WAB_LBROWSE_QUERY:
+ mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
+ name1 = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic];
+ break;
+ case UDNS_WAB_REG_QUERY:
+ mDNS_StopGetDomains(m, &ptr->RegisterQ);
+ mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
+ name1 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration];
+ name2 = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault];
+ break;
+ default:
+ LogMsg("uDNS_DeleteWABQueries: ERROR!! returning from default");
+ return;
+ }
+ // When we get the results to the domain enumeration queries, we add a LocalOnly
+ // entry. For example, if we issue a domain enumeration query for b._dns-sd._udp.xxxx.com,
+ // and when we get a response, we add a LocalOnly entry b._dns-sd._udp.local whose RDATA
+ // points to what we got in the response. Locate the appropriate LocalOnly entries and delete
+ // them.
+ if (name1)
+ {
+ MakeDomainNameFromDNSNameString(&namestorage1, name1);
+ AppendDNSNameString(&namestorage1, "local");
+ }
+ if (name2)
+ {
+ MakeDomainNameFromDNSNameString(&namestorage2, name2);
+ AppendDNSNameString(&namestorage2, "local");
+ }
+ while (*arList)
+ {
+ ARListElem *dereg = *arList;
+ if ((name1 && SameDomainName(&dereg->ar.namestorage, &namestorage1)) ||
+ (name2 && SameDomainName(&dereg->ar.namestorage, &namestorage2)))
+ {
+ LogInfo("uDNS_DeleteWABQueries: Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
+ *arList = dereg->next;
+ err = mDNS_Deregister(m, &dereg->ar);
+ if (err) LogMsg("uDNS_DeleteWABQueries:: ERROR!! mDNS_Deregister returned %d", err);
+ // Memory will be freed in the FreeARElemCallback
+ }
+ else
+ {
+ LogInfo("uDNS_DeleteWABQueries: Skipping PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
+ arList = &(*arList)->next;
+ }
+ }
+}
+
+mDNSexport void uDNS_SetupWABQueries(mDNS *const m)
+{
+ SearchListElem **p = &SearchList, *ptr;
+ mStatus err;
+ int action = 0;
+
+ // step 1: mark each element for removal
+ for (ptr = SearchList; ptr; ptr = ptr->next)
+ ptr->flag |= SLE_DELETE;
+
+ // Make sure we have the search domains from the platform layer so that if we start the WAB
+ // queries below, we have the latest information.
+ mDNS_Lock(m);
+ if (!mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL, mDNSfalse))
+ {
+ // If the configuration did not change, clear the flag so that we don't free the searchlist.
+ // We still have to start the domain enumeration queries as we may not have started them
+ // before.
+ for (ptr = SearchList; ptr; ptr = ptr->next)
+ ptr->flag &= ~SLE_DELETE;
+ LogInfo("uDNS_SetupWABQueries: No config change");
+ }
+ mDNS_Unlock(m);
+
+ if (m->WABBrowseQueriesCount)
+ action |= UDNS_WAB_BROWSE_QUERY;
+ if (m->WABLBrowseQueriesCount)
+ action |= UDNS_WAB_LBROWSE_QUERY;
+ if (m->WABRegQueriesCount)
+ action |= UDNS_WAB_REG_QUERY;
+
+
+ // delete elems marked for removal, do queries for elems marked add
+ while (*p)
+ {
+ ptr = *p;
+ LogInfo("uDNS_SetupWABQueries:action 0x%x: Flags 0x%x, AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c);
+ // If SLE_DELETE is set, stop all the queries, deregister all the records and free the memory.
+ // Otherwise, check to see what the "action" requires. If a particular action bit is not set and
+ // we have started the corresponding queries as indicated by the "flags", stop those queries and
+ // deregister the records corresponding to them.
+ if ((ptr->flag & SLE_DELETE) ||
+ (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED)) ||
+ (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED)) ||
+ (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED)))
+ {
+ if (ptr->flag & SLE_DELETE)
+ {
+ ARListElem *arList = ptr->AuthRecs;
+ ptr->AuthRecs = mDNSNULL;
+ *p = ptr->next;
+
+ // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
+ // We suppressed the domain enumeration for scoped search domains below. When we enable that
+ // enable this.
+ if ((ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) &&
+ !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ LogInfo("uDNS_SetupWABQueries: DELETE Browse for domain %##s", ptr->domain.c);
+ mDNS_StopGetDomains(m, &ptr->BrowseQ);
+ mDNS_StopGetDomains(m, &ptr->DefBrowseQ);
+ }
+ if ((ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) &&
+ !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ LogInfo("uDNS_SetupWABQueries: DELETE Legacy Browse for domain %##s", ptr->domain.c);
+ mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
+ }
+ if ((ptr->flag & SLE_WAB_REG_QUERY_STARTED) &&
+ !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ LogInfo("uDNS_SetupWABQueries: DELETE Registration for domain %##s", ptr->domain.c);
+ mDNS_StopGetDomains(m, &ptr->RegisterQ);
+ mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
+ }
+
+ mDNSPlatformMemFree(ptr);
+
+ // deregister records generated from answers to the query
+ while (arList)
+ {
+ ARListElem *dereg = arList;
+ arList = arList->next;
+ LogInfo("uDNS_SetupWABQueries: DELETE Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
+ err = mDNS_Deregister(m, &dereg->ar);
+ if (err) LogMsg("uDNS_SetupWABQueries:: ERROR!! mDNS_Deregister returned %d", err);
+ // Memory will be freed in the FreeARElemCallback
+ }
+ continue;
+ }
+
+ // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
+ // We suppressed the domain enumeration for scoped search domains below. When we enable that
+ // enable this.
+ if (!(action & UDNS_WAB_BROWSE_QUERY) && (ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED) &&
+ !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ LogInfo("uDNS_SetupWABQueries: Deleting Browse for domain %##s", ptr->domain.c);
+ ptr->flag &= ~SLE_WAB_BROWSE_QUERY_STARTED;
+ uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_BROWSE_QUERY);
+ }
+
+ if (!(action & UDNS_WAB_LBROWSE_QUERY) && (ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED) &&
+ !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ LogInfo("uDNS_SetupWABQueries: Deleting Legacy Browse for domain %##s", ptr->domain.c);
+ ptr->flag &= ~SLE_WAB_LBROWSE_QUERY_STARTED;
+ uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_LBROWSE_QUERY);
+ }
+
+ if (!(action & UDNS_WAB_REG_QUERY) && (ptr->flag & SLE_WAB_REG_QUERY_STARTED) &&
+ !SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ LogInfo("uDNS_SetupWABQueries: Deleting Registration for domain %##s", ptr->domain.c);
+ ptr->flag &= ~SLE_WAB_REG_QUERY_STARTED;
+ uDNS_DeleteWABQueries(m, ptr, UDNS_WAB_REG_QUERY);
+ }
+
+ // Fall through to handle the ADDs
+ }
+
+ if ((action & UDNS_WAB_BROWSE_QUERY) && !(ptr->flag & SLE_WAB_BROWSE_QUERY_STARTED))
+ {
+ // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
+ // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+ if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ mStatus err1, err2;
+ err1 = mDNS_GetDomains(m, &ptr->BrowseQ, mDNS_DomainTypeBrowse, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ if (err1)
+ {
+ LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+ "%d (mDNS_DomainTypeBrowse)\n", ptr->domain.c, err1);
+ }
+ else
+ {
+ LogInfo("uDNS_SetupWABQueries: Starting Browse for domain %##s", ptr->domain.c);
+ }
+ err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ, mDNS_DomainTypeBrowseDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ if (err2)
+ {
+ LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+ "%d (mDNS_DomainTypeBrowseDefault)\n", ptr->domain.c, err2);
+ }
+ else
+ {
+ LogInfo("uDNS_SetupWABQueries: Starting Default Browse for domain %##s", ptr->domain.c);
+ }
+ // For simplicity, we mark a single bit for denoting that both the browse queries have started.
+ // It is not clear as to why one would fail to start and the other would succeed in starting up.
+ // If that happens, we will try to stop both the queries and one of them won't be in the list and
+ // it is not a hard error.
+ if (!err1 || !err2)
+ {
+ ptr->flag |= SLE_WAB_BROWSE_QUERY_STARTED;
+ }
+ }
+ }
+ if ((action & UDNS_WAB_LBROWSE_QUERY) && !(ptr->flag & SLE_WAB_LBROWSE_QUERY_STARTED))
+ {
+ // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
+ // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+ if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ mStatus err1;
+ err1 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ if (err1)
+ {
+ LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+ "%d (mDNS_DomainTypeBrowseAutomatic)\n",
+ ptr->domain.c, err1);
+ }
+ else
+ {
+ ptr->flag |= SLE_WAB_LBROWSE_QUERY_STARTED;
+ LogInfo("uDNS_SetupWABQueries: Starting Legacy Browse for domain %##s", ptr->domain.c);
+ }
+ }
+ }
+ if ((action & UDNS_WAB_REG_QUERY) && !(ptr->flag & SLE_WAB_REG_QUERY_STARTED))
+ {
+ // If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
+ // Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+ if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+ {
+ mStatus err1, err2;
+ err1 = mDNS_GetDomains(m, &ptr->RegisterQ, mDNS_DomainTypeRegistration, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ if (err1)
+ {
+ LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+ "%d (mDNS_DomainTypeRegistration)\n", ptr->domain.c, err1);
+ }
+ else
+ {
+ LogInfo("uDNS_SetupWABQueries: Starting Registration for domain %##s", ptr->domain.c);
+ }
+ err2 = mDNS_GetDomains(m, &ptr->DefRegisterQ, mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+ if (err2)
+ {
+ LogMsg("uDNS_SetupWABQueries: GetDomains for domain %##s returned error(s):\n"
+ "%d (mDNS_DomainTypeRegistrationDefault)", ptr->domain.c, err2);
+ }
+ else
+ {
+ LogInfo("uDNS_SetupWABQueries: Starting Default Registration for domain %##s", ptr->domain.c);
+ }
+ if (!err1 || !err2)
+ {
+ ptr->flag |= SLE_WAB_REG_QUERY_STARTED;
+ }
+ }
+ }
+
+ p = &ptr->next;
+ }
+}
+
+// mDNS_StartWABQueries is called once per API invocation where normally
+// one of the bits is set.
+mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType)
+{
+ if (queryType & UDNS_WAB_BROWSE_QUERY)
+ {
+ m->WABBrowseQueriesCount++;
+ LogInfo("uDNS_StartWABQueries: Browse query count %d", m->WABBrowseQueriesCount);
+ }
+ if (queryType & UDNS_WAB_LBROWSE_QUERY)
+ {
+ m->WABLBrowseQueriesCount++;
+ LogInfo("uDNS_StartWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount);
+ }
+ if (queryType & UDNS_WAB_REG_QUERY)
+ {
+ m->WABRegQueriesCount++;
+ LogInfo("uDNS_StartWABQueries: Reg query count %d", m->WABRegQueriesCount);
+ }
+ uDNS_SetupWABQueries(m);
+}
+
+// mDNS_StopWABQueries is called once per API invocation where normally
+// one of the bits is set.
+mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType)
+{
+ if (queryType & UDNS_WAB_BROWSE_QUERY)
+ {
+ m->WABBrowseQueriesCount--;
+ LogInfo("uDNS_StopWABQueries: Browse query count %d", m->WABBrowseQueriesCount);
+ }
+ if (queryType & UDNS_WAB_LBROWSE_QUERY)
+ {
+ m->WABLBrowseQueriesCount--;
+ LogInfo("uDNS_StopWABQueries: Legacy Browse query count %d", m->WABLBrowseQueriesCount);
+ }
+ if (queryType & UDNS_WAB_REG_QUERY)
+ {
+ m->WABRegQueriesCount--;
+ LogInfo("uDNS_StopWABQueries: Reg query count %d", m->WABRegQueriesCount);
+ }
+ uDNS_SetupWABQueries(m);
+}
+
+mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+{
+ SearchListElem *p = SearchList;
+ int count = *searchIndex;
+ (void) m; // unused
+
+ if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; }
+
+ // Skip the domains that we already looked at before. Guard against "p"
+ // being NULL. When search domains change we may not set the SearchListIndex
+ // of the question to zero immediately e.g., domain enumeration query calls
+ // uDNS_SetupWABQueries which reads in the new search domain but does not
+ // restart the questions immediately. Questions are restarted as part of
+ // network change and hence temporarily SearchListIndex may be out of range.
+
+ for (; count && p; count--)
+ p = p->next;
+
+ while (p)
+ {
+ int labels = CountLabels(&p->domain);
+ if (labels > 0)
+ {
+ const domainname *d = SkipLeadingLabels(&p->domain, labels - 1);
+ if (SameDomainLabel(d->c, (const mDNSu8 *)"\x4" "arpa"))
+ {
+ LogInfo("uDNS_GetNextSearchDomain: skipping search domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ (*searchIndex)++;
+ p = p->next;
+ continue;
+ }
+ if (ignoreDotLocal && SameDomainLabel(d->c, (const mDNSu8 *)"\x5" "local"))
+ {
+ LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ (*searchIndex)++;
+ p = p->next;
+ continue;
+ }
+ }
+ // Point to the next one in the list which we will look at next time.
+ (*searchIndex)++;
+ // When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID
+ // set to mDNSInterface_Unicast. Match the unscoped entries in that case.
+ if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) ||
+ p->InterfaceID == InterfaceID)
+ {
+ LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ return &p->domain;
+ }
+ LogInfo("uDNS_GetNextSearchDomain skipping domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+ p = p->next;
+ }
+ return mDNSNULL;
+}
+
+mDNSlocal void FlushAddressCacheRecords(mDNS *const m)
+{
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *cr;
+ FORALL_CACHERECORDS(slot, cg, cr)
+ {
+ if (cr->resrec.InterfaceID) continue;
+
+ // If a resource record can answer A or AAAA, they need to be flushed so that we will
+ // deliver an ADD or RMV
+ if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
+ RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
+ {
+ LogInfo("FlushAddressCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
+ mDNS_PurgeCacheResourceRecord(m, cr);
+ }
+ }
+}
+
+// Retry questions which has seach domains appended
+mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
+{
+ DNSQuestion *q;
+ mDNSBool found = mDNSfalse;
+
+ // Check to see if there are any questions which needs search domains to be applied.
+ // If there is none, search domains can't possibly affect them.
+ for (q = m->Questions; q; q = q->next)
+ {
+ if (q->AppendSearchDomains)
+ {
+ found = mDNStrue;
+ break;
+ }
+ }
+ if (!found)
+ {
+ LogInfo("RetrySearchDomainQuestions: Questions with AppendSearchDomain not found");
+ return;
+ }
+ LogInfo("RetrySearchDomainQuestions: Question with AppendSearchDomain found %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ // Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries
+ // does this. When we restart the question, we first want to try the new search domains rather
+ // than use the entries that is already in the cache. When we appended search domains, we might
+ // have created cache entries which is no longer valid as there are new search domains now
+ mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL);
+}
+
+// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows:
+// 1) query for b._dns-sd._udp.local on LocalOnly interface
+// (.local manually generated via explicit callback)
+// 2) for each search domain (from prefs pane), query for b._dns-sd._udp.<searchdomain>.
+// 3) for each result from (2), register LocalOnly PTR record b._dns-sd._udp.local. -> <result>
+// 4) result above should generate a callback from question in (1). result added to global list
+// 5) global list delivered to client via GetSearchDomainList()
+// 6) client calls to enumerate domains now go over LocalOnly interface
+// (!!!KRS may add outgoing interface in addition)
+
+struct CompileTimeAssertionChecks_uDNS
+{
+ // Check our structures are reasonable sizes. Including overly-large buffers, or embedding
+ // other overly-large structures instead of having a pointer to them, can inadvertently
+ // cause structure sizes (and therefore memory usage) to balloon unreasonably.
+ char sizecheck_tcpInfo_t [(sizeof(tcpInfo_t) <= 9056) ? 1 : -1];
+ char sizecheck_SearchListElem[(sizeof(SearchListElem) <= 5000) ? 1 : -1];
+};
+
+#else // !UNICAST_DISABLED
+
+mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr)
+{
+ (void) m;
+ (void) rr;
+
+ return mDNSNULL;
+}
+
+mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name)
+{
+ (void) m;
+ (void) name;
+
+ return mDNSNULL;
+}
+
+mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q)
+{
+ (void) m;
+ (void) q;
+
+ return mDNSNULL;
+}
+
+mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
+{
+ (void) m;
+ (void) q;
+}
+
+mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp)
+{
+ (void) tcp;
+}
+
+mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *m, NATTraversalInfo *traversal)
+{
+ (void) m;
+ (void) traversal;
+
+ return mStatus_UnsupportedErr;
+}
+
+mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal)
+{
+ (void) m;
+ (void) traversal;
+
+ return mStatus_UnsupportedErr;
+}
+
+mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q)
+{
+ (void) m;
+ (void) q;
+}
+
+mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext)
+{
+ (void) m;
+ (void) name;
+ (void) target;
+ (void) callback;
+ (void) ZoneDataContext;
+
+ return mDNSNULL;
+}
+
+mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData)
+{
+ (void) m;
+ (void) err;
+ (void) zoneData;
+}
+
+mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+ const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion)
+{
+ (void) m;
+ (void) msg;
+ (void) end;
+ (void) srcaddr;
+ (void) srcport;
+ (void) matchQuestion;
+
+ return uDNS_LLQ_Not;
+}
+
+mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q, mDNSOpaque16 responseFlags)
+{
+ (void) m;
+ (void) q;
+ (void) responseFlags;
+}
+
+mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID)
+{
+ (void) domain;
+ (void) InterfaceID;
+}
+
+mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
+{
+ (void) m;
+}
+
+mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info, const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, mDNSBool autoTunnel)
+{
+ (void) m;
+ (void) info;
+ (void) domain;
+ (void) keyname;
+ (void) b64keydata;
+ (void) hostname;
+ (void) port;
+ (void) autoTunnel;
+
+ return mStatus_UnsupportedErr;
+}
+
+mDNSexport domainname *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+{
+ (void) m;
+ (void) InterfaceID;
+ (void) searchIndex;
+ (void) ignoreDotLocal;
+
+ return mDNSNULL;
+}
+
+mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name)
+{
+ (void) m;
+ (void) name;
+
+ return mDNSNULL;
+}
+
+mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal)
+{
+ (void) m;
+ (void) traversal;
+
+ return mStatus_UnsupportedErr;
+}
+
+mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal)
+{
+ (void) m;
+ (void) traversal;
+
+ return mStatus_UnsupportedErr;
+}
+
+mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSs32 serviceID, const mDNSAddr *addr,
+ const mDNSIPPort port, mDNSu32 scoped, mDNSu32 timeout, mDNSBool cellIntf, mDNSu16 resGroupID, mDNSBool reqA,
+ mDNSBool reqAAAA, mDNSBool reqDO)
+{
+ (void) m;
+ (void) d;
+ (void) interface;
+ (void) serviceID;
+ (void) addr;
+ (void) port;
+ (void) scoped;
+ (void) timeout;
+ (void) cellIntf;
+ (void) resGroupID;
+ (void) reqA;
+ (void) reqAAAA;
+ (void) reqDO;
+
+ return mDNSNULL;
+}
+
+mDNSexport void uDNS_SetupWABQueries(mDNS *const m)
+{
+ (void) m;
+}
+
+mDNSexport void uDNS_StartWABQueries(mDNS *const m, int queryType)
+{
+ (void) m;
+ (void) queryType;
+}
+
+mDNSexport void uDNS_StopWABQueries(mDNS *const m, int queryType)
+{
+ (void) m;
+ (void) queryType;
+}
+
+mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
+{
+ (void) m;
+ (void) fqdn;
+ (void) StatusCallback;
+ (void) StatusContext;
+}
+mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
+{
+ (void) m;
+ (void) v4addr;
+ (void) v6addr;
+ (void) router;
+}
+
+mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn)
+{
+ (void) m;
+ (void) fqdn;
+}
+
+mDNSexport void RecreateNATMappings(mDNS *const m, const mDNSu32 waitTicks)
+{
+ (void) m;
+ (void) waitTicks;
+}
+
+mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q)
+{
+ (void)q;
+
+ return mDNSfalse;
+}
+
+#endif // !UNICAST_DISABLED