return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL));
}
+#define TypeBufferSize 80
+static char *gettype(char *buffer, char *typ)
+ {
+ if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp";
+ if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; }
+ return(typ);
+ }
+
int main(int argc, char **argv)
{
DNSServiceErrorType err;
- char *dom;
+ char buffer[TypeBufferSize], *typ, *dom;
int optind;
// Extract the program name from argv[0], which by convention contains the path to this executable.
//enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL);
break;
- case 'B': {
- char buffer[64], *typ;
- typ = (argc < optind+1) ? "_http" : argv[optind+0]; // If no type argument, browse for advertised web pages
+ case 'B': typ = (argc < optind+1) ? "" : argv[optind+0];
dom = (argc < optind+2) ? "" : argv[optind+1]; // Missing domain argument is the same as empty string i.e. use system default(s)
+ typ = gettype(buffer, typ);
if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
- if (!strchr(typ, '.')) { snprintf(buffer, sizeof(buffer), "%s._tcp", typ); typ = buffer; }
printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
err = DNSServiceBrowse(&client, 0, opinterface, typ, dom, browse_reply, NULL);
break;
- }
case 'L': if (argc < optind+2) goto Fail;
+ typ = (argc < optind+2) ? "" : argv[optind+1];
dom = (argc < optind+3) ? "local" : argv[optind+2];
+ typ = gettype(buffer, typ);
if (dom[0] == '.' && dom[1] == 0) dom = "local"; // We allow '.' on the command line as a synonym for "local"
- printf("Lookup %s.%s.%s\n", argv[optind+0], argv[optind+1], dom);
- err = DNSServiceResolve(&client, 0, opinterface, argv[optind+0], argv[optind+1], dom, (DNSServiceResolveReply)resolve_reply, NULL);
+ printf("Lookup %s.%s.%s\n", argv[optind+0], typ, dom);
+ err = DNSServiceResolve(&client, 0, opinterface, argv[optind+0], typ, dom, (DNSServiceResolveReply)resolve_reply, NULL);
break;
case 'R': if (argc < optind+4) goto Fail;
- err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], NULL, argv[optind+3], argc-(optind+4), argv+(optind+4));
+ typ = (argc < optind+2) ? "" : argv[optind+1];
+ dom = (argc < optind+3) ? "" : argv[optind+2];
+ typ = gettype(buffer, typ);
+ if (dom[0] == '.' && dom[1] == 0) dom[0] = 0; // We allow '.' on the command line as a synonym for empty string
+ err = RegisterService(&client, argv[optind+0], typ, dom, NULL, argv[optind+3], argc-(optind+4), argv+(optind+4));
break;
case 'P': if (argc < optind+6) goto Fail;
include /Developer/Makefiles/pb_makefiles/platform.make
-MVERS = "mDNSResponder-161.1"
+MVERS = "mDNSResponder-164"
DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
Change History (most recent first):
$Log: DNSCommon.c,v $
+Revision 1.185 2007/10/10 20:22:03 cheshire
+Added sanity checks in mDNSSendDNSMessage -- we've seen crashes in DNSDigest_SignMessage
+apparently caused by trying to sign zero-length messages
+
Revision 1.184 2007/10/05 17:56:07 cheshire
Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
mDNSu16 numAdditionals = msg->h.numAdditionals;
mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
+ if (end <= msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
+ {
+ LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
+ return mStatus_BadParamErr;
+ }
+
// Put all the integer values in IETF byte-order (MSB first, LSB second)
*ptr++ = (mDNSu8)(numQuestions >> 8);
*ptr++ = (mDNSu8)(numQuestions & 0xFF);
Change History (most recent first):
$Log: mDNS.c,v $
+Revision 1.751 2007/10/30 23:49:41 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+LLQ state was not being transferred properly between duplicate questions
+
+Revision 1.750 2007/10/29 23:58:52 cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+Use standard "if (mDNSIPv4AddressIsOnes(....ExternalAddress))" mechanism to determine whether callback has been invoked yet
+
+Revision 1.749 2007/10/29 21:28:36 cheshire
+Change "Correcting TTL" log message to LogOperation to suppress it in customer build
+
+Revision 1.748 2007/10/29 20:02:50 cheshire
+<rdar://problem/5526813> BTMM: Wide-area records being announced via multicast
+
+Revision 1.747 2007/10/26 22:53:50 cheshire
+Made mDNS_Register_internal and mDNS_Deregister_internal use AuthRecord_uDNS macro
+instead of replicating the logic in both places
+
+Revision 1.746 2007/10/25 22:48:50 cheshire
+Added LogOperation message saying when we restart GetZoneData for record and service registrations
+
+Revision 1.745 2007/10/25 20:48:47 cheshire
+For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer'
+
+Revision 1.744 2007/10/25 20:06:14 cheshire
+Don't try to do SOA queries using private DNS (TLS over TCP) queries
+
+Revision 1.743 2007/10/25 00:12:46 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Retrigger service registrations whenever a new network interface is added
+
+Revision 1.742 2007/10/24 22:40:06 cheshire
+Renamed: RecordRegistrationCallback -> RecordRegistrationGotZoneData
+Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData
+
+Revision 1.741 2007/10/24 00:50:29 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Retrigger record registrations whenever a new network interface is added
+
+Revision 1.740 2007/10/23 00:38:03 cheshire
+When sending uDNS cache expiration query, need to increment rr->UnansweredQueries
+or code will spin sending the same cache expiration query repeatedly
+
+Revision 1.739 2007/10/22 23:46:41 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Need to clear question->nta pointer after calling CancelGetZoneData()
+
+Revision 1.738 2007/10/19 22:08:49 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.737 2007/10/18 23:06:42 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.736 2007/10/18 20:23:17 cheshire
+Moved SuspendLLQs into mDNS.c, since it's only called from one place
+
+Revision 1.735 2007/10/18 00:12:34 cheshire
+Fixed "unused variable" compiler warning
+
+Revision 1.734 2007/10/17 22:49:54 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.733 2007/10/17 22:37:23 cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
+Revision 1.732 2007/10/17 21:53:51 cheshire
+Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData
+
+Revision 1.731 2007/10/17 18:37:50 cheshire
+<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
+Further refinement: pre-increment m->CurrentRecord before calling mDNS_Deregister_internal()
+
+Revision 1.730 2007/10/16 21:16:07 cheshire
+<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
+
Revision 1.729 2007/10/05 17:56:10 cheshire
Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+#if ForceAlerts
+ if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
+#endif
+
if (ActiveQuestion(q))
{
mDNSs32 sendtime = q->LastQTime + q->ThisQInterval;
*p = rr;
}
- if (rr->resrec.InterfaceID != mDNSInterface_Any || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
+ if (!AuthRecord_uDNS(rr))
{
// For records that are not going to probe, acknowledge them right away
if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
rr->AnnounceCount = 0;
rr->state = regState_FetchingZoneData;
rr->uselease = mDNStrue;
- rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationCallback, rr);
- return rr->nta ? mStatus_NoError : mStatus_NoMemoryErr;
+ rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr);
}
#endif
// actual goodbye packets.
#ifndef UNICAST_DISABLED
- if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly && !rr->ForceMCast && !IsLocalDomain(rr->resrec.name))
- if (rr->RequireGoodbye)
- {
- if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
- rr->resrec.RecordType = kDNSRecordTypeDeregistering;
- uDNS_DeregisterRecord(m, rr);
- // At this point unconditionally we bail out
- // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
- // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
- // process and will complete asynchronously. Either way we don't need to do anything more here.
- return(mStatus_NoError);
- }
+ if (AuthRecord_uDNS(rr) && rr->RequireGoodbye)
+ {
+ if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+ rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+ uDNS_DeregisterRecord(m, rr);
+ // At this point unconditionally we bail out
+ // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
+ // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
+ // process and will complete asynchronously. Either way we don't need to do anything more here.
+ return(mStatus_NoError);
+ }
#endif UNICAST_DISABLED
if (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->LocalAnswer))
LogOperation("Sending %d%% cache expiration query for %s", 80 + 5 * rr->UnansweredQueries, CRDisplayString(m, rr));
q = rr->CRActiveQuestion;
ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID);
- if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it
- else if (!mDNSOpaque16IsZero(q->TargetQID)) q->LastQTime = m->timenow - q->ThisQInterval; // For uDNS, adjust LastQTime
+ // For uDNS queries (TargetQID non-zero) we adjust LastQTime,
+ // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly
+ if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If targeted query, mark it
+ else if (!mDNSOpaque16IsZero(q->TargetQID)) { q->LastQTime = m->timenow - q->ThisQInterval; rr->UnansweredQueries++; }
else if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID;
else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark;
}
{
AuthRecord *rr = m->CurrentRecord;
m->CurrentRecord = rr->next;
- if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing...
+ if (!AuthRecord_uDNS(rr) && rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing...
{
// 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
if (m->timenow - (rr->LastAPTime + rr->ThisAPInterval) < 0)
return(m->NextScheduledEvent);
}
+mDNSlocal void SuspendLLQs(mDNS *m)
+ {
+ DNSQuestion *q;
+ for (q = m->Questions; q; q = q->next)
+ if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived)
+ {
+ // If necessary, tell server it can delete this LLQ state
+ if (q->state == LLQ_Established) sendLLQRefresh(m, q, 0);
+ if (q->nta) { CancelGetZoneData(m, q->nta); q->nta = mDNSNULL; }
+ if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
+ q->state = LLQ_InitialRequest; // Will need to set up new LLQ on wake from sleep
+ q->id = zeroOpaque64;
+ }
+ }
+
+mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question)
+ {
+ // For now this AutoTunnel stuff is specific to Mac OS X.
+ // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
+#if APPLE_OSX_mDNSResponder
+ if (question->qtype == kDNSType_AAAA && question->AuthInfo && question->AuthInfo->AutoTunnel && question->QuestionCallback != AutoTunnelCallback)
+ {
+ question->NoAnswer = NoAnswer_Suspended;
+ AddNewClientTunnel(m, question);
+ return;
+ }
+#endif // APPLE_OSX_mDNSResponder
+
+ if (!question->DuplicateOf)
+ {
+ LogOperation("ActivateUnicastQuery: %##s %s%s",
+ question->qname.c, DNSTypeName(question->qtype), question->AuthInfo ? " (Private)" : "");
+ if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
+ if (question->LongLived) question->state = LLQ_InitialRequest;
+ question->ThisQInterval = InitialQuestionInterval;
+ question->LastQTime = m->timenow - question->ThisQInterval;
+ SetNextQueryTime(m, question);
+ }
+ }
+
// Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
// Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
// Normally, the platform support layer below mDNSCore should call this, not the client layer above.
if (sleepstate)
{
#ifndef UNICAST_DISABLED
- uDNS_Sleep(m);
+ SuspendLLQs(m);
+ SleepServiceRegistrations(m);
+ SleepRecordRegistrations(m);
#endif
// Mark all the records we need to deregister and send them
for (rr = m->ResourceRecords; rr; rr=rr->next)
CacheRecord *cr;
#ifndef UNICAST_DISABLED
+ // On wake, retrigger all our uDNS questions
+ if (m->CurrentQuestion)
+ LogMsg("RestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = m->CurrentQuestion->next;
+ if (!mDNSOpaque16IsZero(q->TargetQID)) ActivateUnicastQuery(m, q);
+ }
+ // and reactivtate record (and service) registrations
uDNS_Wake(m);
#endif
- // 1. Retrigger all our questions
+ // 1. Retrigger all our mDNS questions
for (q = m->Questions; q; q=q->next) // Scan our list of questions
if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q))
{
// 3. Retrigger probing and announcing for all our authoritative records
for (rr = m->ResourceRecords; rr; rr=rr->next)
- {
- if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
- rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
- rr->AnnounceCount = InitialAnnounceCount;
- rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
- InitializeLastAPTime(m, rr);
- }
+ if (!AuthRecord_uDNS(rr))
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+ rr->AnnounceCount = InitialAnnounceCount;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m, rr);
+ }
}
mDNS_Unlock(m);
mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl) // TTL in seconds
{
- if (LLQType == uDNS_LLQ_Poll) ttl = LLQ_POLL_INTERVAL * 2 / mDNSPlatformOneSecond;
- else if (LLQType == uDNS_LLQ_Setup) ttl = kLLQ_DefLease;
+ if (LLQType == uDNS_LLQ_Entire) ttl = kLLQ_DefLease;
else if (LLQType == uDNS_LLQ_Events)
{
// If the TTL is -1 for uDNS LLQ event packet, that means "remove"
response->h.numAuthorities, response->h.numAuthorities == 1 ? "y, " : "ies,",
response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s", LLQType);
+ 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
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
if (SameNameRecordAnswersQuestion(&rr->resrec, &q))
{
- //LogMsg("uDNS Q for %s", CRDisplayString(m, rr));
+ //LogMsg("uDNS marking %s", 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 - rr->resrec.rroriginalttl * mDNSPlatformOneSecond;
+ rr->TimeRcvd = m->timenow - TicksTTL(rr) - 1;
rr->UnansweredQueries = MaxUnansweredQueries;
}
}
// 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 &&
+ if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) &&
mDNSOpaque16IsZero(response->h.id))
- LogMsg("Correcting TTL from %4d to %4d for %s",
+ LogOperation("Correcting TTL from %4d to %4d for %s",
r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2));
r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
}
ptr = getQuestion(response, ptr, end, InterfaceID, &q);
if (ptr && ExpectingUnicastResponseForQuestion(m, response->h.id, &q))
{
- CacheRecord *rr;
+ CacheRecord *rr, *neg = mDNSNULL;
mDNSu32 slot = HashSlot(&q.qname);
CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
if (SameNameRecordAnswersQuestion(&rr->resrec, &q))
{
// 1. If we got a fresh answer to this query, then don't need to generate a negative entry
- // 2. If we already had a negative entry which we were about to discard, then we should resurrect it
- if (rr->resrec.rroriginalttl) break;
- if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) break;
+ if (rr->TimeRcvd + TicksTTL(rr) - m->timenow > 0) break;
+ // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
+ if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr;
}
- if (!rr || rr->resrec.RecordType == kDNSRecordTypePacketNegative)
+ if (!rr)
{
// We start off assuming a negative caching TTL of 60 seconds
// but then look to see if we can find an SOA authority record to tell us a better value we should be using
// of 60 seconds, and we end up polling the server every minute for a record that doesn't exist.
// With this fix in place, when this happens, we double the effective TTL each time (up to one hour),
// so that we back off our polling rate and don't keep hitting the server continually.
- if (rr)
+ if (neg)
{
- if (negttl < rr->resrec.rroriginalttl * 2)
- negttl = rr->resrec.rroriginalttl * 2;
+ if (negttl < neg->resrec.rroriginalttl * 2)
+ negttl = neg->resrec.rroriginalttl * 2;
if (negttl > 3600)
negttl = 3600;
}
negttl = GetEffectiveTTL(LLQType, negttl); // Add 25% grace period if necessary
- if (rr) LogOperation("Renewing negative TTL from %d to %d %s", rr->resrec.rroriginalttl, negttl, CRDisplayString(m, rr));
-
// If we already had a negative cache entry just update it, else make one or more new negative cache entries
- if (rr)
- RefreshCacheRecord(m, rr, negttl);
+ if (neg)
+ {
+ LogOperation("Renewing negative TTL from %d to %d %s", neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg));
+ RefreshCacheRecord(m, neg, negttl);
+ }
else while (1)
{
- LogOperation("Making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype));
+ LogOperation("mDNSCoreReceiveResponse making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype));
MakeNegativeCacheRecord(m, name, hash, q.qtype, q.qclass, negttl);
CreateNewCacheEntry(m, slot, cg);
m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
m->PktNum++;
#ifndef UNICAST_DISABLED
if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR)))
- {
- if (!mDNSOpaque16IsZero(msg->h.id)) ifid = mDNSInterface_Any;
- if (mDNS_LogLevel >= MDNS_LOG_VERBOSE_DEBUG)
- DumpPacket(m, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, msg, end);
- uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport);
- // Note: mDNSCore also needs to get access to received unicast responses
- }
+ if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses
+ {
+ ifid = mDNSInterface_Any;
+ if (mDNS_LogLevel >= MDNS_LOG_VERBOSE_DEBUG)
+ DumpPacket(m, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, 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);
#define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \
(mDNSSameAddress(&(A)->Target, &(B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort)))
+// Note: We explicitly disallow making a public query be a duplicate of a private one. This is to avoid the
+// circular deadlock where a client does a query for something like "dns-sd -Q _dns-query-tls._tcp.company.com SRV"
+// and we have a key for company.com, so we try to locate the private query server for company.com, which necessarily entails
+// doing a standard DNS query for the _dns-query-tls._tcp SRV record for company.com. If we make the latter (public) query
+// a duplicate of the former (private) query, then it will block forever waiting for an answer that will never come.
+
mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
{
DNSQuestion *q;
for (q = m->Questions; q && q != question; q=q->next) // Scan our list for another question
if (q->InterfaceID == question->InterfaceID && // with the same InterfaceID,
SameQTarget(q, question) && // and same unicast/multicast target settings
- q->qtype == question->qtype && // type,
- q->qclass == question->qclass && // class,
- q->AuthInfo == question->AuthInfo && // and privacy status matches
- q->LongLived == question->LongLived && // and long-lived status matches
- q->qnamehash == question->qnamehash &&
+ q->qtype == question->qtype && // type,
+ q->qclass == question->qclass && // class,
+ q->LongLived == question->LongLived && // and long-lived status matches
+ (!q->AuthInfo || question->AuthInfo) && // to avoid deadlock, don't make public query dup of a private one
+ q->qnamehash == question->qnamehash &&
SameDomainName(&q->qname, &question->qname)) // and name
return(q);
return(mDNSNULL);
q->servPort = question->servPort;
q->state = question->state;
- // q->NATInfoUDP = question->NATInfoUDP;
- q->eventPort = question->eventPort;
// q->tcp = question->tcp;
q->origLease = question->origLease;
q->expire = question->expire;
q->id = question->id;
question->nta = mDNSNULL; // If we've got a GetZoneData in progress, transfer it to the newly active question
- // question->NATInfoUDP = mDNSNULL;
// question->tcp = mDNSNULL;
if (q->nta)
{
// Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash
if (question->tcp) LogOperation("UpdateQuestionDuplicates did not transfer tcp pointer");
-
+
+ if (question->state == LLQ_Established)
+ {
+ LogOperation("UpdateQuestionDuplicates transferred LLQ state for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ question->state = 0; // Must zero question->state, or mDNS_StopQuery_internal will clean up and cancel our LLQ from the server
+ }
+
SetNextQueryTime(m,q);
}
}
#define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
(mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort)))
-mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question)
+// Called in normal client context (lock not held)
+mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n)
{
- // For now this AutoTunnel stuff is specific to Mac OS X.
- // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
-#if APPLE_OSX_mDNSResponder
- if (question->qtype == kDNSType_AAAA && question->AuthInfo && question->AuthInfo->AutoTunnel && question->QuestionCallback != AutoTunnelCallback)
- {
- question->NoAnswer = NoAnswer_Suspended;
- AddNewClientTunnel(m, question);
- return;
- }
-#endif // APPLE_OSX_mDNSResponder
-
- if (!question->DuplicateOf)
- {
- if (question->LongLived)
- {
- question->ThisQInterval = 0; // Question is suspended, waiting for GetZoneData to complete
- question->LastQTime = m->timenow;
- LogOperation("uDNS_InitLongLivedQuery: %##s %s %s %d",
- question->qname.c, DNSTypeName(question->qtype), question->AuthInfo ? "(Private)" : "", question->ThisQInterval);
- if (question->nta) CancelGetZoneData(m, question->nta);
- question->state = LLQ_GetZoneInfo; // Necessary to stop "bad state" error in startLLQHandshakeCallback
- question->nta = StartGetZoneData(m, &question->qname, ZoneServiceLLQ, startLLQHandshakeCallback, question);
- if (!question->nta) LogMsg("ERROR: startLLQ - StartGetZoneData failed");
- }
- else
- {
- question->ThisQInterval = InitialQuestionInterval;
- question->LastQTime = m->timenow - question->ThisQInterval;
- }
- }
+ DNSQuestion *q;
+ (void)n; // Unused
+ mDNS_Lock(m);
+ LogOperation("LLQNATCallback external address:port %.4a:%u", &n->ExternalAddress, mDNSVal16(n->ExternalPort));
+ for (q = m->Questions; q; q=q->next)
+ if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived)
+ startLLQHandshake(m, q); // If ExternalPort is zero, will do StartLLQPolling instead
+ mDNS_Unlock(m);
}
mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
question->UniqueAnswers = 0;
question->FlappingInterface1 = mDNSNULL;
question->FlappingInterface2 = mDNSNULL;
- // GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
- // because that would result in an infinite loop (i.e. to do a private query we first need to get
- // the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
- // we'd need to already know the _dns-query-tls SRV record.
- // Also: Make sure we set AuthInfo before calling FindDuplicateQuestion()
- question->AuthInfo = (question->QuestionCallback == GetZoneData_QuestionCallback) ? mDNSNULL
- : GetAuthInfoForName_internal(m, &question->qname);
+ question->AuthInfo = GetAuthInfoForQuestion(m, question); // Must do this before calling FindDuplicateQuestion()
question->DuplicateOf = FindDuplicateQuestion(m, question);
question->NextInDQList = mDNSNULL;
question->SendQNow = mDNSNULL;
question->tcp = mDNSNULL;
question->NoAnswer = NoAnswer_Normal;
- question->state = LLQ_GetZoneInfo;
- mDNSPlatformMemZero(&question->NATInfoUDP, sizeof(question->NATInfoUDP));
- question->eventPort = zeroIPPort;
+ question->state = LLQ_InitialRequest;
question->origLease = 0;
question->expire = 0;
question->ntries = 0;
question->id = zeroOpaque64;
+ if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo;
+
for (i=0; i<DupSuppressInfoSize; i++)
question->DupSuppress[i].InterfaceID = mDNSNULL;
{
question->qDNSServer = GetServerForName(m, &question->qname);
ActivateUnicastQuery(m, question);
+
+ // If long-lived query, and we don't have our NAT mapping active, start it now
+ if (question->LongLived && !m->LLQNAT.clientContext)
+ {
+ m->LLQNAT.Protocol = NATOp_MapUDP;
+ m->LLQNAT.IntPort = m->UnicastPort4;
+ m->LLQNAT.RequestedPort = m->UnicastPort4;
+ m->LLQNAT.clientCallback = LLQNATCallback;
+ m->LLQNAT.clientContext = (void*)1; // Means LLQ NAT Traversal is active
+ mDNS_StartNATOperation_internal(m, &m->LLQNAT);
+ }
}
SetNextQueryTime(m,question);
}
if (*q) *q = (*q)->next;
else
{
+#if !ForceAlerts
if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active
+#endif
LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
question->qname.c, DNSTypeName(question->qtype));
#if ForceAlerts
// so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already
// invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary
// *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query.
- if (question->nta) CancelGetZoneData(m, question->nta);
+ if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; }
- if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived) uDNS_StopLongLivedQuery(m, question);
+ if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived)
+ {
+ // Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal.
+ DNSQuestion *q;
+ for (q = m->Questions; q; q=q->next)
+ if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break;
+ if (!q)
+ {
+ if (!m->LLQNAT.clientContext) // Should never happen, but just in case...
+ LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL");
+ else
+ {
+ LogOperation("Stopping LLQNAT");
+ mDNS_StopNATOperation_internal(m, &m->LLQNAT);
+ m->LLQNAT.clientContext = mDNSNULL; // Means LLQ NAT Traversal not running
+ }
+ }
+
+ // If necessary, tell server it can delete this LLQ state
+ if (question->state == LLQ_Established) sendLLQRefresh(m, question, 0);
+ }
return(mStatus_NoError);
}
if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
#ifndef UNICAST_DISABLED
- if (question->InterfaceID != mDNSInterface_LocalOnly && !question->ForceMCast && !IsLocalDomain(&question->qname))
+ if (Question_uDNS(question))
{
question->LongLived = mDNStrue;
question->ThisQInterval = InitialQuestionInterval;
mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
{
+ AuthRecord *rr;
+ ServiceRecordSet *s;
mDNSBool FirstOfType = mDNStrue;
NetworkInterfaceInfo **p = &m->HostInterfaces;
if (set->McastTxRx && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
{
DNSQuestion *q;
- AuthRecord *rr;
// If flapping, delay between first and second queries is eight seconds instead of one
mDNSs32 delay = flapping ? mDNSPlatformOneSecond * 5 : 0;
mDNSu8 announce = flapping ? (mDNSu8)1 : InitialAnnounceCount;
// For all our non-specific authoritative resource records (and any dormant records specific to this interface)
// we now need them to re-probe if necessary, and then re-announce.
for (rr = m->ResourceRecords; rr; rr=rr->next)
- if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
- {
- if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
- rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
- if (rr->AnnounceCount < announce) rr->AnnounceCount = announce;
- rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
- InitializeLastAPTime(m, rr);
- }
+ if (!AuthRecord_uDNS(rr))
+ if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+ if (rr->AnnounceCount < announce) rr->AnnounceCount = announce;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m, rr);
+ }
+ }
+
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (AuthRecord_uDNS(rr))
+ {
+ LogOperation("mDNS_RegisterInterface: StartGetZoneData for %##s", rr->resrec.name->c);
+ if (rr->nta) CancelGetZoneData(m, rr->nta);
+ rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr);
+ }
+
+ for (s = m->ServiceRegistrations; s; s = s->uDNS_next)
+ {
+ LogOperation("mDNS_RegisterInterface: StartGetZoneData for %##s", s->RR_SRV.resrec.name->c);
+ if (s->nta) CancelGetZoneData(m, s->nta);
+ s->nta = StartGetZoneData(m, s->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, s);
}
mDNS_Unlock(m);
if (!GetServiceTarget(m, srs))
{
// defer registration until we've got a target
- debugf("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c);
+ LogOperation("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c);
srs->state = regState_NoTarget;
srs->nta = mDNSNULL;
return mStatus_NoError;
}
srs->state = regState_FetchingZoneData;
- srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
+ srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, srs);
return srs->nta ? mStatus_NoError : mStatus_NoMemoryErr;
}
sr->state = regState_Zero;
sr->srs_uselease = 0;
- sr->expire = 0;
sr->TestForSelfConflict = 0;
sr->Private = 0;
sr->id = zeroID;
sr->zone.c[0] = 0;
- sr->ns = zeroAddr;
+ sr->SRSUpdateServer = zeroAddr;
sr->SRSUpdatePort = zeroIPPort;
mDNSPlatformMemZero(&sr->NATinfo, sizeof(sr->NATinfo));
sr->ClientCallbackDeferred = 0;
{
*e = extra;
#ifndef UNICAST_DISABLED
- if (sr->RR_SRV.resrec.InterfaceID != mDNSInterface_LocalOnly && !IsLocalDomain(sr->RR_SRV.resrec.name))
+ if (AuthRecord_uDNS(&sr->RR_SRV))
{
extra->r.resrec.RecordType = kDNSRecordTypeShared; // don't want it to conflict with the service name (???)
extra->r.RecordCallback = DummyCallback; // don't generate callbacks for extra RRs for unicast services (WHY NOT????)
mDNSAddr v4, v6, r;
domainname fqdn;
DNSServer *ptr, **p = &m->DNSServers;
+ const DNSServer *oldServers = m->DNSServers;
DNSQuestion *q;
if (m->RegisterSearchDomains) uDNS_RegisterSearchDomains(m);
if (!mDNSOpaque16IsZero(q->TargetQID))
{
DNSServer *s = GetServerForName(m, &q->qname);
- if (q->qDNSServer != s)
+ DNSServer *t = q->qDNSServer;
+ if (t != s)
{
// If DNS Server for this question has changed, reactivate it
- LogOperation("Updating DNS Server from %#a:%d to %#a:%d for %##s (%s)",
- q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort),
- s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort),
+ LogOperation("Updating DNS Server from %p %#a:%d (%##s) to %p %#a:%d (%##s) for %##s (%s)",
+ t, t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"",
+ s, s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"",
q->qname.c, DNSTypeName(q->qtype));
q->qDNSServer = s;
ActivateUnicastQuery(m, q);
p = &(*p)->next;
}
- // If we have no DNS servers at all, then immediately purge all unicast cache records (including for LLQs)
- // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless
- // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour
- if (!m->DNSServers) FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) mDNS_PurgeCacheResourceRecord(m, cr);
+ // If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs).
+ // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless.
+ // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour.
+ // Similarly, if we now have some DNS servers and we used to have none, we want to purge any fake negative results we may have generated.
+ if ((m->DNSServers != mDNSNULL) != (oldServers != mDNSNULL))
+ {
+ int count = 0;
+ FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) { mDNS_PurgeCacheResourceRecord(m, cr); count++; }
+ LogOperation("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache",
+ m->DNSServers ? "DNS server became" : "No DNS servers", count);
+ }
// Did our FQDN change?
if (!SameDomainName(&fqdn, &m->FQDN))
m->mDNS_shutdown = mDNStrue;
#ifndef UNICAST_DISABLED
- uDNS_Sleep(m);
+ SuspendLLQs(m);
+ SleepServiceRegistrations(m);
while (m->Hostnames) mDNS_RemoveDynDNSHostName(m, &m->Hostnames->fqdn);
#endif
while (m->CurrentRecord)
{
rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
- else
- m->CurrentRecord = rr->next;
}
// Now deregister any remaining records we didn't get the first time through
+ m->CurrentRecord = m->ResourceRecords;
while (m->CurrentRecord)
{
rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
- else
- m->CurrentRecord = rr->next;
}
- if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records");
- else debugf("mDNS_Close: No deregistering records remain");
+ if (m->ResourceRecords) LogOperation("mDNS_Close: Sending final packets for deregistering records");
+ else LogOperation("mDNS_Close: No deregistering records remain");
// If any deregistering records remain, send their deregistration announcements before we exit
if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m);
Change History (most recent first):
$Log: mDNSEmbeddedAPI.h,v $
+Revision 1.453 2007/10/29 23:51:22 cheshire
+Added comment about NATTraversalInfo ExternalAddress field
+
+Revision 1.452 2007/10/29 18:13:40 cheshire
+Added Question_uDNS macro, analogous to AuthRecord_uDNS macro
+
+Revision 1.451 2007/10/26 23:42:57 cheshire
+Removed unused "mDNSs32 expire" field from ServiceRecordSet_struct
+
+Revision 1.450 2007/10/26 22:24:08 cheshire
+Added AuthRecord_uDNS() macro to determine when a given AuthRecord needs to be registered via unicast DNS
+
+Revision 1.449 2007/10/25 20:48:47 cheshire
+For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer'
+
+Revision 1.448 2007/10/22 22:19:44 cheshire
+Tidied up code alignment
+
+Revision 1.447 2007/10/22 19:40:30 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Made subroutine mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
+
+Revision 1.446 2007/10/17 22:49:54 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.445 2007/10/17 22:37:23 cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
Revision 1.444 2007/09/29 03:14:52 cheshire
<rdar://problem/5513168> BTMM: mDNSResponder memory corruption in GetAuthInfoForName_internal
Added AutoTunnelUnregistered macro to check state of DomainAuthInfo AuthRecords
enum
{
- NATErr_None = 0,
- NATErr_Vers = 1,
+ NATErr_None = 0,
+ NATErr_Vers = 1,
NATErr_Refused = 2,
NATErr_NetFail = 3,
- NATErr_Res = 4,
- NATErr_Opcode = 5
+ NATErr_Res = 4,
+ NATErr_Opcode = 5
};
typedef mDNSu16 NATErr_t;
// mapping response from the NAT-PMP or UPnP gateway. For example, if we ask for port 80, and
// get assigned port 81, then thereafter we'll contine asking for port 81.
mDNSInterfaceID InterfaceID;
- mDNSv4Addr ExternalAddress;
+ mDNSv4Addr ExternalAddress; // Initially set to onesIPv4Addr, until first callback
mDNSIPPort ExternalPort;
mDNSu32 Lifetime;
mStatus Result;
// DO NOT ADD ANY MORE FIELDS HERE
};
+#define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name))
+#define Question_uDNS(Q) ((Q)->InterfaceID == mDNSInterface_Any && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname))
+
// Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field
typedef struct ARListElem
{
ServiceRecordSet *uDNS_next;
regState_t state;
mDNSBool srs_uselease; // dynamic update contains (should contain) lease option
- mDNSs32 expire; // expiration of lease (-1 for static)
mDNSBool TestForSelfConflict; // on name conflict, check if we're just seeing our own orphaned records
mDNSBool Private; // If zone is private, DNS updates may have to be encrypted to prevent eavesdropping
ZoneData *nta;
mDNSOpaque16 id;
domainname zone; // the zone that is updated
- mDNSAddr ns; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL
+ mDNSAddr SRSUpdateServer; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL
mDNSIPPort SRSUpdatePort; // port on which server accepts dynamic updates
- NATTraversalInfo NATinfo; // may be NULL
+ NATTraversalInfo NATinfo;
mDNSBool ClientCallbackDeferred; // invoke client callback on completion of pending operation(s)
mStatus DeferredStatus; // status to deliver when above flag is set
mDNSBool SRVUpdateDeferred; // do we need to change target or port once current operation completes?
typedef enum
{
- // Setup states
- LLQ_UnInit = 0,
- LLQ_GetZoneInfo = 1,
- LLQ_InitialRequest = 2,
- LLQ_SecondaryRequest = 3,
- LLQ_Refresh = 4,
- LLQ_Retry = 5,
- LLQ_Established = 6,
- LLQ_Suspended = 7,
- LLQ_SuspendDeferred = 8, // suspend once we get zone info
- LLQ_SuspendedPoll = 9, // suspended from polling state
- LLQ_NatMapWaitUDP = 10,
-
- // Established/error states
- LLQ_Static = 16,
- LLQ_Poll = 17,
- LLQ_Error = 18,
- LLQ_Cancelled = 19
+ LLQ_InitialRequest = 1,
+ LLQ_SecondaryRequest = 2,
+ LLQ_Established = 3,
+ LLQ_Poll = 4
} LLQ_State;
// LLQ constants
#define kLLQ_DefLease 7200 // 2 hours
#define kLLQ_MAX_TRIES 3 // retry an operation 3 times max
#define kLLQ_INIT_RESEND 2 // resend an un-ack'd packet after 2 seconds, then double for each additional
-#define kLLQ_DEF_RETRY 1800 // retry a failed operation after 30 minutes
// LLQ Operation Codes
#define kLLQOp_Setup 1
#define kLLQOp_Refresh 2
// LLQ Errror Codes
enum
{
- LLQErr_NoError = 0,
- LLQErr_ServFull = 1,
- LLQErr_Static = 2,
- LLQErr_FormErr = 3,
- LLQErr_NoSuchLLQ = 4,
- LLQErr_BadVers = 5,
+ LLQErr_NoError = 0,
+ LLQErr_ServFull = 1,
+ LLQErr_Static = 2,
+ LLQErr_FormErr = 3,
+ LLQErr_NoSuchLLQ = 4,
+ LLQErr_BadVers = 5,
LLQErr_UnknownErr = 6
};
// LLQ-specific fields. These fields are only meaningful when LongLived flag is set
LLQ_State state;
- NATTraversalInfo NATInfoUDP;
- mDNSIPPort eventPort; // This is non-zero if this is a private LLQ. If we're behind NAT, it's the external UDP port.
mDNSu32 origLease; // seconds (relative)
mDNSs32 expire; // ticks (absolute)
mDNSs16 ntries;
mDNSBool RegisterSearchDomains;
// NAT traversal fields
+ NATTraversalInfo LLQNAT; // Single shared NAT Traversal to receive inbound LLQ notifications
NATTraversalInfo *NATTraversals;
NATTraversalInfo *CurrentNATTraversal;
mDNSs32 retryIntervalGetAddr; // delta between time sent and retry
// Host domains added prior to specification of the primary interface address and computer name will be deferred until
// these values are initialized.
-// When routable V4 interfaces are added or removed, mDNS_UpdateLLQs should be called to re-estabish LLQs in case the
-// destination address for events (i.e. the route) has changed. For performance reasons, the caller is responsible for
-// batching changes, e.g. calling the routine only once if multiple interfaces are simultanously removed or added.
-
// DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer.
// For "split" DNS configurations, in which queries for different domains are sent to different servers (e.g. VPN and external),
// a domain may be associated with a DNS server. For standard configurations, specify the root label (".") or NULL.
extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext);
extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router);
-extern void mDNS_UpdateLLQs(mDNS *m);
extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port);
extern void mDNS_AddSearchDomain(const domainname *const domain);
extern long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len);
extern UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, mDNSIPPort port);
extern void mDNSPlatformUDPClose(UDPSocket *sock);
+extern void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst);
// mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd
extern mStatus mDNSPlatformTLSSetupCerts(void);
char sizecheck_AuthRecord [(sizeof(AuthRecord) <= 1290) ? 1 : -1];
char sizecheck_CacheRecord [(sizeof(CacheRecord) <= 170) ? 1 : -1];
char sizecheck_CacheGroup [(sizeof(CacheGroup) <= 170) ? 1 : -1];
- char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 700) ? 1 : -1];
+ char sizecheck_DNSQuestion [(sizeof(DNSQuestion) <= 560) ? 1 : -1];
char sizecheck_ZoneData [(sizeof(ZoneData) <= 1600) ? 1 : -1];
char sizecheck_NATTraversalInfo [(sizeof(NATTraversalInfo) <= 140) ? 1 : -1];
char sizecheck_HostnameInfo [(sizeof(HostnameInfo) <= 3000) ? 1 : -1];
Change History (most recent first):
$Log: uDNS.c,v $
+Revision 1.528 2007/11/02 21:32:30 cheshire
+<rdar://problem/5575593> BTMM: Deferring deregistration of record log messages on sleep/wake
+
+Revision 1.527 2007/11/01 16:08:51 cheshire
+Tidy up alignment of "SetRecordRetry refresh" log messages
+
+Revision 1.526 2007/10/31 19:26:55 cheshire
+Don't need to log "Permanently abandoning service registration" message when we're intentionally deleting a service
+
+Revision 1.525 2007/10/30 23:58:59 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+After failure, double retry interval up to maximum of 30 minutes
+
+Revision 1.524 2007/10/30 20:10:47 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.523 2007/10/30 00:54:31 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Fixed timing logic to double retry interval properly
+
+Revision 1.522 2007/10/30 00:04:43 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Made the code not give up and abandon the record when it gets an error in regState_UpdatePending state
+
+Revision 1.521 2007/10/29 23:58:52 cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+Use standard "if (mDNSIPv4AddressIsOnes(....ExternalAddress))" mechanism to determine whether callback has been invoked yet
+
+Revision 1.520 2007/10/29 21:48:36 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Added 10% random variation on LLQ renewal time, to reduce unintended timing correlation between multiple machines
+
+Revision 1.519 2007/10/29 21:37:00 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Added 10% random variation on record refresh time, to reduce accidental timing correlation between multiple machines
+
+Revision 1.518 2007/10/26 23:41:29 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.517 2007/10/25 23:30:12 cheshire
+Private DNS registered records now deregistered on sleep and re-registered on wake
+
+Revision 1.516 2007/10/25 22:53:52 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Don't unlinkSRS and permanently give up at the first sign of trouble
+
+Revision 1.515 2007/10/25 21:08:07 cheshire
+Don't try to send record registrations/deletions before we have our server address
+
+Revision 1.514 2007/10/25 20:48:47 cheshire
+For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer'
+
+Revision 1.513 2007/10/25 20:06:13 cheshire
+Don't try to do SOA queries using private DNS (TLS over TCP) queries
+
+Revision 1.512 2007/10/25 18:25:15 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Don't need a NAT mapping for autotunnel services
+
+Revision 1.511 2007/10/25 00:16:23 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Fixed retry timing logic; when DNS server returns an error code, we should retry later,
+instead of just deleting our record ("UnlinkAuthRecord") and completely giving up
+
+Revision 1.510 2007/10/24 22:40:06 cheshire
+Renamed: RecordRegistrationCallback -> RecordRegistrationGotZoneData
+Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData
+
+Revision 1.509 2007/10/24 00:54:07 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.508 2007/10/24 00:05:03 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+When sending TLS/TCP LLQ setup request over VPN, need to set EventPort to 5353, not zero
+
+Revision 1.507 2007/10/23 00:33:36 cheshire
+Improved debugging messages
+
+Revision 1.506 2007/10/22 19:54:13 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Only put EventPort in LLQ request when sending from an RFC 1918 source address, not when sending over VPN
+
+Revision 1.505 2007/10/19 22:08:49 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.504 2007/10/18 23:06:43 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.503 2007/10/18 20:23:17 cheshire
+Moved SuspendLLQs into mDNS.c, since it's only called from one place
+
+Revision 1.502 2007/10/17 22:49:54 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.501 2007/10/17 22:37:23 cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
+Revision 1.500 2007/10/17 21:53:51 cheshire
+Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData
+
+Revision 1.499 2007/10/16 21:16:50 cheshire
+Get rid of unused uDNS_Sleep() routine
+
+Revision 1.498 2007/10/16 20:59:41 cheshire
+Export SuspendLLQs/SleepServiceRegistrations/SleepRecordRegistrations so they're callable from other files
+
+Revision 1.497 2007/10/05 18:09:44 cheshire
+<rdar://problem/5524841> Services advertised with wrong target host
+
Revision 1.496 2007/10/04 22:38:59 cheshire
Added LogOperation message showing new q->ThisQInterval after sending uDNS query packet
Revision 1.495 2007/10/03 00:16:19 cheshire
-In startPrivateQueryCallback, need to grab lock before calling SetNextQueryTime
+In PrivateQueryGotZoneData, need to grab lock before calling SetNextQueryTime
Revision 1.494 2007/10/02 21:11:08 cheshire
<rdar://problem/5518270> LLQ refreshes don't work, which breaks BTMM browsing
Revision 1.456 2007/09/05 21:00:17 cheshire
<rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions
-Additional refinement: ThisQInterval needs to be restored in tcpCallback, not in startPrivateQueryCallback
+Additional refinement: ThisQInterval needs to be restored in tcpCallback, not in PrivateQueryGotZoneData
Revision 1.455 2007/09/05 20:53:06 cheshire
Tidied up alignment of code layout; code was clearing m->tcpAddrInfo.sock instead of m->tcpDeviceInfo.sock
Revision 1.453 2007/09/05 02:26:57 cheshire
<rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions
-In startPrivateQueryCallback, restore q->ThisQInterval to non-zero value after GetZoneData completes
+In PrivateQueryGotZoneData, restore q->ThisQInterval to non-zero value after GetZoneData completes
Revision 1.452 2007/08/31 22:58:22 cheshire
If we have an existing TCP connection we should re-use it instead of just bailing out
// (for service record sets, use RR_SRV as representative for time checks
mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mStatus SendErr)
{
+ mDNSs32 elapsed = m->timenow - rr->LastAPTime;
rr->LastAPTime = m->timenow;
- if (SendErr == mStatus_TransientErr || rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL) rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
- else if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL) rr->ThisAPInterval *= 2;
- else rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL;
+
+#if 0
+ // Code for stress-testing registration renewal code
+ if (rr->expire && rr->expire - m->timenow > mDNSPlatformOneSecond * 120)
+ {
+ LogOperation("Adjusting expiry from %d to 120 seconds for %s",
+ (rr->expire - m->timenow) / mDNSPlatformOneSecond, ARDisplayString(m, rr));
+ rr->expire = m->timenow + mDNSPlatformOneSecond * 120;
+ }
+#endif
+
+ if (rr->expire && rr->expire - m->timenow > mDNSPlatformOneSecond)
+ {
+ mDNSs32 remaining = rr->expire - m->timenow;
+ rr->ThisAPInterval = remaining/2 + mDNSRandom(remaining/10);
+ LogOperation("SetRecordRetry refresh in %4d of %4d for %s",
+ rr->ThisAPInterval / mDNSPlatformOneSecond,
+ (rr->expire - m->timenow) / mDNSPlatformOneSecond,
+ ARDisplayString(m, rr));
+ return;
+ }
+
+ rr->expire = 0;
+
+ // If at least half our our time interval has elapsed, it's time to double rr->ThisAPInterval
+ // If resulting interval is too small, set to at least INIT_UCAST_POLL_INTERVAL (3 seconds)
+ // If resulting interval is too large, set to at most 30 minutes
+ if (rr->ThisAPInterval / 2 <= elapsed) rr->ThisAPInterval *= 2;
+ if (rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL || SendErr == mStatus_TransientErr)
+ rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
+ if (rr->ThisAPInterval > 30 * 60 * mDNSPlatformOneSecond)
+ rr->ThisAPInterval = 30 * 60 * mDNSPlatformOneSecond;
+
+ LogOperation("SetRecordRetry retry in %4d for %s", rr->ThisAPInterval / mDNSPlatformOneSecond, ARDisplayString(m, rr));
}
// ***************************************************************************
// Check to see if adding this new DomainAuthInfo has changed the credentials for any of our questions
for (q = m->Questions; q; q=q->next)
{
- if (q->QuestionCallback != GetZoneData_QuestionCallback)
+ DomainAuthInfo *newinfo = GetAuthInfoForQuestion(m, q);
+ if (q->AuthInfo != newinfo)
{
- DomainAuthInfo *newinfo = GetAuthInfoForName_internal(m, &q->qname);
- if (q->AuthInfo != newinfo)
- {
- debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)",
- q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL,
- newinfo ? newinfo ->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
- q->AuthInfo = newinfo;
- }
+ debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)",
+ q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL,
+ newinfo ? newinfo ->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
+ q->AuthInfo = newinfo;
}
}
#pragma mark - Long-Lived Queries
#endif
+// Lock must be held -- otherwise m->timenow is undefined
mDNSlocal void StartLLQPolling(mDNS *const m, DNSQuestion *q)
{
LogOperation("StartLLQPolling: %##s", q->qname.c);
q->state = LLQ_Poll;
- q->ThisQInterval = LLQ_POLL_INTERVAL;
- q->LastQTime = m->timenow - q->ThisQInterval; // trigger immediate poll
+ q->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+ // We want to send our poll query ASAP, but the "+ 1" is because if we set the time to now,
+ // we risk causing spurious "SendQueries didn't send all its queries" log messages
+ q->LastQTime = m->timenow - q->ThisQInterval + 1;
SetNextQueryTime(m, q);
}
-// Forward reference
-mDNSlocal void LLQNatMapComplete(mDNS *m, NATTraversalInfo *n);
-
-mDNSlocal void StartLLQNatMap(mDNS *m, DNSQuestion *q)
- {
- LogOperation("StartLLQNatMap: LLQ_NatMapWaitUDP");
- mDNSPlatformMemZero(&q->NATInfoUDP, sizeof(NATTraversalInfo));
- q->NATInfoUDP.IntPort = q->NATInfoUDP.RequestedPort = m->UnicastPort4;
- q->NATInfoUDP.Protocol = NATOp_MapUDP;
- q->NATInfoUDP.clientCallback = LLQNatMapComplete;
- q->NATInfoUDP.clientContext = q; // Must be set non-null so we know this NATTraversalInfo object is in use
- mDNS_StartNATOperation_internal(m, &q->NATInfoUDP);
- }
-
mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *const question, const LLQOptData *const data, mDNSBool includeQuestion)
{
AuthRecord rr;
return ptr;
}
-mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question)
+// Normally we'd just request event packets be sent directly to m->LLQNAT.ExternalPort, except...
+// with LLQs over TLS/TCP we're doing a weird thing where instead of requesting packets be sent to ExternalAddress:ExternalPort
+// we're requesting that packets be sent to ExternalPort, but at the source address of our outgoing TCP connection.
+// Normally, after going through the NAT gateway, the source address of our outgoing TCP connection is the same as ExternalAddress,
+// so this is fine, except when the TCP connection ends up going over a VPN tunnel instead.
+// To work around this, if we find that the source address for our TCP connection is not a private address, we tell the Dot Mac
+// LLQ server to send events to us directly at port 5353 on that address, instead of at our mapped external NAT port.
+
+mDNSlocal mDNSu16 GetLLQEventPort(const mDNS *const m, const mDNSAddr *const dst)
{
- InitializeDNSMessage(&msg->h, question->TargetQID, uQueryFlags);
-
- *endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
- if (!*endPtr)
- {
- LogMsg("ERROR: Unicast query out of space in packet");
- return mStatus_UnknownErr;
- }
- return mStatus_NoError;
+ mDNSAddr src;
+ mDNSPlatformSourceAddrForDest(&src, dst);
+ //LogMsg("GetLLQEventPort: src %#a for dst %#a (%d)", &src, dst, mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : 0);
+ return(mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : mDNSVal16(MulticastDNSPort));
}
// Normally called with llq set.
mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const LLQOptData *llq)
{
mDNSu8 *responsePtr = m->omsg.data;
- mStatus err;
LLQOptData llqBuf;
if (q->ntries++ == kLLQ_MAX_TRIES)
{
- LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s Will re-try in %d minutes",
- kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60);
- q->state = LLQ_Retry;
- q->LastQTime = m->timenow;
- q->ThisQInterval = (kLLQ_DEF_RETRY * mDNSPlatformOneSecond);
- SetNextQueryTime(m, q);
- // !!!KRS give a callback error in these cases?
+ LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s", kLLQ_MAX_TRIES, q->qname.c);
+ StartLLQPolling(m,q);
return;
}
{
llqBuf.vers = kLLQ_Vers;
llqBuf.llqOp = kLLQOp_Setup;
- llqBuf.err = LLQErr_NoError;
+ llqBuf.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP
llqBuf.id = q->id;
llqBuf.llqlease = q->origLease;
llq = &llqBuf;
q->ThisQInterval = q->tcp ? 0 : (kLLQ_INIT_RESEND * q->ntries * mDNSPlatformOneSecond); // If using TCP, don't need to retransmit
SetNextQueryTime(m, q);
- if (constructQueryMsg(&m->omsg, &responsePtr, q)) goto error;
- responsePtr = putLLQ(&m->omsg, responsePtr, q, llq, mDNSfalse);
- if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; }
-
- //LogOperation("sendChallengeResponse %#a:%d %d %p %d", &q->servAddr, mDNSVal16(q->servPort), q->tcp, q->AuthInfo, responsePtr - (mDNSu8 *)&m->omsg);
- err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
- if (err) debugf("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err);
- // on error, we procede as normal and retry after the appropriate interval
+ // To simulate loss of challenge response packet, uncomment line below
+ //if (q->ntries == 1) return;
- return;
-
- error:
- q->state = LLQ_Error;
+ InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+ responsePtr = putQuestion(&m->omsg, responsePtr, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
+ if (responsePtr) responsePtr = putLLQ(&m->omsg, responsePtr, q, llq, mDNSfalse);
+ if (responsePtr) mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
+ else StartLLQPolling(m,q);
}
-mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const rdataOPT *opt)
+mDNSlocal void SetLLQTimer(mDNS *const m, DNSQuestion *const q, const LLQOptData *const llq)
{
- mStatus err = mStatus_NoError;
+ mDNSs32 lease = (mDNSs32)llq->llqlease * mDNSPlatformOneSecond;
+ q->origLease = llq->llqlease;
+ q->LastQTime = m->timenow;
+ q->expire = m->timenow + lease;
+ q->ThisQInterval = lease/2 + mDNSRandom(lease/10);
+ SetNextQueryTime(m, q);
+ }
+mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const LLQOptData *const llq)
+ {
if (rcode && rcode != kDNSFlag1_RC_NXDomain)
- { LogMsg("ERROR: recvSetupResponse %##s - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c); goto fail; }
+ { LogMsg("ERROR: recvSetupResponse %##s - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c); return; }
- if (opt->OptData.llq.llqOp != kLLQOp_Setup)
- { LogMsg("ERROR: recvSetupResponse %##s - bad op %d", q->qname.c, opt->OptData.llq.llqOp); goto fail; }
+ if (llq->llqOp != kLLQOp_Setup)
+ { LogMsg("ERROR: recvSetupResponse %##s - bad op %d", q->qname.c, llq->llqOp); return; }
- if (opt->OptData.llq.vers != kLLQ_Vers)
- { LogMsg("ERROR: recvSetupResponse %##s - bad vers %d", q->qname.c, opt->OptData.llq.vers); goto fail; }
+ if (llq->vers != kLLQ_Vers)
+ { LogMsg("ERROR: recvSetupResponse %##s - bad vers %d", q->qname.c, llq->vers); return; }
if (q->state == LLQ_InitialRequest)
{
- const LLQOptData *const llq = &opt->OptData.llq;
//LogOperation("Got LLQ_InitialRequest");
- switch(llq->err)
- {
- case LLQErr_NoError: break;
- case LLQErr_ServFull:
- LogMsg("recvSetupResponse - received ServFull from server for LLQ %##s Retry in %lu sec", q->qname.c, llq->llqlease);
- q->LastQTime = m->timenow;
- q->ThisQInterval = ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond);
- q->state = LLQ_Retry;
- SetNextQueryTime(m, q);
- case LLQErr_Static:
- q->state = LLQ_Static;
- q->ThisQInterval = 0;
- LogMsg("LLQ %##s: static", q->qname.c);
- goto exit;
- case LLQErr_FormErr:
- LogMsg("ERROR: recvSetupResponse - received FormErr from server for LLQ %##s", q->qname.c);
- goto error;
- case LLQErr_BadVers:
- LogMsg("ERROR: recvSetupResponse - received BadVers from server");
- goto error;
- case LLQErr_UnknownErr:
- LogMsg("ERROR: recvSetupResponse - received UnknownErr from server for LLQ %##s", q->qname.c);
- goto error;
- default:
- LogMsg("ERROR: recvSetupResponse - received invalid error %d for LLQ %##s", llq->err, q->qname.c);
- goto error;
- }
+ if (llq->err) { LogMsg("recvSetupResponse - received llq->err %d from server", llq->err); StartLLQPolling(m,q); return; }
if (q->origLease != llq->llqlease)
debugf("recvSetupResponse: requested lease %lu, granted lease %lu", q->origLease, llq->llqlease);
// update state
q->state = LLQ_SecondaryRequest;
q->id = llq->id;
- // if (q->ntries == 1) goto exit; // Test for simulating loss of challenge response packet
q->ntries = 0; // first attempt to send response
-
sendChallengeResponse(m, q, llq);
- goto exit;
-
- error:
- q->state = LLQ_Error;
- goto exit;
}
-
- if (q->state == LLQ_SecondaryRequest)
+ else if (q->state == LLQ_SecondaryRequest)
{
//LogOperation("Got LLQ_SecondaryRequest");
// if the server sends back SERVFULL or STATIC.
if (q->AuthInfo)
{
- LogOperation("Private LLQ_SecondaryRequest; copying id %08X%08X", opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1]);
- q->id = opt->OptData.llq.id;
+ LogOperation("Private LLQ_SecondaryRequest; copying id %08X%08X", llq->id.l[0], llq->id.l[1]);
+ q->id = llq->id;
}
- if (opt->OptData.llq.err) { LogMsg("ERROR: recvSetupResponse %##s code %d from server", q->qname.c, opt->OptData.llq.err); q->state = LLQ_Error; goto fail; }
- if (!mDNSSameOpaque64(&q->id, &opt->OptData.llq.id))
- { LogMsg("recvSetupResponse - ID changed. discarding"); goto exit; } // this can happen rarely (on packet loss + reordering)
- q->expire = m->timenow + ((mDNSs32)opt->OptData.llq.llqlease * mDNSPlatformOneSecond );
- q->LastQTime = m->timenow;
- q->ThisQInterval = ((mDNSs32)opt->OptData.llq.llqlease * (mDNSPlatformOneSecond / 2));
- q->origLease = opt->OptData.llq.llqlease;
+ if (llq->err) { LogMsg("ERROR: recvSetupResponse %##s code %d from server", q->qname.c, llq->err); StartLLQPolling(m,q); return; }
+ if (!mDNSSameOpaque64(&q->id, &llq->id))
+ { LogMsg("recvSetupResponse - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering)
q->state = LLQ_Established;
- SetNextQueryTime(m, q);
- goto exit;
- }
-
- if (q->state == LLQ_Established) goto exit;
-
- LogMsg("ERROR: recvSetupResponse %##s - bad state %d", q->qname.c, q->state);
-
-fail:
- err = mStatus_UnknownErr;
-
-exit:
-
- if (err)
- {
- LogOperation("recvSetupResponse error %d ", err);
- StartLLQPolling(m, q);
+ q->ntries = 0;
+ SetLLQTimer(m, q, llq);
}
}
-// Returns mDNStrue if mDNSCoreReceiveResponse should treat this packet as a series of add/remove instructions (like an mDNS response)
-// Returns mDNSfalse if mDNSCoreReceiveResponse should treat this as a single authoritative result (like a normal unicast DNS response)
mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport)
{
DNSQuestion pktQ, *q;
if (msg->h.numQuestions && getQuestion(msg, msg->data, end, 0, &pktQ))
{
const rdataOPT *opt = GetLLQOptData(m, msg, end);
- if (opt)
+
+ for (q = m->Questions; q; q = q->next)
{
- for (q = m->Questions; q; q = q->next)
+ if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname))
{
- if (q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname))
+ debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d",
+ q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr,
+ opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1], q->id.l[0], q->id.l[1], opt->OptData.llq.llqOp);
+ if (q->state == LLQ_Poll)
{
- debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d",
- q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr,
- opt->OptData.llq.id.l[0], opt->OptData.llq.id.l[1], q->id.l[0], q->id.l[1], opt->OptData.llq.llqOp);
- if (q->state == LLQ_Poll)
- {
- m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
- return uDNS_LLQ_Poll;
- }
- else if (q->state == LLQ_Established || (q->state == LLQ_Refresh && msg->h.numAnswers))
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ LogOperation("uDNS_recvLLQResponse got poll response; moving to LLQ_InitialRequest for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ q->state = LLQ_InitialRequest;
+ q->servPort = zeroIPPort; // Clear servPort so that startLLQHandshake will retry the GetZoneData processing
+ q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry LLQ setup in approx 15 minutes
+ q->LastQTime = m->timenow;
+ SetNextQueryTime(m, q);
+ return uDNS_LLQ_Entire; // uDNS_LLQ_Entire means flush stale records; assume a large effective TTL
+ }
+ else if (opt && q->state == LLQ_Established && opt->OptData.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
+ {
+ mDNSu8 *ackEnd;
+ if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
+ //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags);
+ ackEnd = putLLQ(&m->omsg, m->omsg.data, mDNSNULL, &opt->OptData.llq, mDNSfalse);
+ if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, srcaddr, srcport, mDNSNULL, mDNSNULL);
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ return uDNS_LLQ_Events;
+ }
+ if (opt && mDNSSameOpaque16(msg->h.id, q->TargetQID))
+ {
+ if (q->state == LLQ_Established && opt->OptData.llq.llqOp == kLLQOp_Refresh && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id) && msg->h.numAdditionals && !msg->h.numAnswers)
{
- if (opt->OptData.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
+ if (opt->OptData.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->OptData.llq.err);
+ else
{
- mDNSu8 *ackEnd;
- if (q->LongLived && q->state == LLQ_Poll && !mDNSIPPortIsZero(q->servPort)) q->ThisQInterval = LLQ_POLL_INTERVAL;
- else if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
- //debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags);
- ackEnd = putLLQ(&m->omsg, m->omsg.data, mDNSNULL, &opt->OptData.llq, mDNSfalse);
- if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, srcaddr, srcport, mDNSNULL, mDNSNULL);
- m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
- return uDNS_LLQ_Events;
+ //LogOperation("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
+ GrantCacheExtensions(m, q, opt->OptData.llq.llqlease);
+ SetLLQTimer(m, q, &opt->OptData.llq);
+ q->ntries = 0;
}
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ return uDNS_LLQ_Ignore;
}
- if (mDNSSameOpaque16(msg->h.id, q->TargetQID))
+ if (q->state < LLQ_Established && mDNSSameAddress(srcaddr, &q->servAddr))
{
- if (opt->OptData.llq.llqOp == kLLQOp_Refresh && q->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers && mDNSSameOpaque64(&opt->OptData.llq.id, &q->id))
- {
- if (opt->OptData.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->OptData.llq.err);
- else
- {
- //LogOperation("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
- GrantCacheExtensions(m, q, opt->OptData.llq.llqlease);
- q->expire = q->LastQTime + ((mDNSs32)opt->OptData.llq.llqlease * mDNSPlatformOneSecond );
- q->ThisQInterval = ((mDNSs32)opt->OptData.llq.llqlease * (mDNSPlatformOneSecond/2));
- q->origLease = opt->OptData.llq.llqlease;
- q->state = LLQ_Established;
- }
- m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
- return uDNS_LLQ_Events;
- }
- if (q->state < LLQ_Static)
- {
- if (mDNSSameAddress(srcaddr, &q->servAddr))
- {
- LLQ_State oldstate = q->state;
- recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, opt);
- m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
- //DumpPacket(m, msg, end);
- // If this is our Ack+Answers packet resulting from a correct challenge response, then it's a full list
- // of answers, and any cache answers we have that are not included in this packet need to be flushed
- //LogMsg("uDNS_recvLLQResponse: recvSetupResponse state %d returning %d", oldstate, oldstate != LLQ_SecondaryRequest);
- return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Setup : uDNS_LLQ_Events);
- }
- }
+ LLQ_State oldstate = q->state;
+ recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, &opt->OptData.llq);
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ // We have a protocol anomaly here in the LLQ definition.
+ // Both the challenge packet from the server and the ack+answers packet have opt->OptData.llq.llqOp == kLLQOp_Setup.
+ // However, we need to treat them differently:
+ // The challenge packet has no answers in it, and tells us nothing about whether our cache entries
+ // are still valid, so this packet should not cause us to do anything that messes with our cache.
+ // The ack+answers packet gives us the whole truth, so we should handle it by updating our cache
+ // to match the answers in the packet, and only the answers in the packet.
+ return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Entire : uDNS_LLQ_Ignore);
}
}
}
- m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
- //LogMsg("uDNS_recvLLQResponse: returning 0");
}
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
}
return uDNS_LLQ_Not;
}
-// Forward declaration
-mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease);
-
// Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
mDNSBool closed = mDNSfalse;
mDNSu8 *end;
mDNS *m = tcpInfo->m;
- DomainAuthInfo *AuthInfo =
- tcpInfo->question ? tcpInfo->question->AuthInfo :
- tcpInfo->srs ? GetAuthInfoForName(m, tcpInfo->srs->RR_SRV.resrec.name) :
- tcpInfo->rr ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL;
tcpInfo_t **backpointer =
tcpInfo->question ? &tcpInfo->question->tcp :
if (ConnectionEstablished)
{
+ DomainAuthInfo *AuthInfo;
// connection is established - send the message
- if (tcpInfo->question && tcpInfo->question->LongLived && (tcpInfo->question->state == LLQ_Established || tcpInfo->question->state == LLQ_Refresh))
+ if (tcpInfo->question && tcpInfo->question->LongLived && tcpInfo->question->state == LLQ_Established)
{
//LogMsg("tcpCallback calling sendLLQRefresh %##s (%s)", tcpInfo->question->qname.c, DNSTypeName(tcpInfo->question->qtype));
mDNS_Lock(m);
mDNS_Unlock(m);
return;
}
- else if (tcpInfo->question && tcpInfo->question->LongLived && tcpInfo->question->state != LLQ_Poll)
+ else if (tcpInfo->question && tcpInfo->question->LongLived && tcpInfo->question->state != LLQ_Poll && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort))
{
+ // Notes:
+ // If we have a NAT port mapping, ExternalPort is the external port
+ // If we have a routable address so we don't need a port mapping, ExternalPort is the same as our own internal port
+ // If we need a NAT port mapping but can't get one, then ExternalPort is zero
LLQOptData llqData; // set llq rdata
llqData.vers = kLLQ_Vers;
llqData.llqOp = kLLQOp_Setup;
- llqData.err = LLQErr_NoError;
- llqData.err = mDNSVal16(tcpInfo->question->eventPort); // Tell server our external NAT-mapped UDP port
- if (llqData.err == 0) llqData.err = 5353; // If not using NAT, then we need events sent directly to 5353
+ llqData.err = GetLLQEventPort(m, &tcpInfo->Addr); // We're using TCP; tell server what UDP port to send notifications to
LogOperation("tcpCallback: eventPort %d", llqData.err);
llqData.id = zeroOpaque64;
llqData.llqlease = kLLQ_DefLease;
InitializeDNSMessage(&tcpInfo->request.h, tcpInfo->question->TargetQID, uQueryFlags);
- //LogMsg("tcpCallback: putLLQ %p", AuthInfo);
end = putLLQ(&tcpInfo->request, tcpInfo->request.data, tcpInfo->question, &llqData, mDNStrue);
-
if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; }
}
else if (tcpInfo->question)
{
- err = constructQueryMsg(&tcpInfo->request, &end, tcpInfo->question);
- if (err) { LogMsg("ERROR: tcpCallback: constructQueryMsg - %ld", err); err = mStatus_UnknownErr; goto exit; }
+ DNSQuestion *q = tcpInfo->question;
+ InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags);
+ end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
}
else
end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen;
+ // Defensive coding for <rdar://problem/5546824> Crash in mDNSResponder at GetAuthInfoForName_internal + 366
+ // Don't know yet what's causing this, but at least we can be cautious and try to avoid crashing if we find our pointers in an unexpected state
+ if (tcpInfo->srs && tcpInfo->srs->RR_SRV.resrec.name != &tcpInfo->srs->RR_SRV.namestorage)
+ LogMsg("tcpCallback: ERROR: tcpInfo->srs->RR_SRV.resrec.name %p != &tcpInfo->srs->RR_SRV.namestorage %p",
+ tcpInfo->srs->RR_SRV.resrec.name, &tcpInfo->srs->RR_SRV.namestorage);
+ if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage)
+ LogMsg("tcpCallback: ERROR: tcpInfo->rr->resrec.name %p != &tcpInfo->rr->namestorage %p",
+ tcpInfo->rr->resrec.name, &tcpInfo->rr->namestorage);
+ if (tcpInfo->srs && tcpInfo->srs->RR_SRV.resrec.name != &tcpInfo->srs->RR_SRV.namestorage) return;
+ if (tcpInfo->rr && tcpInfo->rr-> resrec.name != &tcpInfo->rr-> namestorage) return;
+
+ AuthInfo = tcpInfo->question ? tcpInfo->question->AuthInfo :
+ tcpInfo->srs ? GetAuthInfoForName(m, tcpInfo->srs->RR_SRV.resrec.name) :
+ tcpInfo->rr ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name) : mDNSNULL;
+
err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo);
if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %ld", err); err = mStatus_UnknownErr; goto exit; }
// we won't end up double-disposing our tcpInfo_t
*backpointer = mDNSNULL;
+ mDNS_Lock(m); // Need to grab the lock to get m->timenow
+
if (tcpInfo->question)
{
DNSQuestion *q = tcpInfo->question;
- mDNS_Lock(m); // Need to grab the lock to get m->timenow
if (q->ThisQInterval == 0 || q->LastQTime + q->ThisQInterval - m->timenow > MAX_UCAST_POLL_INTERVAL)
{
q->LastQTime = m->timenow;
q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
SetNextQueryTime(m, q);
}
- mDNS_Unlock(m);
// ConnFailed is actually okay. It just means that the server closed the connection but the LLQ is still okay.
// If the error isn't ConnFailed, then the LLQ is in bad shape.
- if (err != mStatus_ConnFailed) tcpInfo->question->state = LLQ_Error;
- }
-
- if (tcpInfo->rr)
- {
- mDNSBool deregPending = (tcpInfo->rr->state == regState_DeregPending) ? mDNStrue : mDNSfalse;
-
- UnlinkAuthRecord(m, tcpInfo->rr);
- tcpInfo->rr->state = regState_Unregistered;
-
- if (!deregPending)
- {
- // Right now tcpCallback does not run holding the lock, so no need to drop the lock
- //mDNS_DropLockBeforeCallback();
- if (tcpInfo->rr->RecordCallback)
- tcpInfo->rr->RecordCallback(m, tcpInfo->rr, err);
- //mDNS_ReclaimLockAfterCallback();
- // NOTE: not safe to touch any client structures here --
- // once we issue the callback, client is free to reuse or deallocate the srs memory
- }
+ if (err != mStatus_ConnFailed) StartLLQPolling(m, tcpInfo->question);
}
- if (tcpInfo->srs)
- {
- mDNSBool deregPending = (tcpInfo->srs->state == regState_DeregPending) ? mDNStrue : mDNSfalse;
+ if (tcpInfo->rr) SetRecordRetry(m, tcpInfo->rr, mStatus_NoError);
- unlinkSRS(m, tcpInfo->srs);
- tcpInfo->srs->state = regState_Unregistered;
+ if (tcpInfo->srs) SetRecordRetry(m, &tcpInfo->srs->RR_SRV, mStatus_NoError);
- if (!deregPending)
- {
- // Right now tcpCallback does not run holding the lock, so no need to drop the lock
- //mDNS_DropLockBeforeCallback();
- tcpInfo->srs->ServiceCallback(m, tcpInfo->srs, err);
- //mDNS_ReclaimLockAfterCallback();
- // NOTE: not safe to touch any client structures here --
- // once we issue the callback, client is free to reuse or deallocate the srs memory
- }
- }
+ mDNS_Unlock(m);
DisposeTCPConn(tcpInfo);
}
if (!info->sock) { LogMsg("SendServiceRegistration: uanble to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); }
err = mDNSPlatformTCPConnect(info->sock, Addr, Port, 0, tcpCallback, info);
+ // Probably suboptimal here.
+ // Instead of returning mDNSNULL here on failure, we should probably invoke the callback with an error code.
+ // That way clients can put all the error handling and retry/recovery code in one place,
+ // instead of having to handle immediate errors in one place and async errors in another.
+ // Also: "err == mStatus_ConnEstablished" probably never happens.
+
+ // Don't need to log "connection failed" in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
if (err == mStatus_ConnEstablished) { tcpCallback(info->sock, info, mDNStrue, mStatus_NoError); }
- else if (err != mStatus_ConnPending ) { LogMsg("MakeTCPConnection: connection failed"); mDNSPlatformMemFree(info); return(mDNSNULL); }
+ else if (err != mStatus_ConnPending ) { LogOperation("MakeTCPConnection: connection failed"); mDNSPlatformMemFree(info); return(mDNSNULL); }
return(info);
}
mDNSPlatformMemFree(tcp);
}
-mDNSlocal void RemoveLLQNatMappings(mDNS *m, DNSQuestion *q)
+// Lock must be held
+mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
{
- if (q->NATInfoUDP.clientContext)
+ if (mDNSIPv4AddressIsOnes(m->LLQNAT.ExternalAddress))
{
- mDNS_StopNATOperation_internal(m, &q->NATInfoUDP);
- q->NATInfoUDP.clientContext = mDNSNULL;
+ LogOperation("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes
+ q->LastQTime = m->timenow;
+ SetNextQueryTime(m, q);
+ return;
}
- }
-mDNSlocal void startLLQHandshake(mDNS *m, DNSQuestion *q)
- {
- mStatus err = mStatus_NoError;
- if (q->AuthInfo)
+ if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort))
{
- LogOperation("startLLQHandshakePrivate Addr %#a%s Server %#a:%d%s %##s (%s) eventport %d",
- &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "",
- &q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "",
- q->qname.c, DNSTypeName(q->qtype), mDNSVal16(q->eventPort));
-
- if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&q->servAddr))
- if (q->state == LLQ_InitialRequest)
- {
- // start
- if (!q->NATInfoUDP.clientContext) { q->state = LLQ_NatMapWaitUDP; StartLLQNatMap(m, q); goto exit; }
- else
- {
- LogMsg("startLLQHandshake state == LLQ_InitialRequest but already have NATInfoUDP.clientContext %##s (%s)",
- q->qname.c, DNSTypeName(q->qtype));
- err = mStatus_UnknownErr;
- goto exit;
- }
- }
-
- LogOperation("startLLQHandshake TCP %p %##s (%s)", q->tcp, q->qname.c, DNSTypeName(q->qtype));
- if (q->tcp) LogMsg("startLLQHandshake: Already have TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- else q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, q, mDNSNULL, mDNSNULL);
+ LogOperation("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ StartLLQPolling(m, q);
+ return;
+ }
- // update question state
- //q->state = LLQ_InitialRequest;
- q->state = LLQ_SecondaryRequest; // Right now, for private DNS, we skip the four-way LLQ handshake
- q->origLease = kLLQ_DefLease;
- q->ThisQInterval = 0;
+ if (mDNSIPPortIsZero(q->servPort))
+ {
+ LogOperation("startLLQHandshake: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10); // Retry in approx 15 minutes
q->LastQTime = m->timenow;
SetNextQueryTime(m, q);
+ q->servAddr = zeroAddr;
+ q->servPort = zeroIPPort;
+ if (q->nta) CancelGetZoneData(m, q->nta);
+ q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q);
+ return;
+ }
- err = mStatus_NoError;
+ if (q->AuthInfo)
+ {
+ if (q->tcp) LogOperation("startLLQHandshake: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ if (q->tcp) DisposeTCPConn(q->tcp);
+ q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, q, mDNSNULL, mDNSNULL);
+ if (!q->tcp)
+ q->ThisQInterval = mDNSPlatformOneSecond * 5; // If TCP failed (transient networking glitch) try again in five seconds
+ else
+ {
+ q->state = LLQ_SecondaryRequest; // Right now, for private DNS, we skip the four-way LLQ handshake
+ q->origLease = kLLQ_DefLease;
+ q->ThisQInterval = 0;
+ }
+ q->LastQTime = m->timenow;
+ SetNextQueryTime(m, q);
}
else
{
- mDNSu8 *end;
- LLQOptData llqData;
-
- LogOperation("startLLQHandshake Addr %#a%s Server %#a:%d%s %##s (%s) RequestedPort %d",
+ LogOperation("startLLQHandshake m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)",
&m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "",
&q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr) ? " (RFC 1918)" : "",
- q->qname.c, DNSTypeName(q->qtype), mDNSVal16(q->NATInfoUDP.RequestedPort));
+ q->qname.c, DNSTypeName(q->qtype));
- if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&q->servAddr))
+ if (q->ntries++ >= kLLQ_MAX_TRIES)
{
- // start
- if (!q->NATInfoUDP.clientContext) { q->state = LLQ_NatMapWaitUDP; StartLLQNatMap(m, q); }
- else { err = mStatus_UnknownErr; goto exit; }
+ LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c);
+ StartLLQPolling(m, q);
}
-
- if (q->ntries++ >= kLLQ_MAX_TRIES)
- { LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c); err = mStatus_UnknownErr; goto exit; }
-
- // set llq rdata
- llqData.vers = kLLQ_Vers;
- llqData.llqOp = kLLQOp_Setup;
- llqData.err = LLQErr_NoError;
- llqData.id = zeroOpaque64;
- llqData.llqlease = kLLQ_DefLease;
-
- InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
- end = putLLQ(&m->omsg, m->omsg.data, q, &llqData, mDNStrue);
- if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); q->state = LLQ_Error; return; }
-
- err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL);
- // on error, we procede as normal and retry after the appropriate interval
- if (err) { debugf("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", err); err = mStatus_NoError; }
-
- // update question state
- q->state = LLQ_InitialRequest;
- q->origLease = kLLQ_DefLease;
- q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
- q->LastQTime = m->timenow - q->ThisQInterval;
- SetNextQueryTime(m, q);
-
- err = mStatus_NoError;
- }
-
-exit:
- if (err)
- {
- LogOperation("startLLQHandshake error %d ", err);
- StartLLQPolling(m, q);
- }
- }
-
-// Called in normal client context (lock not held)
-mDNSlocal void LLQNatMapComplete(mDNS *m, NATTraversalInfo *n)
- {
- if (m->CurrentQuestion)
- LogMsg("LLQNatMapComplete: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
- m->CurrentQuestion = m->Questions;
-
- while (m->CurrentQuestion)
- {
- DNSQuestion *q = m->CurrentQuestion;
- m->CurrentQuestion = m->CurrentQuestion->next;
- if (q->LongLived)
+ else
{
- if (q->state == LLQ_NatMapWaitUDP)
- {
- if (!n->Result)
- {
- q->state = LLQ_GetZoneInfo;
- q->eventPort = n->ExternalPort;
- startLLQHandshake(m, q);
- }
- else
- {
- LogMsg("LLQNatMapComplete error %d Internal %d Requested %d External %d", n->Result,
- mDNSVal16(q->NATInfoUDP.IntPort), mDNSVal16(q->NATInfoUDP.RequestedPort), mDNSVal16(q->NATInfoUDP.ExternalPort));
- RemoveLLQNatMappings(m, q);
- StartLLQPolling(m, q);
- }
- }
+ mDNSu8 *end;
+ LLQOptData llqData;
+
+ // set llq rdata
+ llqData.vers = kLLQ_Vers;
+ llqData.llqOp = kLLQOp_Setup;
+ llqData.err = LLQErr_NoError; // Don't need to tell server UDP notification port when sending over UDP
+ llqData.id = zeroOpaque64;
+ llqData.llqlease = kLLQ_DefLease;
+
+ InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+ end = putLLQ(&m->omsg, m->omsg.data, q, &llqData, mDNStrue);
+ if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; }
+
+ mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL);
+
+ // update question state
+ q->state = LLQ_InitialRequest;
+ q->origLease = kLLQ_DefLease;
+ q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
+ q->LastQTime = m->timenow;
+ SetNextQueryTime(m, q);
}
}
- m->CurrentQuestion = mDNSNULL;
}
-// if we ever want to refine support for multiple hostnames, we can add logic matching service names to a particular hostname
-// for now, we grab the first registered DynDNS name, if any, or a static name we learned via a reverse-map query
mDNSexport const domainname *GetServiceTarget(mDNS *m, ServiceRecordSet *srs)
{
LogOperation("GetServiceTarget %##s", srs->RR_SRV.resrec.name->c);
return(&srs->RR_SRV.resrec.rdata->u.srv.target);
else
{
- HostnameInfo *hi = m->Hostnames;
-
#if APPLE_OSX_mDNSResponder
DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name);
if (AuthInfo && AuthInfo->AutoTunnel)
{
- if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0)
- {
- if (m->AutoTunnelHostAddr.b[0]) SetupLocalAutoTunnelInterface_internal(m);
- return(mDNSNULL);
- }
+ // If this AutoTunnel is not yet active, start it now (which entails activating its NAT Traversal request,
+ // which will subsequently advertise the appropriate records when the NAT Traversal returns a result)
+ if (!AuthInfo->AutoTunnelNAT.clientContext && m->AutoTunnelHostAddr.b[0])
+ SetupLocalAutoTunnelInterface_internal(m);
+ if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL);
return(&AuthInfo->AutoTunnelHostRecord.namestorage);
}
+ else
#endif APPLE_OSX_mDNSResponder
-
- while (hi)
{
- if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh) return(hi->arv4.resrec.name);
- if (hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) return(hi->arv6.resrec.name);
- hi = hi->next;
+ const int srvcount = CountLabels(srs->RR_SRV.resrec.name);
+ HostnameInfo *besthi = mDNSNULL, *hi;
+ int best = 0;
+ for (hi = m->Hostnames; hi; hi = hi->next)
+ if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh ||
+ hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh)
+ {
+ int x, hostcount = CountLabels(&hi->fqdn);
+ for (x = hostcount < srvcount ? hostcount : srvcount; x > 0 && x > best; x--)
+ if (SameDomainName(SkipLeadingLabels(srs->RR_SRV.resrec.name, srvcount - x), SkipLeadingLabels(&hi->fqdn, hostcount - x)))
+ { best = x; besthi = hi; }
+ }
+
+ if (besthi) return(&besthi->fqdn);
}
if (m->StaticHostname.c[0]) return(&m->StaticHostname);
return(mDNSNULL);
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("SendServiceRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
- if (mDNSIPv4AddressIsZero(srs->ns.ip.v4)) { LogMsg("SendServiceRegistration - NS not set!"); return; }
+ if (mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4)) // Don't know our UpdateServer yet
+ {
+ srs->RR_SRV.LastAPTime = m->timenow;
+ if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 5)
+ srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5;
+ return;
+ }
+
+ if (srs->state == regState_Registered) srs->state = regState_Refresh;
id = mDNS_NewMessageID(m);
InitializeDNSMessage(&m->omsg.h, id, UpdateReqFlags);
if (!(ptr = PutResourceRecordTTLJumbo(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) { err = mStatus_UnknownErr; goto exit; }
target = GetServiceTarget(m, srs);
- if (!target)
+ if (!target || target->c[0] == 0)
{
- debugf("SendServiceRegistration - no target for %##s", srs->RR_SRV.resrec.name->c);
+ LogOperation("SendServiceRegistration - no target for %##s", srs->RR_SRV.resrec.name->c);
srs->state = regState_NoTarget;
return;
}
if (srs->Private)
{
- LogOperation("SendServiceRegistration TCP %p %s", srs->tcp, ARDisplayString(m, &srs->RR_SRV));
- if (srs->tcp) LogMsg("SendServiceRegistration: Already have TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
- else srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->ns, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
- srs->RR_SRV.LastAPTime = m->timenow;
- srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
+ if (srs->tcp) LogOperation("SendServiceRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
+ if (srs->tcp) DisposeTCPConn(srs->tcp);
+ srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
+ if (!srs->tcp) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+ else if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 30) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 30;
}
else
{
- err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->ns, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
+ err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
if (err) debugf("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err);
- SetRecordRetry(m, &srs->RR_SRV, err);
}
- err = mStatus_NoError;
+ SetRecordRetry(m, &srs->RR_SRV, err);
+ return;
exit:
if (err)
{
- LogMsg("SendServiceRegistration - Error formatting message %d", err);
+ LogMsg("SendServiceRegistration ERROR formatting message %d!! Permanently abandoning service registration %##s", err, srs->RR_SRV.resrec.name->c);
unlinkSRS(m, srs);
srs->state = regState_Unregistered;
mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype);
// GetZoneData_QuestionCallback is called from normal client callback context (core API calls allowed)
-mDNSexport void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+mDNSlocal void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
{
ZoneData *zd = (ZoneData*)question->QuestionContext;
}
else
{
- LogMsg("ERROR: GetZoneData_QuestionCallback - recursed to root label of %##s without finding SOA", zd->ChildName.c);
+ LogOperation("GetZoneData recursed to root label of %##s without finding SOA", zd->ChildName.c);
zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd);
mDNSPlatformMemFree(zd);
}
{
if (qtype == kDNSType_SRV)
{
- LogOperation("lookupDNSPort %##s", ZoneDataSRV(zd));
AssignDomainName(&zd->question.qname, ZoneDataSRV(zd));
AppendDomainName(&zd->question.qname, &zd->ZoneName);
+ LogOperation("lookupDNSPort %##s", zd->question.qname.c);
}
zd->question.ThisQInterval = -1; // So that GetZoneData_QuestionCallback() knows whether to cancel this question (Is this necessary?)
return zd;
}
-// if LLQ NAT context unreferenced, delete the mapping
-mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m)
+// GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
+// because that would result in an infinite loop (i.e. to do a private query we first need to get
+// the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
+// we'd need to already know the _dns-query-tls SRV record.
+// Also, as a general rule, we never do SOA queries privately
+mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q) // Must be called with lock held
{
- NATTraversalInfo *n = m->NATTraversals;
- DNSQuestion *q;
-
- while (n)
- {
- NATTraversalInfo *current = n;
- n = n->next;
- if (current->clientCallback == LLQNatMapComplete)
- {
- for (q = m->Questions; q; q = q->next) if (&q->NATInfoUDP == current) break;
- if (!q) mDNS_StopNATOperation_internal(m, current);
- }
- }
+ if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNSNULL);
+ if (q->qtype == kDNSType_SOA ) return(mDNSNULL);
+ return(GetAuthInfoForName_internal(m, &q->qname));
}
// ***************************************************************************
if (!srs) { LogMsg("CompleteSRVNatMap called with unknown ServiceRecordSet object"); return; }
if (!n->NATLease) return;
- if (n->Result)
- {
- HostnameInfo *hi = m->Hostnames;
- while (hi)
- {
- if (hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh) break;
- else hi = hi->next;
- }
-
- if (hi)
- {
- debugf("Port map failed for service %##s - using IPv6 service target", srs->RR_SRV.resrec.name->c);
- mDNS_StopNATOperation(m, &srs->NATinfo);
- goto register_service;
- }
- else srs->state = regState_NATError;
- }
-
- register_service:
mDNS_Lock(m);
- if (!mDNSIPv4AddressIsZero(srs->ns.ip.v4))
+ if (!mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4))
SendServiceRegistration(m, srs); // non-zero server address means we already have necessary zone data to send update
else
{
// SHOULD NEVER HAPPEN!
- LogOperation("ERROR: CompleteSRVNatMap called but srs->ns.ip.v4 is zero!");
+ LogOperation("ERROR: CompleteSRVNatMap called but srs->SRSUpdateServer.ip.v4 is zero!");
srs->state = regState_FetchingZoneData;
if (srs->nta) CancelGetZoneData(m, srs->nta); // Make sure we cancel old one before we start a new one
- srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
+ srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, srs);
}
mDNS_Unlock(m);
}
{
mDNSu8 *p = srs->RR_PTR.resrec.name->c;
if (p[0]) p += 1 + p[0];
-
if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) srs->NATinfo.Protocol = NATOp_MapTCP;
else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) srs->NATinfo.Protocol = NATOp_MapUDP;
- else { LogMsg("StartSRVNatMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name->c); goto error; }
+ else { LogMsg("StartSRVNatMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name->c); return; }
+
+ if (srs->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &srs->NATinfo);
srs->NATinfo.IntPort = srs->RR_SRV.resrec.rdata->u.srv.port;
srs->NATinfo.RequestedPort = srs->RR_SRV.resrec.rdata->u.srv.port;
srs->NATinfo.NATLease = 0; // Request default lease
srs->NATinfo.clientCallback = CompleteSRVNatMap;
srs->NATinfo.clientContext = srs;
mDNS_StartNATOperation_internal(m, &srs->NATinfo);
- return;
-
- error:
- if (srs->nta) CancelGetZoneData(m, srs->nta); // Make sure we cancel old one before we start a new one
- srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
}
// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
-mDNSexport void ServiceRegistrationZoneDataComplete(mDNS *const m, mStatus err, const ZoneData *zoneData)
+mDNSexport void ServiceRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData)
{
ServiceRecordSet *srs = (ServiceRecordSet *)zoneData->ZoneDataContext;
if (m->mDNS_busy != m->mDNS_reentrancy)
- LogMsg("ServiceRegistrationZoneDataComplete: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+ LogMsg("ServiceRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
srs->nta = mDNSNULL;
- if (err) goto error;
- if (!zoneData) { LogMsg("ERROR: ServiceRegistrationZoneDataComplete invoked with NULL result and no error"); goto error; }
-
- if (srs->RR_SRV.resrec.rrclass != zoneData->ZoneClass)
- { LogMsg("Service %##s - class does not match zone", srs->RR_SRV.resrec.name->c); goto error; }
+ if (err || !zoneData) return;
// cache zone data
AssignDomainName(&srs->zone, &zoneData->ZoneName);
- srs->ns.type = mDNSAddrType_IPv4;
- srs->ns = zoneData->Addr;
+ srs->SRSUpdateServer.type = mDNSAddrType_IPv4;
+ srs->SRSUpdateServer = zoneData->Addr;
if (!mDNSIPPortIsZero(zoneData->Port))
{
srs->SRSUpdatePort = zoneData->Port;
- srs->Private = zoneData->ZonePrivate;
+ srs->Private = zoneData->ZonePrivate;
}
else
{
srs->srs_uselease = mDNSfalse;
}
- LogOperation("ServiceRegistrationZoneDataComplete %#a %d %#a %d", &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4), &srs->ns, mDNSAddrIsRFC1918(&srs->ns));
+ LogOperation("ServiceRegistrationGotZoneData My IPv4 %#a%s Server %#a:%d%s for %##s",
+ &m->AdvertisedV4, mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC1918)" : "",
+ &srs->SRSUpdateServer, mDNSVal16(srs->SRSUpdatePort), mDNSAddrIsRFC1918(&srs->SRSUpdateServer) ? " (RFC1918)" : "",
+ srs->RR_SRV.resrec.name->c);
if (!mDNSIPPortIsZero(srs->RR_SRV.resrec.rdata->u.srv.port) &&
- mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->ns) &&
+ mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->SRSUpdateServer) &&
srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
{
srs->state = regState_NATMap;
- LogOperation("ServiceRegistrationZoneDataComplete StartSRVNatMap");
+ LogOperation("ServiceRegistrationGotZoneData StartSRVNatMap");
StartSRVNatMap(m, srs);
}
else
SendServiceRegistration(m, srs);
mDNS_Unlock(m);
}
- return;
-
-error:
- unlinkSRS(m, srs);
- srs->state = regState_Unregistered;
-
- // Don't need to do the mDNS_DropLockBeforeCallback stuff here, because this code is
- // *already* being invoked in the right callback context, with mDNS_reentrancy correctly incremented.
- srs->ServiceCallback(m, srs, err);
- // CAUTION: MUST NOT do anything more with rr after calling srs->ServiceCallback(), because the client's callback function
- // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
}
mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs)
mStatus err = mStatus_UnknownErr;
mDNSu32 i;
+ if (mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4)) // Don't know our UpdateServer yet
+ {
+ srs->RR_SRV.LastAPTime = m->timenow;
+ if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 5)
+ srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5;
+ return;
+ }
+
id = mDNS_NewMessageID(m);
InitializeDNSMessage(&m->omsg.h, id, UpdateReqFlags);
if (srs->Private)
{
- LogOperation("SendServiceDeregistration TCP %p %s", srs->tcp, ARDisplayString(m, &srs->RR_SRV));
- if (srs->tcp) LogMsg("SendServiceDeregistration: Already have TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
- else srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->ns, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
- srs->RR_SRV.LastAPTime = m->timenow;
- srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
+ if (srs->tcp) LogOperation("SendServiceDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, &srs->RR_SRV));
+ if (srs->tcp) DisposeTCPConn(srs->tcp);
+ srs->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, srs, mDNSNULL);
+ if (!srs->tcp) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+ else if (srs->RR_SRV.ThisAPInterval < mDNSPlatformOneSecond * 30) srs->RR_SRV.ThisAPInterval = mDNSPlatformOneSecond * 30;
}
else
{
- err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->ns, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
+ err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &srs->SRSUpdateServer, srs->SRSUpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, srs->RR_SRV.resrec.name));
if (err && err != mStatus_TransientErr) { debugf("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto exit; }
- SetRecordRetry(m, &srs->RR_SRV, err);
}
- err = mStatus_NoError;
+ SetRecordRetry(m, &srs->RR_SRV, err);
+ return;
exit:
if (err)
{
+ LogMsg("SendServiceDeregistration ERROR formatting message %d!! Permanently abandoning service registration %##s", err, srs->RR_SRV.resrec.name->c);
unlinkSRS(m, srs);
srs->state = regState_Unregistered;
}
const domainname *const nt = GetServiceTarget(m, srs);
const domainname *const newtarget = nt ? nt : (domainname*)"";
mDNSBool TargetChanged = (newtarget->c[0] && srs->state == regState_NoTarget) || !SameDomainName(curtarget, newtarget);
- mDNSBool HaveZoneData = !mDNSIPv4AddressIsZero(srs->ns.ip.v4);
+ mDNSBool HaveZoneData = !mDNSIPv4AddressIsZero(srs->SRSUpdateServer.ip.v4);
// Nat state change if:
// We were behind a NAT, and now we are behind a new NAT, or
// We were not behind a NAT and now we are
mDNSIPPort port = srs->RR_SRV.resrec.rdata->u.srv.port;
- mDNSBool NowBehindNAT = (!mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->ns));
+ mDNSBool NowNeedNATMAP = (srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP && !mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && !mDNSAddrIsRFC1918(&srs->SRSUpdateServer));
mDNSBool WereBehindNAT = (srs->NATinfo.clientContext != mDNSNULL);
mDNSBool PortWasMapped = (srs->NATinfo.clientContext && !mDNSSameIPPort(srs->NATinfo.RequestedPort, port)); // I think this is always false -- SC Sept 07
- mDNSBool NATChanged = (!WereBehindNAT && NowBehindNAT) || (!NowBehindNAT && PortWasMapped);
+ mDNSBool NATChanged = (!WereBehindNAT && NowNeedNATMAP) || (!NowNeedNATMAP && PortWasMapped);
- LogOperation("UpdateSRV %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowBehindNAT %d WereBehindNAT %d PortWasMapped %d NATChanged %d",
+ LogOperation("UpdateSRV %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowNeedNATMAP %d WereBehindNAT %d PortWasMapped %d NATChanged %d",
srs->RR_SRV.resrec.name->c, newtarget,
- TargetChanged, HaveZoneData, mDNSVal16(port), NowBehindNAT, WereBehindNAT, PortWasMapped, NATChanged);
+ TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged);
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("UpdateSRV: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
case regState_NoTarget:
if (newtarget->c[0])
{
- debugf("UpdateSRV: %s service %##s", HaveZoneData ? (NATChanged && NowBehindNAT ? "Starting Port Map for" : "Registering") : "Getting Zone Data for", srs->RR_SRV.resrec.name->c);
+ debugf("UpdateSRV: %s service %##s", HaveZoneData ? (NATChanged && NowNeedNATMAP ? "Starting Port Map for" : "Registering") : "Getting Zone Data for", srs->RR_SRV.resrec.name->c);
if (!HaveZoneData)
{
srs->state = regState_FetchingZoneData;
if (srs->nta) CancelGetZoneData(m, srs->nta); // Make sure we cancel old one before we start a new one
- srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationZoneDataComplete, srs);
+ srs->nta = StartGetZoneData(m, srs->RR_SRV.resrec.name, ZoneServiceUpdate, ServiceRegistrationGotZoneData, srs);
}
else
{
- if (srs->NATinfo.clientContext && (NATChanged || !NowBehindNAT))
+ if (srs->NATinfo.clientContext && (NATChanged || !NowNeedNATMAP))
{
mDNS_StopNATOperation_internal(m, &srs->NATinfo);
srs->NATinfo.clientContext = mDNSNULL;
}
- if (NATChanged && NowBehindNAT && srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
+ if (NATChanged && NowNeedNATMAP && srs->RR_SRV.AutoTarget == Target_AutoHostAndNATMAP)
{ srs->state = regState_NATMap; StartSRVNatMap(m, srs); }
else SendServiceRegistration(m, srs);
}
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+ if (mDNSIPv4AddressIsZero(rr->UpdateServer.ip.v4)) // Don't know our UpdateServer yet
+ {
+ rr->LastAPTime = m->timenow;
+ if (rr->ThisAPInterval < mDNSPlatformOneSecond * 5)
+ rr->ThisAPInterval = mDNSPlatformOneSecond * 5;
+ return;
+ }
+
rr->RequireGoodbye = mDNStrue;
rr->id = mDNS_NewMessageID(m);
InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
if (rr->Private)
{
LogOperation("SendRecordRegistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
- if (rr->tcp) LogMsg("SendRecordRegistration: Already have TCP connection for %s", ARDisplayString(m, rr));
- else rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
- rr->LastAPTime = m->timenow;
- rr->ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
+ if (rr->tcp) LogOperation("SendRecordRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr));
+ if (rr->tcp) DisposeTCPConn(rr->tcp);
+ rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
+ if (!rr->tcp) rr->ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+ else if (rr->ThisAPInterval < mDNSPlatformOneSecond * 30) rr->ThisAPInterval = mDNSPlatformOneSecond * 30;
}
else
{
err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %ld", err);
- SetRecordRetry(m, rr, err);
}
+ SetRecordRetry(m, rr, err);
+
if (rr->state != regState_Refresh && rr->state != regState_DeregDeferred && rr->state != regState_UpdatePending)
rr->state = regState_Pending;
- err = mStatus_NoError;
+ return;
exit:
-
- if (err)
- {
- LogMsg("SendRecordRegistration: Error formatting message %d", err);
-
- if (rr->state != regState_Unregistered)
- {
- UnlinkAuthRecord(m, rr);
- rr->state = regState_Unregistered;
- }
-
- mDNS_DropLockBeforeCallback();
- if (rr->RecordCallback)
- rr->RecordCallback(m, rr, err);
- mDNS_ReclaimLockAfterCallback();
- // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
- // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
- }
+ LogMsg("SendRecordRegistration: Error formatting message for %s", ARDisplayString(m, rr));
}
// Called with lock held
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("hndlServiceUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+ SetRecordRetry(m, &srs->RR_SRV, mStatus_NoError);
+
switch (srs->state)
{
case regState_Pending:
{
srs->TestForSelfConflict = mDNSfalse;
if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict; // NoSuchRecord implies that our prereq was not met, so we actually have a name conflict
- if (err) srs->state = regState_Unregistered;
- else srs->state = regState_Registered;
+ if (!err) srs->state = regState_Registered;
InvokeCallback = mDNStrue;
break;
}
else
{
//!!!KRS make sure all structs will still get cleaned up when client calls DeregisterService with this state
- if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name->c); srs->state = regState_Unregistered; }
+ if (err) LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name->c);
else srs->state = regState_Registered;
InvokeCallback = mDNStrue;
break;
{
LogMsg("Error %ld for refresh of service %##s", err, srs->RR_SRV.resrec.name->c);
InvokeCallback = mDNStrue;
- srs->state = regState_Unregistered;
}
else srs->state = regState_Registered;
break;
if (err)
{
LogMsg("hndlServiceUpdateReply: error updating TXT record for service %##s", srs->RR_SRV.resrec.name->c);
- srs->state = regState_Unregistered;
InvokeCallback = mDNStrue;
}
else
{
// extra resource record queued for this service - copy zone srs and register
AssignDomainName(&(*e)->r.zone, &srs->zone);
- (*e)->r.UpdateServer = srs->ns;
+ (*e)->r.UpdateServer = srs->SRSUpdateServer;
(*e)->r.UpdatePort = srs->SRSUpdatePort;
(*e)->r.uselease = srs->srs_uselease;
SendRecordRegistration(m, &(*e)->r);
else e = &(*e)->next;
}
- srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc.
- if (srs->state == regState_Unregistered) unlinkSRS(m, srs);
+ if (srs->state == regState_Unregistered)
+ {
+ if (err != mStatus_MemFree)
+ LogMsg("hndlServiceUpdateReply ERROR! state == regState_Unregistered but err != mStatus_MemFree. Permanently abandoning service registration %##s",
+ srs->RR_SRV.resrec.name->c);
+ unlinkSRS(m, srs);
+ }
else if (txt->QueuedRData && srs->state == regState_Registered)
{
if (InvokeCallback)
if (m->mDNS_busy != m->mDNS_reentrancy+1)
LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+ SetRecordRetry(m, rr, mStatus_NoError);
+
if (rr->state == regState_UpdatePending)
{
- if (err)
- {
- LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
- rr->state = regState_Unregistered;
- }
- else
- {
- debugf("Update record %##s - success", rr->resrec.name->c);
- rr->state = regState_Registered;
- // deallocate old RData
- if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData);
- SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
- rr->OrigRData = mDNSNULL;
- rr->InFlightRData = mDNSNULL;
- }
+ if (err) LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
+ rr->state = regState_Registered;
+ // deallocate old RData
+ if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData);
+ SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
+ rr->OrigRData = mDNSNULL;
+ rr->InFlightRData = mDNSNULL;
}
if (rr->state == regState_DeregPending)
return;
}
LogMsg("hndlRecordUpdateReply: Registration of record %##s type %d failed with error %ld", rr->resrec.name->c, rr->resrec.rrtype, err);
- rr->state = regState_Unregistered;
+ return;
}
}
- if (rr->state == regState_Unregistered) UnlinkAuthRecord(m, rr);
- else rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc.
+ if (rr->state == regState_Unregistered) // Should never happen
+ {
+ LogMsg("hndlRecordUpdateReply rr->state == regState_Unregistered %s", ARDisplayString(m, rr));
+ return;
+ }
if (rr->QueuedRData && rr->state == regState_Registered)
{
{
//if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return;
if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return;
- if (!mDNSOpaque16IsZero(msg->h.id))
- for (qptr = m->Questions; qptr; qptr = qptr->next)
- if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW)
+ for (qptr = m->Questions; qptr; qptr = qptr->next)
+ if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW)
+ {
+ if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring");
+ else if (qptr->tcp)
{
- if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring");
- else if (qptr->tcp)
- {
- // There may be a race condition here, if the server decides to drop the connection just as we decide to reuse it
- // For now it should not be serious because our normal retry logic (as used to handle UDP packet loss)
- // should take care of it but later we may want to look at handling this case explicitly
- LogOperation("uDNS_ReceiveMsg: Using existing TCP connection for %##s (%s)", qptr->qname.c, DNSTypeName(qptr->qtype));
- mDNS_DropLockBeforeCallback();
- tcpCallback(qptr->tcp->sock, qptr->tcp, mDNStrue, mStatus_NoError);
- mDNS_ReclaimLockAfterCallback();
- }
- else qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, qptr, mDNSNULL, mDNSNULL);
+ // There may be a race condition here, if the server decides to drop the connection just as we decide to reuse it
+ // For now it should not be serious because our normal retry logic (as used to handle UDP packet loss)
+ // should take care of it but later we may want to look at handling this case explicitly
+ LogOperation("uDNS_ReceiveMsg: Using existing TCP connection for %##s (%s)", qptr->qname.c, DNSTypeName(qptr->qtype));
+ mDNS_DropLockBeforeCallback();
+ tcpCallback(qptr->tcp->sock, qptr->tcp, mDNStrue, mStatus_NoError);
+ mDNS_ReclaimLockAfterCallback();
}
+ else qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, qptr, mDNSNULL, mDNSNULL);
+ }
}
- if (QR_OP == UpdateR && !mDNSOpaque16IsZero(msg->h.id))
+ if (QR_OP == UpdateR)
{
mDNSu32 lease = GetPktLease(m, msg, end);
- mDNSs32 expire = (m->timenow + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4);
+ mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond;
+
+ //rcode = kDNSFlag1_RC_SrvErr; // Simulate server failure (rcode 2)
if (CurrentServiceRecordSet)
LogMsg("uDNS_ReceiveMsg ERROR CurrentServiceRecordSet already set");
{
err = checkUpdateResult(m, sptr->RR_SRV.resrec.name, rcode, msg, end);
if (!err && sptr->srs_uselease && lease)
- if (sptr->expire - expire >= 0 || sptr->state != regState_UpdatePending)
- sptr->expire = expire;
+ if (sptr->RR_SRV.expire - expire >= 0 || sptr->state != regState_UpdatePending)
+ sptr->RR_SRV.expire = expire;
hndlServiceUpdateReply(m, sptr, err);
CurrentServiceRecordSet = mDNSNULL;
return;
#pragma mark - Query Routines
#endif
-mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
+mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
{
mDNSu8 *end;
LLQOptData llq;
return;
}
- if ((q->state == LLQ_Refresh && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0)
+ if ((q->state == LLQ_Established && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0)
{
- LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d minutes", q->qname.c, DNSTypeName(q->qtype), kLLQ_DEF_RETRY/60);
- q->state = LLQ_Retry;
- q->LastQTime = m->timenow;
- q->ThisQInterval = kLLQ_DEF_RETRY * mDNSPlatformOneSecond;
- SetNextQueryTime(m, q);
+ LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d minutes", q->qname.c, DNSTypeName(q->qtype), LLQ_POLL_INTERVAL/3600);
+ StartLLQPolling(m,q);
return;
- //!!!KRS handle this - periodically try to re-establish
}
llq.vers = kLLQ_Vers;
llq.llqOp = kLLQOp_Refresh;
- llq.err = LLQErr_NoError;
+ llq.err = q->tcp ? GetLLQEventPort(m, &q->servAddr) : LLQErr_NoError; // If using TCP tell server what UDP port to send notifications to
llq.id = q->id;
llq.llqlease = lease;
err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, q->AuthInfo);
if (err) debugf("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err);
- if (q->state == LLQ_Established) q->ntries = 1;
- else q->ntries++;
+ q->ntries++;
debugf("sendLLQRefresh ntries %d %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
- q->state = LLQ_Refresh;
q->LastQTime = m->timenow;
SetNextQueryTime(m, q);
}
-// wrapper for startLLQHandshake, invoked by async op callback
-mDNSexport void startLLQHandshakeCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
+mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
{
DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext;
+ mDNS_Lock(m);
+
// If we get here it means that the GetZoneData operation has completed, and is is about to cancel
// its question and free the ZoneData memory. We no longer need to hold onto our pointer (which
// we use for cleaning up if our LLQ is cancelled *before* the GetZoneData operation has completes).
- q->nta = mDNSNULL;
+ q->nta = mDNSNULL;
+ q->servAddr = zeroAddr;
+ q->servPort = zeroIPPort;
- // check state first to make sure it is OK to touch question object
- if (q->state == LLQ_Cancelled)
+ if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port))
{
- // StopQuery was called while we were getting the zone info
- debugf("startLLQHandshakeCallback - LLQ Cancelled.");
- return;
+ q->servAddr = zoneInfo->Addr;
+ q->servPort = zoneInfo->Port;
+ q->AuthInfo = zoneInfo->ZonePrivate ? GetAuthInfoForName_internal(m, &q->qname) : mDNSNULL;
+ q->ntries = 0;
+ LogOperation("LLQGotZoneData %#a:%d", &q->servAddr, mDNSVal16(q->servPort));
+ startLLQHandshake(m, q);
}
-
- mDNS_Lock(m);
-
- if (q->state != LLQ_GetZoneInfo)
- {
- LogMsg("ERROR: startLLQHandshakeCallback - bad state %d", q->state);
- err = mStatus_UnknownErr;
- goto exit;
- }
-
- if (err)
- {
- LogMsg("ERROR: startLLQHandshakeCallback %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
- StartLLQPolling(m, q);
- err = mStatus_NoError;
- goto exit;
- }
-
- if (!zoneInfo)
- {
- LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code");
- err = mStatus_UnknownErr;
- goto exit;
- }
-
- if (mDNSIPPortIsZero(zoneInfo->Port))
- {
- LogOperation("LLQ port lookup failed - reverting to polling for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- q->servPort = zeroIPPort;
- StartLLQPolling(m, q);
- goto exit;
- }
-
- // cache necessary zone data
- q->servAddr = zoneInfo->Addr;
- q->servPort = zoneInfo->Port;
- if (!zoneInfo->ZonePrivate) q->AuthInfo = mDNSNULL;
-
- q->ntries = 0;
-
- if (q->state == LLQ_SuspendDeferred) q->state = LLQ_Suspended;
- else startLLQHandshake(m, q);
-
-exit:
-
- if (err && q) q->state = LLQ_Error;
+ else
+ StartLLQPolling(m,q);
mDNS_Unlock(m);
}
// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
-mDNSlocal void startPrivateQueryCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
+mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
{
DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext;
- LogOperation("startPrivateQueryCallback %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ LogOperation("PrivateQueryGotZoneData %##s (%s) err %d Zone %##s Private %d", q->qname.c, DNSTypeName(q->qtype), err, zoneInfo->ZoneName.c, zoneInfo->ZonePrivate);
// If we get here it means that the GetZoneData operation has completed, and is is about to cancel
// its question and free the ZoneData memory. We no longer need to hold onto our pointer (which
if (err)
{
- LogMsg("ERROR: startPrivateQueryCallback %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
- goto exit;
- }
-
- if (!zoneInfo)
- {
- LogMsg("ERROR: startPrivateQueryCallback invoked with NULL result and no error code");
- err = mStatus_UnknownErr;
- goto exit;
+ LogMsg("ERROR: PrivateQueryGotZoneData %##s (%s) invoked with error code %ld", q->qname.c, DNSTypeName(q->qtype), err);
+ return;
}
if (!zoneInfo->ZonePrivate)
mDNS_Lock(m);
SetNextQueryTime(m, q);
mDNS_Unlock(m);
- goto exit;
+ return;
// Next call to uDNS_CheckCurrentQuestion() will do this as a non-private query
}
if (!q->AuthInfo)
{
- LogMsg("ERROR: startPrivateQueryCallback: cannot find credentials for q %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- err = mStatus_UnknownErr;
- goto exit;
+ LogMsg("ERROR: PrivateQueryGotZoneData: cannot find credentials for q %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ return;
}
q->TargetQID = mDNS_NewMessageID(m);
- if (q->tcp)
- {
- LogOperation("startPrivateQueryCallback: Already have TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- tcpCallback(q->tcp->sock, q->tcp, mDNStrue, mStatus_NoError);
- }
- else q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, q, mDNSNULL, mDNSNULL);
-
-exit:
-
- if (err) mDNS_StopQuery(m, q);
- }
-
-// uDNS_StopLongLivedQuery happens IN ADDITION to stopQuery
-mDNSexport void uDNS_StopLongLivedQuery(mDNS *const m, DNSQuestion *const question)
- {
- LogOperation("uDNS_StopLongLivedQuery %##s (%s) state %d", question->qname.c, DNSTypeName(question->qtype), question->state);
-
- switch (question->state)
- {
- case LLQ_UnInit: LogMsg("ERROR: uDNS_StopLongLivedQuery - state LLQ_UnInit"); return; //!!!KRS should we unlink info<->question here?
-
- case LLQ_GetZoneInfo:
- case LLQ_SuspendDeferred: question->state = LLQ_Cancelled; return;
-
- case LLQ_Established:
- case LLQ_Refresh: sendLLQRefresh(m, question, 0); break;
-
- default: debugf("uDNS_StopLongLivedQuery - silently discarding LLQ in state %d", question->state); break;
- }
-
- RemoveLLQNatMappings(m, question);
- CheckForUnreferencedLLQMapping(m);
+ if (q->tcp) DisposeTCPConn(q->tcp);
+ q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, q, mDNSNULL, mDNSNULL);
}
// ***************************************************************************
#endif
// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
-mDNSexport void RecordRegistrationCallback(mDNS *const m, mStatus err, const ZoneData *zoneData)
+mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData)
{
AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext;
AuthRecord *ptr;
if (m->mDNS_busy != m->mDNS_reentrancy)
- LogMsg("RecordRegistrationCallback: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+ LogMsg("RecordRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
newRR->nta = mDNSNULL;
// make sure record is still in list (!!!)
for (ptr = m->ResourceRecords; ptr; ptr = ptr->next) if (ptr == newRR) break;
- if (!ptr) { LogMsg("RecordRegistrationCallback - RR no longer in list. Discarding."); return; }
+ if (!ptr) { LogMsg("RecordRegistrationGotZoneData - RR no longer in list. Discarding."); return; }
// check error/result
- if (err) { LogMsg("RecordRegistrationCallback: error %ld", err); goto error; }
- if (!zoneData) { LogMsg("ERROR: RecordRegistrationCallback invoked with NULL result and no error"); goto error; }
+ if (err)
+ {
+ if (err != mStatus_NoSuchNameErr) LogMsg("RecordRegistrationGotZoneData: error %ld", err);
+ return;
+ }
+
+ if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; }
if (newRR->resrec.rrclass != zoneData->ZoneClass)
{
- LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)",
- newRR->resrec.rrclass, zoneData->ZoneClass);
- goto error;
+ LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", newRR->resrec.rrclass, zoneData->ZoneClass);
+ return;
}
// Don't try to do updates to the root name server.
// organizations use their own private pseudo-TLD, like ".home", etc, and we don't want to block that.
if (zoneData->ZoneName.c[0] == 0)
{
- LogMsg("RecordRegistrationCallback: Only name server claiming responsibility for \"%##s\" is \"%##s\"!", newRR->resrec.name->c, zoneData->ZoneName.c);
- err = mStatus_NoSuchNameErr;
- goto error;
+ LogOperation("RecordRegistrationGotZoneData: No name server found claiming responsibility for \"%##s\"!", newRR->resrec.name->c);
+ return;
}
// Store discovered zone data
newRR->UpdateServer = zoneData->Addr;
newRR->UpdatePort = zoneData->Port;
newRR->Private = zoneData->ZonePrivate;
- debugf("RecordRegistrationCallback: Set newRR->UpdateServer %##s %##s to %#a:%d",
+ debugf("RecordRegistrationGotZoneData: Set newRR->UpdateServer %##s %##s to %#a:%d",
newRR->resrec.name->c, zoneData->ZoneName.c, &newRR->UpdateServer, mDNSVal16(newRR->UpdatePort));
if (mDNSIPPortIsZero(zoneData->Port) || mDNSAddressIsZero(&zoneData->Addr))
{
- LogMsg("RecordRegistrationCallback: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c);
- err = mStatus_NoSuchNameErr;
- goto error;
+ LogOperation("RecordRegistrationGotZoneData: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c);
+ return;
}
-
mDNS_Lock(m); // SendRecordRegistration expects to be called with the lock held
SendRecordRegistration(m, newRR);
mDNS_Unlock(m);
- return;
-
-error:
- if (newRR->state != regState_Unregistered)
- {
- mDNS_Lock(m);
- UnlinkAuthRecord(m, newRR);
- newRR->state = regState_Unregistered;
- mDNS_Unlock(m);
- }
-
- // Don't need to do the mDNS_DropLockBeforeCallback stuff here, because this code is
- // *already* being invoked in the right callback context, with mDNS_reentrancy correctly incremented.
- if (newRR->RecordCallback)
- newRR->RecordCallback(m, newRR, err);
- // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
- // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
}
mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr)
{
mDNSu8 *ptr = m->omsg.data;
mDNSu8 *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
- mStatus err = mStatus_NoError;
-
- InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
- ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
- if (!ptr) { err = mStatus_UnknownErr; goto exit; }
- if (!(ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec))) { err = mStatus_UnknownErr; goto exit; }
-
- rr->state = regState_DeregPending;
-
- if (rr->Private)
+ if (mDNSIPv4AddressIsZero(rr->UpdateServer.ip.v4)) // Don't know our UpdateServer yet
{
- LogOperation("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
- if (rr->tcp) LogMsg("SendRecordDeregistration: ERROR: Already have TCP connection for %s", ARDisplayString(m, rr));
- else rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
rr->LastAPTime = m->timenow;
- rr->ThisAPInterval = 0x3FFFFFFF; // TCP will handle any necessary retransmissions for us
- }
- else
- {
- err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
- if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
- SetRecordRetry(m, rr, err);
- CompleteDeregistration(m, rr); // Don't touch rr after this
+ if (rr->ThisAPInterval < mDNSPlatformOneSecond * 5)
+ rr->ThisAPInterval = mDNSPlatformOneSecond * 5;
return;
}
- err = mStatus_NoError;
-
-exit:
+ InitializeDNSMessage(&m->omsg.h, rr->id, UpdateReqFlags);
- if (err)
+ ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+ if (ptr) ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec);
+ if (!ptr)
{
- LogMsg("Error: SendRecordDeregistration - could not contruct deregistration packet");
- UnlinkAuthRecord(m, rr);
- rr->state = regState_Unregistered;
+ LogMsg("SendRecordDeregistration Error: could not contruct deregistration packet for %s", ARDisplayString(m, rr));
+ if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr);
+ }
+ else
+ {
+ if (rr->Private)
+ {
+ LogOperation("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
+ if (rr->tcp) LogOperation("SendRecordDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr));
+ if (rr->tcp) DisposeTCPConn(rr->tcp);
+ rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, mDNSNULL, rr);
+ if (!rr->tcp) rr->ThisAPInterval = mDNSPlatformOneSecond * 5; // If failed to make TCP connection, try again in ten seconds (5*2)
+ else if (rr->ThisAPInterval < mDNSPlatformOneSecond * 30) rr->ThisAPInterval = mDNSPlatformOneSecond * 30;
+ SetRecordRetry(m, rr, mStatus_NoError);
+ }
+ else
+ {
+ mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
+ if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
+ if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr); // Don't touch rr after this
+ }
}
}
case regState_Refresh:
case regState_Pending:
case regState_UpdatePending:
- rr->state = regState_DeregDeferred;
- LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name->c);
- return mStatus_NoError;
case regState_FetchingZoneData:
case regState_Registered: break;
case regState_DeregPending: break;
default: LogMsg("uDNS_DeregisterRecord: State %d for %##s type %s", rr->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype)); return mStatus_NoError;
}
- if (rr->state != regState_Unregistered) SendRecordDeregistration(m, rr);
+ if (rr->state != regState_Unregistered) { rr->state = regState_DeregPending; SendRecordDeregistration(m, rr); }
return mStatus_NoError;
}
case regState_Unregistered:
debugf("uDNS_DeregisterService - service %##s not registered", srs->RR_SRV.resrec.name->c);
return mStatus_BadReferenceErr;
- case regState_Pending:
- case regState_Refresh:
- case regState_UpdatePending:
- // deregister following completion of in-flight operation
- srs->state = regState_DeregDeferred;
- return mStatus_NoError;
case regState_DeregPending:
case regState_DeregDeferred:
debugf("Double deregistration of service %##s", srs->RR_SRV.resrec.name->c);
srs->ServiceCallback(m, srs, mStatus_MemFree);
mDNS_ReclaimLockAfterCallback();
return mStatus_NoError;
+ case regState_Pending:
+ case regState_Refresh:
+ case regState_UpdatePending:
case regState_FetchingZoneData:
case regState_Registered:
srs->state = regState_DeregPending;
sendtime = m->SuppressStdPort53Queries;
if (m->timenow - sendtime < 0) return;
- if (q->LongLived && q->state != LLQ_Poll)
+ if (q->LongLived)
{
- if (q->state >= LLQ_InitialRequest && q->state <= LLQ_Established)
+ switch (q->state)
{
- // sanity check to avoid packet flood bugs
- if (q->state == LLQ_Established || q->state == LLQ_Refresh) sendLLQRefresh(m, q, q->origLease);
- else if (q->state == LLQ_InitialRequest ) startLLQHandshake(m, q);
- else if (q->state == LLQ_SecondaryRequest ) sendChallengeResponse(m, q, mDNSNULL);
- else if (q->state == LLQ_Retry ) { q->ntries = 0; startLLQHandshake(m, q); }
- }
- else
- {
- // This should never happen. Any LLQ not in states LLQ_InitialRequest to LLQ_Established should not have have ThisQInterval set.
- // (uDNS_CheckCurrentQuestion() is only called for DNSQuestions with non-zero ThisQInterval)
- LogMsg("uDNS_CheckCurrentQuestion: %##s (%s) state %d sendtime %d ThisQInterval %d",
- q->qname.c, DNSTypeName(q->qtype), q->state, sendtime - m->timenow, q->ThisQInterval);
- q->LastQTime = m->timenow;
- q->ThisQInterval *= 2;
- SetNextQueryTime(m, q);
+ case LLQ_InitialRequest: startLLQHandshake(m, q); break;
+ case LLQ_SecondaryRequest: sendChallengeResponse(m, q, mDNSNULL); break;
+ case LLQ_Established: sendLLQRefresh(m, q, q->origLease); break;
+ case LLQ_Poll: break; // Do nothing (handled below)
}
}
if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q))
{
- err = constructQueryMsg(&m->omsg, &end, q);
+ InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+ end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
private = q->AuthInfo;
}
else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL) // Make sure at least three seconds has elapsed since last test query
{
LogOperation("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port));
- q->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+ q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep;
q->qDNSServer->lasttest = m->timenow;
InitializeDNSMessage(&m->omsg.h, mDNS_NewMessageID(m), uQueryFlags);
end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN);
}
- if (err) LogMsg("Error: uDNS_CheckCurrentQuestion - constructQueryMsg. Skipping question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- else
+ if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q)))
{
- if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q)))
+ //LogMsg("uDNS_CheckCurrentQuestion %d %p %##s (%s)", sendtime - m->timenow, private, q->qname.c, DNSTypeName(q->qtype));
+ if (private)
{
- //LogMsg("uDNS_CheckCurrentQuestion %d %p %##s (%s)", sendtime - m->timenow, private, q->qname.c, DNSTypeName(q->qtype));
- if (private)
- {
- if (q->nta) LogMsg("uDNS_CheckCurrentQuestion Error: GetZoneData already started for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
- else q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, startPrivateQueryCallback, q);
- q->ThisQInterval = 0; // Suspend this question until GetZoneData completes
- }
- else
- {
- err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
- m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100);
- }
- }
-
- if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); // surpress syslog messages if we have no network
- else if (!q->LongLived && q->ThisQInterval < MAX_UCAST_POLL_INTERVAL)
- {
- q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; // Only increase interval if send succeeded
- LogOperation("Adjusted ThisQInterval to %d for %##s (%s)", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype));
+ if (q->nta) CancelGetZoneData(m, q->nta);
+ q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q);
+ q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep;
}
- else if (q->LongLived && q->state == LLQ_Poll)
+ else
{
- // Bit of a hack here -- if we dropped the interval down to do the DNS test query, need to put
- // it back or we'll poll every three seconds. The real solution is that the DNS test query
- // should be a real query in its own right, which other queries are blocked on, rather than
- // being shoehorned in here and borrowing another question's q->LastQTime and q->ThisQInterval
- q->ThisQInterval = LLQ_POLL_INTERVAL;
+ err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
+ m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100);
}
}
+
+ if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); // surpress syslog messages if we have no network
+ else
+ {
+ q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep; // Only increase interval if send succeeded
+ if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL)
+ q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
+ LogOperation("Increased ThisQInterval to %d for %##s (%s)", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype));
+ }
q->LastQTime = m->timenow;
SetNextQueryTime(m, q);
}
// 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
+ // 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);
for (rr = cg->members; rr; rr=rr->next)
if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr);
- if (!q->qDNSServer) LogMsg("uDNS_CheckCurrentQuestion no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+ if (!q->qDNSServer) LogOperation("uDNS_CheckCurrentQuestion no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
else LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c);
MakeNegativeCacheRecord(m, &q->qname, q->qnamehash, q->qtype, q->qclass, 60);
cur->Result != cur->NewResult)
{
//LogMsg("NAT callback %d %d %d", cur->Protocol, cur->ExpiryTime, cur->retryInterval);
- if (cur->Protocol && mDNSIPPortIsZero(ExternalPort))
+ if (cur->Protocol && mDNSIPPortIsZero(ExternalPort) && !mDNSIPv4AddressIsZero(m->Router.ip.v4))
LogMsg("Failed to obtain NAT port mapping from router %#a external address %.4a internal port %d",
&m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort));
cur->ExternalAddress = m->ExternalAddress;
for (rr = m->ResourceRecords; rr; rr = rr->next)
{
- if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending || rr->state == regState_DeregDeferred || rr->state == regState_Refresh)
+ if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending ||
+ rr->state == regState_DeregDeferred || rr->state == regState_Refresh || rr->state == regState_Registered)
{
if (rr->LastAPTime + rr->ThisAPInterval - m->timenow < 0)
{
- if (rr->tcp) { rr->LastAPTime = m->timenow; rr->ThisAPInterval = 0x3FFFFFFF; }
- else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr);
+ if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+ if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr);
else SendRecordRegistration(m, rr);
}
- if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval;
- }
- if (rr->uselease && rr->state == regState_Registered)
- {
- if (rr->expire - m->timenow < 0)
- {
- debugf("refreshing record %##s", rr->resrec.name->c);
- rr->state = regState_Refresh;
- SendRecordRegistration(m, rr);
- }
- if (rr->expire - nextevent < 0) nextevent = rr->expire;
+ if (nextevent - (rr->LastAPTime + rr->ThisAPInterval) > 0)
+ nextevent = (rr->LastAPTime + rr->ThisAPInterval);
}
}
return nextevent;
{
ServiceRecordSet *srs = CurrentServiceRecordSet;
CurrentServiceRecordSet = CurrentServiceRecordSet->uDNS_next;
- if (srs->state == regState_Pending || srs->state == regState_DeregPending || srs->state == regState_DeregDeferred || srs->state == regState_Refresh || srs->state == regState_UpdatePending)
+ if (srs->state == regState_Pending || srs->state == regState_DeregPending || srs->state == regState_DeregDeferred ||
+ srs->state == regState_Refresh || srs->state == regState_UpdatePending || srs->state == regState_Registered)
{
- if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - m->timenow < 0)
+ if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - m->timenow <= 0)
{
- if (srs->tcp) { srs->RR_SRV.LastAPTime = m->timenow; srs->RR_SRV.ThisAPInterval = 0x3FFFFFFF; }
- else if (srs->state == regState_DeregPending) { SendServiceDeregistration(m, srs); continue; }
+ if (srs->tcp) { DisposeTCPConn(srs->tcp); srs->tcp = mDNSNULL; }
+ if (srs->state == regState_DeregPending) SendServiceDeregistration(m, srs);
else SendServiceRegistration(m, srs);
}
- if (nextevent - srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval > 0)
- nextevent = srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval;
- }
-
- if (srs->srs_uselease && srs->state == regState_Registered)
- {
- if (srs->expire - m->timenow < 0)
- {
- debugf("refreshing service %##s", srs->RR_SRV.resrec.name->c);
- srs->state = regState_Refresh;
- SendServiceRegistration(m, srs);
- }
- if (srs->expire - nextevent < 0) nextevent = srs->expire;
+ if (nextevent - (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval) > 0)
+ nextevent = (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval);
}
}
return nextevent;
#pragma mark - Startup, Shutdown, and Sleep
#endif
-// DeregisterActive causes active LLQs to be removed from the server, e.g. before sleep. Pass false
-// following a location change, as the server will reject deletions from a source address different
-// from the address on which the LLQ was created.
-
-mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive)
- {
- DNSQuestion *q;
-
- for (q = m->Questions; q; q = q->next)
- {
- if (q->LongLived)
- {
- if (q->state == LLQ_GetZoneInfo)
- {
- debugf("Marking %##s suspend-deferred", q->qname.c);
- q->state = LLQ_SuspendDeferred; // suspend once we're done getting zone info
- }
- else if (q->state < LLQ_Suspended)
- {
- if (DeregisterActive && (q->state == LLQ_Established || q->state == LLQ_Refresh))
- {
- debugf("Deleting LLQ %##s", q->qname.c);
- sendLLQRefresh(m, q, 0);
- }
- debugf("Marking %##s suspended", q->qname.c);
- q->state = LLQ_Suspended;
-
- if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
-
- q->id = zeroOpaque64;
- }
- else if (q->state == LLQ_Poll)
- {
- debugf("Marking %##s suspended-poll", q->qname.c);
- q->state = LLQ_SuspendedPoll;
- }
-
- RemoveLLQNatMappings(m, q); // may not need nat mapping if we restart with new route
- }
- }
- CheckForUnreferencedLLQMapping(m);
- }
-
-mDNSlocal void RestartQueries(mDNS *m)
- {
- if (m->CurrentQuestion)
- LogMsg("RestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
- m->CurrentQuestion = m->Questions;
- while (m->CurrentQuestion)
- {
- DNSQuestion *q = m->CurrentQuestion;
- m->CurrentQuestion = m->CurrentQuestion->next;
-
- if (!mDNSOpaque16IsZero(q->TargetQID) && !q->DuplicateOf)
- {
- if (q->LongLived)
- {
- if (q->state == LLQ_Suspended || q->state == LLQ_NatMapWaitUDP)
- {
- q->ntries = -1;
- startLLQHandshake(m, q);
- }
- else if (q->state == LLQ_SuspendDeferred)
- q->state = LLQ_GetZoneInfo; // we never finished getting zone data - proceed as usual
- else if (q->state == LLQ_SuspendedPoll)
- {
- // if we were polling, we may have had bad zone data due to firewall, etc. - refetch
- q->ntries = 0;
- q->state = LLQ_GetZoneInfo;
- if (q->nta) CancelGetZoneData(m, q->nta); // Make sure we cancel old one before we start a new one
- q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, startLLQHandshakeCallback, q);
- }
- }
- else
- {
- q->LastQTime = m->timenow;
- q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; // trigger poll in 3 seconds (to reduce packet rate when restarts come in rapid succession)
- SetNextQueryTime(m, q);
- }
- }
- }
- m->CurrentQuestion = mDNSNULL;
- }
-
-mDNSexport void mDNS_UpdateLLQs(mDNS *m)
- {
- mDNS_Lock(m);
- SuspendLLQs(m, mDNStrue);
- RestartQueries(m);
- mDNS_Unlock(m);
- }
-
// simplest sleep logic - rather than having sleep states that must be dealt with explicitly in all parts of
// the code, we simply send a deregistration, and put the service in Refresh state, with a timeout far enough
// in the future that we'll sleep (or the sleep will be cancelled) before it is retransmitted. Then to wake,
// we just move up the timers.
-mDNSlocal void SleepRecordRegistrations(mDNS *m)
+mDNSexport void SleepRecordRegistrations(mDNS *m)
{
AuthRecord *rr = m->ResourceRecords;
if (rr->state == regState_Registered ||
rr->state == regState_Refresh)
{
- mDNSu8 *ptr = m->omsg.data, *end = (mDNSu8 *)&m->omsg + sizeof(DNSMessage);
- InitializeDNSMessage(&m->omsg.h, mDNS_NewMessageID(m), UpdateReqFlags);
-
- // construct deletion update
- ptr = putZone(&m->omsg, ptr, end, &rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
- if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; }
- ptr = putDeletionRecord(&m->omsg, ptr, &rr->resrec);
- if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; }
-
- mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, &rr->UpdateServer, rr->UpdatePort, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
+ SendRecordDeregistration(m, rr);
rr->state = regState_Refresh;
rr->LastAPTime = m->timenow;
rr->ThisAPInterval = 300 * mDNSPlatformOneSecond;
}
}
-mDNSlocal void SleepServiceRegistrations(mDNS *m)
+mDNSexport void SleepServiceRegistrations(mDNS *m)
{
ServiceRecordSet *srs = m->ServiceRegistrations;
while (srs)
// 6) client calls to enumerate domains now go over LocalOnly interface
// (!!!KRS may add outgoing interface in addition)
-mDNSexport void uDNS_Sleep(mDNS *const m)
- {
- SuspendLLQs(m, mDNStrue);
- SleepServiceRegistrations(m);
- SleepRecordRegistrations(m);
- }
-
mDNSexport void uDNS_Wake(mDNS *const m)
{
- RestartQueries(m);
WakeServiceRegistrations(m);
WakeRecordRegistrations(m);
}
Change History (most recent first):
$Log: uDNS.h,v $
+Revision 1.88 2007/10/25 20:06:13 cheshire
+Don't try to do SOA queries using private DNS (TLS over TCP) queries
+
+Revision 1.87 2007/10/24 22:40:06 cheshire
+Renamed: RecordRegistrationCallback -> RecordRegistrationGotZoneData
+Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData
+
+Revision 1.86 2007/10/18 23:06:42 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Additional fixes and refinements
+
+Revision 1.85 2007/10/18 20:23:17 cheshire
+Moved SuspendLLQs into mDNS.c, since it's only called from one place
+
+Revision 1.84 2007/10/17 22:49:54 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.83 2007/10/17 22:37:23 cheshire
+<rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
+
+Revision 1.82 2007/10/17 21:53:51 cheshire
+Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData
+
+Revision 1.81 2007/10/16 21:16:50 cheshire
+Get rid of unused uDNS_Sleep() routine
+
+Revision 1.80 2007/10/16 20:59:41 cheshire
+Export SuspendLLQs/SleepServiceRegistrations/SleepRecordRegistrations so they're callable from other files
+
Revision 1.79 2007/09/20 01:13:19 cheshire
Export CacheGroupForName so it's callable from other files
// Entry points into unicast-specific routines
-extern void startLLQHandshakeCallback(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
+extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
+extern void startLLQHandshake(mDNS *m, DNSQuestion *q);
+extern void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease);
-extern void uDNS_StopLongLivedQuery(mDNS *const m, DNSQuestion *const question);
-
-extern void uDNS_Sleep(mDNS *const m);
extern void uDNS_Wake(mDNS *const m);
+extern void SleepServiceRegistrations(mDNS *m);
+extern void SleepRecordRegistrations(mDNS *m);
+
// uDNS_UpdateRecord
// following fields must be set, and the update validated, upon entry.
// rr->NewRData
extern mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question);
extern mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal);
-extern void RecordRegistrationCallback(mDNS *const m, mStatus err, const ZoneData *zoneData);
-extern void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
+extern void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData);
extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr);
-extern void ServiceRegistrationZoneDataComplete(mDNS *const m, mStatus err, const ZoneData *result);
+extern void ServiceRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *result);
extern const domainname *GetServiceTarget(mDNS *m, ServiceRecordSet *srs);
extern mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs);
typedef enum
{
uDNS_LLQ_Not = 0, // Normal uDNS answer: Flush any stale records from cache, and respect record TTL
- uDNS_LLQ_Poll, // LLQ Poll: Flush any stale records from cache, but assume TTL is 2 x poll interval
- uDNS_LLQ_Setup, // LLQ Initial answer packet: Flush any stale records from cache; assume TTL is 2 x LLQ refresh interval
+ uDNS_LLQ_Ignore, // LLQ initial challenge packet: ignore -- has no useful records for us
+ uDNS_LLQ_Entire, // LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval
uDNS_LLQ_Events // LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval
} uDNS_LLQType;
extern uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport);
extern DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name);
+extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q);
extern void DisposeTCPConn(struct tcpInfo_t *tcp);
// NAT traversal
Change History (most recent first):
$Log: LegacyNATTraversal.c,v $
+Revision 1.44 2007/11/02 20:45:40 cheshire
+Don't log "connection failed" in customer builds
+
+Revision 1.43 2007/10/18 20:09:47 cheshire
+<rdar://problem/5545930> BTMM: Back to My Mac not working with D-Link DGL-4100 NAT gateway
+
+Revision 1.42 2007/10/16 17:37:18 cheshire
+<rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
+Cut SendSOAPMsgControlAction stack from 2144 to 96 bytes
+
+Revision 1.41 2007/10/15 23:02:00 cheshire
+Off-by-one error: Incorrect trailing zero byte on the end of the SSDP Discovery message
+
Revision 1.40 2007/09/20 21:41:49 cheshire
<rdar://problem/5495568> Legacy NAT Traversal - unmap request failed with error -65549
mDNS *m = tcpInfo->m;
mDNSu16 err = NATErr_None;
mDNSv4Addr ExtAddr;
- char *addrPtr;
char *ptr = (char *)tcpInfo->Reply;
char *end = (char *)tcpInfo->Reply + tcpInfo->nread;
+ char *addrend;
+ static char tagname[20] = "NewExternalIPAddress"; // Array NOT including a terminating nul
// LogOperation("handleLNTGetExternalAddressResponse: %s", ptr);
- while (ptr && ptr < end) // Find "NewExternalIPAddress"
- {
- if (*ptr == 'N' && (strncasecmp(ptr, "NewExternalIPAddress", 20) == 0)) break; // find the first 'N'; is this NewExternalIPAddress? if not, keep looking
- ptr++;
- }
- if (ptr == mDNSNULL || ptr >= end) return; // bad or incomplete response
- ptr+=21; // skip over "NewExternalIPAddress>"
- if (ptr >= end) { LogOperation("handleLNTGetExternalAddressResponse: past end of buffer!"); return; }
-
- // find the end of the address and terminate the string so inet_pton() can convert it
- for (addrPtr = ptr; addrPtr && addrPtr < end; addrPtr++) if (*addrPtr == '<') break; // first find the next '<' and count the chars
- if (addrPtr == mDNSNULL || addrPtr >= end) { LogOperation("handleLNTGetExternalAddressResponse: didn't find SOAP URL string"); return; }
- *addrPtr = '\0';
+ while (ptr < end && strncasecmp(ptr, tagname, sizeof(tagname))) ptr++;
+ ptr += sizeof(tagname); // Skip over "NewExternalIPAddress"
+ while (ptr < end && *ptr != '>') ptr++;
+ ptr += 1; // Skip over ">"
+ // Find the end of the address and terminate the string so inet_pton() can convert it
+ addrend = ptr;
+ while (addrend < end && (mdnsIsDigit(*addrend) || *addrend == '.')) addrend++;
+ if (addrend >= end) return;
+ *addrend = 0;
if (inet_pton(AF_INET, ptr, &ExtAddr) <= 0)
- { LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address"); err = NATErr_NetFail; }
+ { LogMsg("handleLNTGetExternalAddressResponse: Router returned bad address %s", ptr); err = NATErr_NetFail; }
if (!err) LogOperation("handleLNTGetExternalAddressResponse: External IP address is %.4a", &ExtAddr);
natTraversalHandleAddressReply(m, err, ExtAddr);
NATTraversalInfo *natInfo;
for (natInfo = m->NATTraversals; natInfo; natInfo=natInfo->next) { if (natInfo == tcpInfo->parentNATInfo) break; }
-
+
if (!natInfo) { LogOperation("handleLNTPortMappingResponse: can't find matching tcpInfo in NATTraversals!"); return; }
// start from the beginning of the HTTP header; find "200 OK" status message; if the first characters after the
}
else
{
- LogMsg("LNT MakeTCPConnection: connection failed");
+ // Don't need to log this in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
+ LogOperation("LNT MakeTCPConnection: connection failed");
mDNSPlatformTCPCloseConnection(info->sock); // Dispose the socket we created with mDNSPlatformTCPSocket() above
info->sock = mDNSNULL;
mDNSPlatformMemFree(info->Reply);
"</SOAP-ENV:Envelope>\r\n";
mStatus err;
- char body[2048]; // Typically requires 1110-1122 bytes, so 2048 allows a generous safety margin
+ char *body = (char *)&m->omsg; // Typically requires 1110-1122 bytes; m->omsg is 8952 bytes, which is plenty
int bodyLen;
if (m->UPnPSOAPURL == mDNSNULL || m->UPnPSOAPAddressString == mDNSNULL) // if no SOAP URL or address exists get out here
{ LogOperation("SendSOAPMsgControlAction: no SOAP URL or address string"); return mStatus_Invalid; }
// Create body
- bodyLen = mDNS_snprintf (body, sizeof(body), body1, Action);
- bodyLen += AddSOAPArguments(body + bodyLen, sizeof(body) - bodyLen, numArgs, Arguments);
- bodyLen += mDNS_snprintf (body + bodyLen, sizeof(body) - bodyLen, body2, Action);
+ bodyLen = mDNS_snprintf (body, sizeof(m->omsg), body1, Action);
+ bodyLen += AddSOAPArguments(body + bodyLen, sizeof(m->omsg) - bodyLen, numArgs, Arguments);
+ bodyLen += mDNS_snprintf (body + bodyLen, sizeof(m->omsg) - bodyLen, body2, Action);
// Create info->Request; the header needs to contain the bodyLen in the "Content-Length" field
if (!info->Request) info->Request = mDNSPlatformMemAllocate(LNT_MAXBUFSIZE);
"Man:\"ssdp:discover\"\r\n"
"MX:3\r\n\r\n";
- LogOperation("LNT_SendDiscoveryMsg %.4a %.4a", &m->Router.ip.v4, &m->ExternalAddress);
+ LogOperation("LNT_SendDiscoveryMsg Router %.4a Current External Address %.4a", &m->Router.ip.v4, &m->ExternalAddress);
if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSIPv4AddressIsZero(m->ExternalAddress))
- mDNSPlatformSendUDP(m, msg, msg + sizeof(msg), 0, &m->Router, SSDPPort);
+ mDNSPlatformSendUDP(m, msg, msg + sizeof(msg) - 1, 0, &m->Router, SSDPPort);
}
#endif /* _LEGACY_NAT_TRAVERSAL_ */
Change History (most recent first):
$Log: daemon.c,v $
+Revision 1.347 2007/11/02 22:00:13 cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+Need to hold the lock while calling SetDomainSecrets
+
+Revision 1.346 2007/11/02 20:18:13 cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+
+Revision 1.345 2007/10/17 18:41:21 cheshire
+For debugging, make SIGUSR1 simulate a KeychainChanged event as well as a NetworkChanged
+
Revision 1.344 2007/09/29 01:06:17 mcguire
<rdar://problem/5507862> 9A564: mDNSResponder crash in mDNS_Execute
(void)size; // Unused
(void)info; // Unused
mach_msg_header_t *msg_header = (mach_msg_header_t *)msg;
- KQueueLock(&mDNSStorage);
+ mDNS *const m = &mDNSStorage;
+
+ // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
+ KQueueLock(m);
switch(msg_header->msgh_id)
{
case SIGHUP: {
- mDNS *m = &mDNSStorage;
mDNSu32 slot;
CacheGroup *cg;
CacheRecord *rr;
case SIGTERM: ExitCallback(msg_header->msgh_id); break;
case SIGINFO: INFOCallback(); break;
case SIGUSR1: LogMsg("SIGUSR1: Simulate Network Configuration Change Event");
- mDNSMacOSXNetworkChanged(&mDNSStorage); break;
+ mDNSMacOSXNetworkChanged(m);
+
+ // Simulate KeychainChanged
+ mDNS_Lock(m);
+ SetDomainSecrets(m);
+ mDNS_Unlock(m);
+
+ break;
case SIGUSR2: SigLogLevel(); break;
default: LogMsg("SignalCallback: Unknown signal %d", msg_header->msgh_id); break;
}
- KQueueUnlock(&mDNSStorage, "Unix Signal");
+ KQueueUnlock(m, "Unix Signal");
}
// On 10.2 the MachServerName is DNSServiceDiscoveryServer
// we then systematically lose our own looped-back packets.
if (m->p->NetworkChanged && now - m->p->NetworkChanged >= 0) mDNSMacOSXNetworkChanged(m);
+ // KeyChain frequently fails to notify clients of change events. To work around this
+ // we set a timer and periodically poll to detect if any changes have occurred.
+ // Without this Back To My Mac just does't work for a large number of users.
+ // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
+ if (m->p->KeyChainBugTimer && now - m->p->KeyChainBugTimer >= 0)
+ {
+ m->p->KeyChainBugTimer = NonZeroTime(now + m->p->KeyChainBugInterval);
+ m->p->KeyChainBugInterval *= 2;
+ if (m->p->KeyChainBugInterval > 16 * mDNSPlatformOneSecond) m->p->KeyChainBugTimer = 0;
+ mDNS_Lock(m);
+ SetDomainSecrets(m);
+ mDNS_Unlock(m);
+ }
+
// 2. Call mDNS_Execute() to let mDNSCore do what it needs to do
mDNSs32 nextevent = mDNS_Execute(m);
if (nextevent - m->p->NetworkChanged > 0)
nextevent = m->p->NetworkChanged;
+ if (m->p->KeyChainBugTimer)
+ if (nextevent - m->p->KeyChainBugTimer > 0)
+ nextevent = m->p->KeyChainBugTimer;
+
// 3. Deliver any waiting browse messages to clients
DNSServiceBrowser *b = DNSServiceBrowserList;
Change History (most recent first):
$Log: mDNSMacOSX.c,v $
+Revision 1.508 2007/11/02 21:59:37 cheshire
+Added comment about locking
+
+Revision 1.507 2007/11/02 20:18:13 cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+
+Revision 1.506 2007/10/30 20:46:45 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+
+Revision 1.505 2007/10/29 23:55:10 cheshire
+<rdar://problem/5526791> BTMM: Changing Local Hostname doesn't update Back to My Mac registered records
+Don't need to manually fake another AutoTunnelNATCallback if it has not yet received its first callback
+(and indeed should not, since the result fields will not yet be set up correctly in this case)
+
+Revision 1.504 2007/10/26 00:50:37 cheshire
+<rdar://problem/5526791> BTMM: Changing Local Hostname doesn't update Back to My Mac registered records
+
+Revision 1.503 2007/10/25 23:11:42 cheshire
+Ignore IPv6 ULA addresses configured on lo0 loopback interface
+
+Revision 1.502 2007/10/22 20:07:07 cheshire
+Moved mDNSPlatformSourceAddrForDest from mDNSMacOSX.c to PlatformCommon.c so
+Posix build can share the code (better than just pasting it into mDNSPosix.c)
+
+Revision 1.501 2007/10/22 19:40:30 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+Made subroutine mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
+
+Revision 1.500 2007/10/17 22:49:55 cheshire
+<rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
+
+Revision 1.499 2007/10/17 19:47:54 cheshire
+Improved debugging messages
+
+Revision 1.498 2007/10/17 18:42:06 cheshire
+Export SetDomainSecrets so its callable from other files
+
+Revision 1.497 2007/10/16 17:03:07 cheshire
+<rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
+Cut SetDomainSecrets stack from 3792 to 1760 bytes
+
Revision 1.496 2007/10/04 20:33:05 mcguire
<rdar://problem/5518845> BTMM: Racoon configuration removed when network changes
else
{
LogMsg("mDNSPlatformSendUDP: dst is not an IPv4 or IPv6 address!");
+#if ForceAlerts
+ *(long*)0 = 0;
+#endif
return mStatus_BadParamErr;
}
mDNSlocal void RegisterAutoTunnelRecords(mDNS *m, DomainAuthInfo *info)
{
- if (!info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && AutoTunnelUnregistered(info))
+ if (info->AutoTunnelNAT.clientContext && !info->AutoTunnelNAT.Result && !mDNSIPPortIsZero(info->AutoTunnelNAT.ExternalPort) && AutoTunnelUnregistered(info))
{
- LogOperation("RegisterAutoTunnelRecords %##s", info->AutoTunnelService.namestorage.c);
mStatus err;
- info->AutoTunnelService.resrec.rdata->u.srv.port = info->AutoTunnelNAT.ExternalPort;
- info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique;
- err = mDNS_Register(m, &info->AutoTunnelService);
- if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c);
-
- info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique;
- mDNS_Lock(m);
- mDNS_AddDynDNSHostName(m, &info->AutoTunnelTarget.namestorage, mDNSNULL, info);
- mDNS_Unlock(m);
+ LogOperation("RegisterAutoTunnelRecords %##s (%#s)", info->domain.c, m->hostlabel.c);
- if (info->AutoTunnelHostRecord.namestorage.c[0] == 0)
- {
- AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel);
- AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain);
- }
+ // 1. Set up our address record for the internal tunnel address
+ // (User-visible user-friendly host name, used as target in AutoTunnel SRV records)
+ info->AutoTunnelHostRecord.namestorage.c[0] = 0;
+ AppendDomainLabel(&info->AutoTunnelHostRecord.namestorage, &m->hostlabel);
+ AppendDomainName (&info->AutoTunnelHostRecord.namestorage, &info->domain);
+ info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = m->AutoTunnelHostAddr;
info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeKnownUnique;
err = mDNS_Register(m, &info->AutoTunnelHostRecord);
if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c);
+ // 2. Set up device info record
+ ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
+ mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
+ mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
+ mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
+ info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string
+ info->AutoTunnelDeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string
info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeKnownUnique;
err = mDNS_Register(m, &info->AutoTunnelDeviceInfo);
if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelDeviceInfo %##s", err, info->AutoTunnelDeviceInfo.namestorage.c);
+ // 3. Set up our address record for the external tunnel address
+ // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record)
+ info->AutoTunnelTarget.namestorage.c[0] = 0;
+ AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->AutoTunnelLabel);
+ AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain);
+ info->AutoTunnelTarget.resrec.RecordType = kDNSRecordTypeKnownUnique;
+
+ mDNS_Lock(m);
+ mDNS_AddDynDNSHostName(m, &info->AutoTunnelTarget.namestorage, mDNSNULL, info);
+ mDNS_Unlock(m);
+
+ // 4. Set up IKE tunnel's SRV record: "AutoTunnelHostRecord SRV 0 0 port AutoTunnelTarget"
+ AssignDomainName (&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
+ AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel);
+ AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain);
+ info->AutoTunnelService.resrec.rdata->u.srv.priority = 0;
+ info->AutoTunnelService.resrec.rdata->u.srv.weight = 0;
+ info->AutoTunnelService.resrec.rdata->u.srv.port = info->AutoTunnelNAT.ExternalPort;
+ AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage);
+ info->AutoTunnelService.resrec.RecordType = kDNSRecordTypeKnownUnique;
+ err = mDNS_Register(m, &info->AutoTunnelService);
+ if (err) LogMsg("RegisterAutoTunnelRecords error %d registering AutoTunnelService %##s", err, info->AutoTunnelService.namestorage.c);
+
LogMsg("AutoTunnel server listening for connections on %##s[%.4a]:%d:%##s[%.16a]",
info->AutoTunnelTarget.namestorage.c, &m->AdvertisedV4.ip.v4, mDNSVal16(info->AutoTunnelNAT.IntPort),
info->AutoTunnelHostRecord.namestorage.c, &m->AutoTunnelHostAddr);
mDNSlocal void DeregisterAutoTunnelRecords(mDNS *m, DomainAuthInfo *info)
{
- LogOperation("DeregisterAutoTunnelRecords %##s", info->AutoTunnelService.namestorage.c);
+ LogOperation("DeregisterAutoTunnelRecords %##s", info->domain.c);
if (info->AutoTunnelService.resrec.RecordType > kDNSRecordTypeDeregistering)
{
mStatus err = mDNS_Deregister(m, &info->AutoTunnelService);
info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered;
LogMsg("DeregisterAutoTunnelRecords error %d deregistering AutoTunnelHostRecord %##s", err, info->AutoTunnelHostRecord.namestorage.c);
}
- info->AutoTunnelHostRecord.namestorage.c[0] = 0;
}
if (info->AutoTunnelDeviceInfo.resrec.RecordType > kDNSRecordTypeDeregistering)
mDNSlocal void AutoTunnelNATCallback(mDNS *m, NATTraversalInfo *n)
{
DomainAuthInfo *info = (DomainAuthInfo *)n->clientContext;
- LogOperation("AutoTunnelNATCallback Result %d %.4a Internal %d External %d %##s", n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), info->AutoTunnelService.namestorage.c);
+ LogOperation("AutoTunnelNATCallback Result %d %.4a Internal %d External %d %#s.%##s",
+ n->Result, &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), m->hostlabel.c, info->domain.c);
m->NextSRVUpdate = m->timenow;
DeregisterAutoTunnelRecords(m,info);
}
}
+mDNSlocal void AbortDeregistration(mDNS *const m, AuthRecord *rr)
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ {
+ LogOperation("Aborting deregistration of %s", ARDisplayString(m, rr));
+ CompleteDeregistration(m, rr);
+ }
+ else if (rr->resrec.RecordType != kDNSRecordTypeUnregistered)
+ LogMsg("AbortDeregistration ERROR RecordType %02X for %s", ARDisplayString(m, rr));
+ }
+
// Before SetupLocalAutoTunnelInterface_internal is called,
// m->AutoTunnelHostAddr.b[0] must be non-zero, and there must be at least one TunnelClient or TunnelServer
// Must be called with the lock held
{
if (info->AutoTunnel && !info->deltime && !info->AutoTunnelNAT.clientContext)
{
- // 1. Set up our address record for the internal tunnel address
- // (User-visible user-friendly host name, used as target in AutoTunnel SRV records)
+ // If we just resurrected a DomainAuthInfo that is still deregistering, we need to abort the deregistration process before re-using the AuthRecord memory
+ AbortDeregistration(m, &info->AutoTunnelHostRecord);
+ AbortDeregistration(m, &info->AutoTunnelDeviceInfo);
+ AbortDeregistration(m, &info->AutoTunnelService);
+
mDNS_SetupResourceRecord(&info->AutoTunnelHostRecord, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
- info->AutoTunnelHostRecord.namestorage.c[0] = 0;
- info->AutoTunnelHostRecord.resrec.rdata->u.ipv6 = m->AutoTunnelHostAddr;
-
- // 2. Set up device info record
- mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
- mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
- ConstructServiceName(&info->AutoTunnelDeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &info->domain);
- mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
- mDNSPlatformMemCopy(info->AutoTunnelDeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
- info->AutoTunnelDeviceInfo.resrec.rdata->u.data[0] = 6 + len; // "model=" plus the device string
- info->AutoTunnelDeviceInfo.resrec.rdlength = 7 + len; // One extra for the length byte at the start of the string
-
- // 3. Set up our address record for the external tunnel address
- // (Constructed name, not generally user-visible, used as target in IKE tunnel's SRV record)
- mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
- info->AutoTunnelTarget.namestorage.c[0] = 0;
- AppendDomainLabel(&info->AutoTunnelTarget.namestorage, &m->AutoTunnelLabel);
- AppendDomainName (&info->AutoTunnelTarget.namestorage, &info->domain);
-
- // 4. Set up IKE tunnel's SRV record: "AutoTunnelHostRecord SRV 0 0 port AutoTunnelTarget"
- mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
- AssignDomainName(&info->AutoTunnelService.namestorage, (const domainname*) "\x0B" "_autotunnel" "\x04" "_udp");
- AppendDomainLabel(&info->AutoTunnelService.namestorage, &m->hostlabel);
- AppendDomainName (&info->AutoTunnelService.namestorage, &info->domain);
- info->AutoTunnelService.resrec.rdata->u.srv.priority = 0;
- info->AutoTunnelService.resrec.rdata->u.srv.weight = 0;
- AssignDomainName(&info->AutoTunnelService.resrec.rdata->u.srv.target, &info->AutoTunnelTarget.namestorage);
+ mDNS_SetupResourceRecord(&info->AutoTunnelDeviceInfo, mDNSNULL, mDNSInterface_Any, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+ mDNS_SetupResourceRecord(&info->AutoTunnelTarget, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
+ mDNS_SetupResourceRecord(&info->AutoTunnelService, mDNSNULL, mDNSInterface_Any, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnregistered, AutoTunnelRecordCallback, info);
// Try to get a NAT port mapping for the AutoTunnelService
info->AutoTunnelNAT.clientCallback = AutoTunnelNATCallback;
mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
tmpDst.ip.v4 = tun->rmt_outer;
mDNSAddr tmpSrc = zeroAddr;
- FindSourceAddrForIP(&tmpDst, &tmpSrc);
+ mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
if (tmpSrc.type == mDNSAddrType_IPv4) tun->loc_outer = tmpSrc.ip.v4;
else tun->loc_outer = m->AdvertisedV4.ip.v4;
{
int ifru_flags6 = 0;
#ifndef NO_IPV6
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
if (ifa->ifa_addr->sa_family == AF_INET6 && InfoSocket >= 0)
{
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
struct in6_ifreq ifr6;
mDNSPlatformMemZero((char *)&ifr6, sizeof(ifr6));
strlcpy(ifr6.ifr_name, ifa->ifa_name, sizeof(ifr6.ifr_name));
if (!(ifru_flags6 & (IN6_IFF_NOTREADY | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY)))
{
if (ifa->ifa_flags & IFF_LOOPBACK)
- if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa;
- else v6Loopback = ifa;
+ {
+ if (ifa->ifa_addr->sa_family == AF_INET) v4Loopback = ifa;
+#ifndef NO_IPV6
+ else if (sin6->sin6_addr.s6_addr[0] != 0xFD) v6Loopback = ifa;
+#endif
+ }
else
{
NetworkInterfaceInfoOSX *i = AddInterfaceToList(m, ifa, utc);
MakeDomainLabelFromLiteralString(&hostlabel, defaultname);
}
+ mDNSBool namechange = mDNSfalse;
+
// We use a case-sensitive comparison here because even though changing the capitalization
// of the name alone is not significant to DNS, it's still a change from the user's point of view
if (SameDomainLabelCS(m->p->usernicelabel.c, nicelabel.c))
if (m->p->usernicelabel.c[0]) // Don't show message first time through, when we first read name from prefs on boot
LogMsg("User updated Computer Name from %#s to %#s", m->p->usernicelabel.c, nicelabel.c);
m->p->usernicelabel = m->nicelabel = nicelabel;
+ namechange = mDNStrue;
}
if (SameDomainLabelCS(m->p->userhostlabel.c, hostlabel.c))
LogMsg("User updated Local Hostname from %#s to %#s", m->p->userhostlabel.c, hostlabel.c);
m->p->userhostlabel = m->hostlabel = hostlabel;
mDNS_SetFQDN(m);
+ namechange = mDNStrue;
+ }
+
+#if APPLE_OSX_mDNSResponder
+ if (namechange) // If either name has changed, we need to tickle our AutoTunnel state machine to update its registered records
+ {
+ DomainAuthInfo *info;
+ for (info = m->AuthInfoList; info; info = info->next)
+ if (info->AutoTunnelNAT.clientContext && !mDNSIPv4AddressIsOnes(info->AutoTunnelNAT.ExternalAddress))
+ AutoTunnelNATCallback(m, &info->AutoTunnelNAT);
}
+#endif
return(mStatus_NoError);
}
}
else
{
- LogOperation("mDNSPlatformSetDNSConfig: Registering %d resolvers", config->n_resolver);
+ LogOperation("mDNSPlatformSetDNSConfig: config->n_resolver = %d", config->n_resolver);
if (setservers)
{
for (i = 0; i < config->n_resolver; i++)
}
// MUST be called holding the lock -- this routine calls SetupLocalAutoTunnelInterface_internal()
-mDNSlocal void SetDomainSecrets(mDNS *m)
+mDNSexport void SetDomainSecrets(mDNS *m)
{
#ifdef NO_SECURITYFRAMEWORK
(void)m;
}
#endif APPLE_OSX_mDNSResponder
+ // String Array used to write list of private domains to Dynamic Store
CFMutableArrayRef sa = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (!sa) { LogMsg("SetDomainSecrets: CFArrayCreateMutable failed"); return; }
CFIndex i;
if (CFDataGetTypeID() != CFGetTypeID(CFArrayGetValueAtIndex(entry, j)))
{ LogMsg("SetDomainSecrets: malformed entry item"); continue; }
- // Validate that attributes are not too large
- char dstring[MAX_ESCAPED_DOMAIN_NAME];
- char keynamebuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal C-string name, including terminating NUL
// The names have already been vetted by the helper, but checking them again here helps humans and automated tools verify correctness
+
+ // Get DNS domain this key is for
+ char stringbuf[MAX_ESCAPED_DOMAIN_NAME]; // Max legal domainname as C-string, including terminating NUL
data = CFArrayGetValueAtIndex(entry, 0);
- if (CFDataGetLength(data) >= (int)sizeof(dstring))
+ if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Bad kSecServiceItemAttr length %d", CFDataGetLength(data)); continue; }
- CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)dstring);
- dstring[CFDataGetLength(data)] = '\0';
- data = CFArrayGetValueAtIndex(entry, 1);
- if (CFDataGetLength(data) >= (int)sizeof(keynamebuf))
- { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
- CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)keynamebuf);
- keynamebuf[CFDataGetLength(data)] = '\0';
+ CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
+ stringbuf[CFDataGetLength(data)] = '\0';
domainname domain;
- if (!MakeDomainNameFromDNSNameString(&domain, dstring)) { LogMsg("SetDomainSecrets: bad key domain %s", dstring); continue; }
+ if (!MakeDomainNameFromDNSNameString(&domain, stringbuf)) { LogMsg("SetDomainSecrets: bad key domain %s", stringbuf); continue; }
+
+ // Get key name
+ data = CFArrayGetValueAtIndex(entry, 1);
+ if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
+ { LogMsg("SetDomainSecrets: Bad kSecAccountItemAttr length %d", CFDataGetLength(data)); continue; }
+ CFDataGetBytes(data, CFRangeMake(0,CFDataGetLength(data)), (UInt8 *)stringbuf);
+ stringbuf[CFDataGetLength(data)] = '\0';
- // Get DNS key name
domainname keyname;
- if (!MakeDomainNameFromDNSNameString(&keyname, keynamebuf)) { LogMsg("SetDomainSecrets: bad key name %s", keynamebuf); continue; }
+ if (!MakeDomainNameFromDNSNameString(&keyname, stringbuf)) { LogMsg("SetDomainSecrets: bad key name %s", stringbuf); continue; }
- // Get DNS key data
- char keystring[1024];
+ // Get key data
data = CFArrayGetValueAtIndex(entry, 2);
- if (CFDataGetLength(data) >= (int)sizeof(keystring))
+ if (CFDataGetLength(data) >= (int)sizeof(stringbuf))
{ LogMsg("SetDomainSecrets: Shared secret too long: %d", CFDataGetLength(data)); continue; }
- CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)keystring);
- keystring[CFDataGetLength(data)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key
+ CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), (UInt8 *)stringbuf);
+ stringbuf[CFDataGetLength(data)] = '\0'; // mDNS_SetSecretForDomain requires NULL-terminated C string for key
DomainAuthInfo *FoundInList;
for (FoundInList = m->AuthInfoList; FoundInList; FoundInList = FoundInList->next)
LogOperation("SetDomainSecrets: tunnel to %##s no longer marked for deletion", client->dstname.c);
client->markedForDeletion = mDNSfalse;
// If the key has changed, reconfigure the tunnel
- if (strncmp(keystring, client->b64keydata, sizeof(client->b64keydata)))
+ if (strncmp(stringbuf, client->b64keydata, sizeof(client->b64keydata)))
{
mDNSBool queryNotInProgress = client->q.ThisQInterval < 0;
LogOperation("SetDomainSecrets: secret changed for tunnel %##s %s", client->dstname.c, queryNotInProgress ? "reconfiguring" : "query in progress");
if (queryNotInProgress) AutoTunnelSetKeys(client, mDNSfalse);
- mDNS_snprintf(client->b64keydata, sizeof(client->b64keydata), "%s", keystring);
+ mDNS_snprintf(client->b64keydata, sizeof(client->b64keydata), "%s", stringbuf);
if (queryNotInProgress) AutoTunnelSetKeys(client, mDNStrue);
}
}
}
- mDNSBool keyChanged = FoundInList && FoundInList->AutoTunnel ? strncmp(keystring, FoundInList->b64keydata, sizeof(FoundInList->b64keydata)) : mDNSfalse;
+ mDNSBool keyChanged = FoundInList && FoundInList->AutoTunnel ? strncmp(stringbuf, FoundInList->b64keydata, sizeof(FoundInList->b64keydata)) : mDNSfalse;
#endif APPLE_OSX_mDNSResponder
// Uncomment the line below to view the keys as they're read out of the system keychain
// DO NOT SHIP CODE THIS WAY OR YOU'LL LEAK SECRET DATA INTO A PUBLICLY READABLE FILE!
- //LogOperation("SetDomainSecrets: %##s %##s %s", &domain.c, &keyname.c, keystring);
+ //LogOperation("SetDomainSecrets: %##s %##s %s", &domain.c, &keyname.c, stringbuf);
// If didn't find desired domain in the list, make a new entry
ptr = FoundInList;
}
LogOperation("SetDomainSecrets: %d of %d %##s", i, ArrayCount, &domain);
- if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, keystring, IsTunnelModeDomain(&domain)) == mStatus_BadParamErr)
+ if (mDNS_SetSecretForDomain(m, ptr, &domain, &keyname, stringbuf, IsTunnelModeDomain(&domain)) == mStatus_BadParamErr)
{
if (!FoundInList) mDNSPlatformMemFree(ptr); // If we made a new DomainAuthInfo here, and it turned out bad, dispose it immediately
continue;
if (keyChanged && AnonymousRacoonConfig)
{
LogOperation("SetDomainSecrets: secret changed for %##s", &domain);
- (void)mDNSConfigureServer(kmDNSUp, keystring);
+ (void)mDNSConfigureServer(kmDNSUp, stringbuf);
}
#endif APPLE_OSX_mDNSResponder
- CFStringRef cfs = CFStringCreateWithCString(NULL, dstring, kCFStringEncodingUTF8);
+ ConvertDomainNameToCString(&domain, stringbuf);
+ CFStringRef cfs = CFStringCreateWithCString(NULL, stringbuf, kCFStringEncodingUTF8);
if (cfs) { CFArrayAppendValue(sa, cfs); CFRelease(cfs); }
}
CFRelease(secrets);
{
ClientTunnel *cur = *ptr;
LogOperation("SetDomainSecrets: removing client %##s from list", cur->dstname.c);
- if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q);
+ if (cur->q.ThisQInterval >= 0) mDNS_StopQuery(m, &cur->q);
AutoTunnelSetKeys(cur, mDNSfalse);
*ptr = cur->next;
freeL("ClientTunnel", cur);
{
// stop the NAT operation
mDNS_StopNATOperation_internal(m, &info->AutoTunnelNAT);
- if (info->AutoTunnelHostRecord.namestorage.c[0] && info->AutoTunnelNAT.clientCallback)
+ if (info->AutoTunnelNAT.clientCallback)
{
- // reset port and let the AutoTunnelNATCallback handle cleanup
+ // Reset port and let the AutoTunnelNATCallback handle cleanup
info->AutoTunnelNAT.ExternalAddress = m->ExternalAddress;
info->AutoTunnelNAT.ExternalPort = zeroIPPort;
info->AutoTunnelNAT.Lifetime = 0;
{
LogOperation("*** Network Configuration Change *** (%d)%s",
m->p->NetworkChanged ? mDNS_TimeNow(m) - m->p->NetworkChanged : 0,
- m->p->NetworkChanged ? "": " (no scheduled configuration change)");
+ m->p->NetworkChanged ? "" : " (no scheduled configuration change)");
m->p->NetworkChanged = 0; // If we received a network change event and deferred processing, we're now dealing with it
mDNSs32 utc = mDNSPlatformUTC();
MarkAllInterfacesInactive(m, utc);
UpdateInterfaceList(m, utc);
- int nDeletions = ClearInactiveInterfaces(m, utc);
- int nAdditions = SetupActiveInterfaces(m, utc);
+ ClearInactiveInterfaces(m, utc);
+ SetupActiveInterfaces(m, utc);
#if APPLE_OSX_mDNSResponder
{
mDNSAddr tmpSrc = zeroAddr;
mDNSAddr tmpDst = { mDNSAddrType_IPv4, {{{0}}} };
tmpDst.ip.v4 = p->rmt_outer;
- FindSourceAddrForIP(&tmpDst, &tmpSrc);
+ mDNSPlatformSourceAddrForDest(&tmpSrc, &tmpDst);
if (!mDNSSameIPv6Address(p->loc_inner, m->AutoTunnelHostAddr) ||
!mDNSSameIPv4Address(p->loc_outer, tmpSrc.ip.v4))
{
}
#endif APPLE_OSX_mDNSResponder
- uDNS_SetupDNSConfig(m); // note - call DynDNSConfigChanged *before* mDNS_UpdateLLQs
- if (nDeletions || nAdditions) mDNS_UpdateLLQs(m); // so that LLQs are restarted against the up to date name servers
+ uDNS_SetupDNSConfig(m);
if (m->MainCallback)
m->MainCallback(m, mStatus_ConfigChanged);
m->p->NetworkChanged - NonZeroTime(m->timenow + delay) < 0)
m->p->NetworkChanged = NonZeroTime(m->timenow + delay);
+ // KeyChain frequently fails to notify clients of change events. To work around this
+ // we set a timer and periodically poll to detect if any changes have occurred.
+ // Without this Back To My Mac just does't work for a large number of users.
+ // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
+ if (c3 || CFArrayContainsValue(changedKeys, range, NetworkChangedKey_BackToMyMac))
+ {
+ LogOperation("*** NetworkChanged *** starting KeyChainBugTimer");
+ m->p->KeyChainBugTimer = NonZeroTime(m->timenow + delay);
+ m->p->KeyChainBugInterval = mDNSPlatformOneSecond;
+ }
+
if (!m->SuppressSending ||
m->SuppressSending - m->p->NetworkChanged < 0)
m->SuppressSending = m->p->NetworkChanged;
keychainEvent == kSecAddEvent ? "kSecAddEvent" :
keychainEvent == kSecDeleteEvent ? "kSecDeleteEvent" :
keychainEvent == kSecUpdateEvent ? "kSecUpdateEvent" : "<Unknown>");
+ // We're running on the CFRunLoop (Mach port) thread, not the kqueue thread, so we need to grab the KQueueLock before proceeding
KQueueLock(m);
mDNS_Lock(m);
SetDomainSecrets(m);
m->p->userhostlabel.c[0] = 0;
m->p->usernicelabel.c[0] = 0;
m->p->NotifyUser = 0;
+ m->p->KeyChainBugTimer = 0;
m->AutoTunnelHostAddr.b[0] = 0; // Zero out AutoTunnelHostAddr so UpdateInterfaceList() know it has to set it up
Change History (most recent first):
$Log: mDNSMacOSX.h,v $
+Revision 1.74 2007/11/02 20:18:13 cheshire
+<rdar://problem/5575583> BTMM: Work around keychain notification bug <rdar://problem/5124399>
+
+Revision 1.73 2007/10/17 18:42:06 cheshire
+Export SetDomainSecrets so its callable from other files
+
Revision 1.72 2007/08/01 16:09:14 cheshire
Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
mDNSs32 NotifyUser;
mDNSs32 HostNameConflict; // Time we experienced conflict on our link-local host name
mDNSs32 NetworkChanged;
+
+ // KeyChain frequently fails to notify clients of change events. To work around this
+ // we set a timer and periodically poll to detect if any changes have occurred.
+ // Without this Back To My Mac just does't work for a large number of users.
+ // See <rdar://problem/5124399> Not getting Keychain Changed events when enabling BTMM
+ mDNSs32 KeyChainBugTimer;
+ mDNSs32 KeyChainBugInterval;
+
SCDynamicStoreRef Store;
CFRunLoopSourceRef StoreRLS;
IONotificationPortRef PowerPortRef;
extern int KQueueFD;
extern void NotifyOfElusiveBug(const char *title, const char *msg); // Both strings are UTF-8 text
+extern void SetDomainSecrets(mDNS *m);
extern void mDNSMacOSXNetworkChanged(mDNS *const m);
extern int mDNSMacOSXSystemBuildNumber(char *HINFO_SWstring);
# then try typing "gmake os=xxx" instead.
#
# $Log: Makefile,v $
+# Revision 1.78 2007/10/22 20:16:49 cheshire
+# Got rid of jaguar and panther from list of target platforms;
+# changed "os=tiger" to "os=x" (which works with both Tiger and Leopard)
+#
+# Revision 1.77 2007/10/22 20:04:51 cheshire
+# Need to include PlatformCommon.c.o in embedded targets
+#
# Revision 1.76 2007/07/31 23:39:02 mcguire
# Don't bail on errors in flex-generated .c files
#
LDCONFIG = ldconfig
else
-ifeq ($(os),jaguar)
-CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -DNOT_HAVE_SOCKLEN_T
-LD = libtool -dynamic
-LINKOPTS = -lSystem
-LDSUFFIX = dylib
-JDK = /System/Library/Frameworks/JavaVM.framework/Home
-JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM
-else
-
-ifeq ($(os),panther)
-CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror
-LD = libtool -dynamic
-LINKOPTS = -lSystem
-LDSUFFIX = dylib
-JDK = /System/Library/Frameworks/JavaVM.framework/Home
-JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM
-else
-
-ifeq ($(os),tiger)
+ifeq ($(os),x)
CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement #-Wunreachable-code
CC = @gcc-4.0
LD = $(CC) -dynamiclib
JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM
else
-$(error ERROR: Must specify target OS on command-line, e.g. "make os=tiger [target]".\
-Supported operating systems include: jaguar, panther, tiger, linux, netbsd, freebsd, openbsd, solaris)
-endif
-endif
+$(error ERROR: Must specify target OS on command-line, e.g. "make os=x [target]".\
+Supported operating systems include: x, linux, netbsd, freebsd, openbsd, solaris)
endif
endif
endif
#############################################################################
# The following targets build embedded example programs
-SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o
+SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \
+ $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o
COMMONOBJ = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o
APPOBJ = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o
Change History (most recent first):
$Log: PosixDaemon.c,v $
+Revision 1.43 2007/10/22 20:05:34 cheshire
+Use mDNSPlatformSourceAddrForDest instead of FindSourceAddrForIP
+
Revision 1.42 2007/09/18 19:09:02 cheshire
<rdar://problem/5489549> mDNSResponderHelper (and other binaries) missing SCCS version strings
static void Reconfigure(mDNS *m)
{
mDNSAddr DynDNSIP;
+ const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };;
mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL);
if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0)
LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable");
ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL);
- FindSourceAddrForIP(NULL, &DynDNSIP);
+ mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy);
if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL);
if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL);
m->MainCallback(m, mStatus_ConfigChanged);
Change History (most recent first):
$Log: PlatformCommon.c,v $
+Revision 1.13 2007/10/22 20:07:07 cheshire
+Moved mDNSPlatformSourceAddrForDest from mDNSMacOSX.c to PlatformCommon.c so
+Posix build can share the code (better than just pasting it into mDNSPosix.c)
+
+Revision 1.12 2007/10/16 17:19:53 cheshire
+<rdar://problem/3557903> Performance: Core code will not work on platforms with small stacks
+Cut ReadDDNSSettingsFromConfFile stack from 2112 to 1104 bytes
+
Revision 1.11 2007/07/31 23:08:34 mcguire
<rdar://problem/5329542> BTMM: Make AutoTunnel mode work with multihoming
#include <stdio.h> // Needed for fopen() etc.
#include <unistd.h> // Needed for close()
#include <string.h> // Needed for strlen() etc.
-#include <errno.h> // Needed for errno etc.
+#include <errno.h> // Needed for errno etc.
#include <sys/socket.h> // Needed for socket() etc.
#include <netinet/in.h> // Needed for sockaddr_in
#endif
// Bind a UDP socket to find the source address to a destination
-mDNSexport void FindSourceAddrForIP(mDNSAddr *const dst, mDNSAddr *src)
+mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
{
- struct sockaddr_in addr;
+ union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr;
socklen_t len = sizeof(addr);
- int sock = socket(AF_INET,SOCK_DGRAM,0);
+ int sock = socket(AF_INET, SOCK_DGRAM, 0);
src->type = mDNSAddrType_None;
if (sock == -1) return;
- if (dst->type != mDNSAddrType_IPv4) return; // Only support v4 for now
- addr.sin_family = AF_INET;
- addr.sin_port = 1; // Not important, any port will do
- if (dst == NULL) addr.sin_addr.s_addr = 0x11111111;
- else addr.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
- if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return; }
- if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return; }
+ if (dst->type == mDNSAddrType_IPv4)
+ {
+ addr.a4.sin_len = sizeof(addr.a4);
+ addr.a4.sin_family = AF_INET;
+ addr.a4.sin_port = 1; // Not important, any port will do
+ addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
+ }
+ else if (dst->type == mDNSAddrType_IPv6)
+ {
+ addr.a6.sin6_len = sizeof(addr.a6);
+ addr.a6.sin6_family = AF_INET6;
+ addr.a6.sin6_flowinfo = 0;
+ addr.a6.sin6_port = 1; // Not important, any port will do
+ addr.a6.sin6_addr = *(struct in6_addr*)&dst->ip.v6;
+ addr.a6.sin6_scope_id = 0;
+ }
+ else return;
+
+ if ((connect(sock, &addr.s, addr.s.sa_len)) < 0)
+ { LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno)); goto exit; }
+
+ if ((getsockname(sock, &addr.s, &len)) < 0)
+ { LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; }
+
+ src->type = dst->type;
+ if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr;
+ else src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr;
+exit:
close(sock);
- src->type = mDNSAddrType_IPv4;
- src->ip.v4.NotAnInteger = addr.sin_addr.s_addr;
}
// dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length
mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled)
{
- char buf [MAX_ESCAPED_DOMAIN_NAME];
- char secret[MAX_ESCAPED_DOMAIN_NAME] = "";
+ char buf[MAX_ESCAPED_DOMAIN_NAME] = "";
mStatus err;
FILE *f = fopen(filename, "r");
if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue;
if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf;
if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf;
- GetConfigOption(secret, "secret-64", f); // failure means no authentication
+ buf[0] = 0;
+ GetConfigOption(buf, "secret-64", f); // failure means no authentication
fclose(f);
f = NULL;
}
return;
}
- if (domain && domain->c[0] && secret[0])
+ if (domain && domain->c[0] && buf[0])
{
DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info));
// for now we assume keyname = service reg domain and we use same key for service and hostname registration
- err = mDNS_SetSecretForDomain(m, info, domain, domain, secret, mDNSfalse);
+ err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, mDNSfalse);
if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
}
Change History (most recent first):
$Log: PlatformCommon.h,v $
+Revision 1.8 2007/10/22 20:07:52 cheshire
+Deleted unused FindSourceAddrForIP() function
+
Revision 1.7 2007/07/31 23:08:34 mcguire
<rdar://problem/5329542> BTMM: Make AutoTunnel mode work with multihoming
*/
-extern void FindSourceAddrForIP(mDNSAddr *const dst, mDNSAddr *src);
extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled);
*/
#ifndef _DNS_SD_H
-#define _DNS_SD_H 1610100
+#define _DNS_SD_H 1640000
#ifdef __cplusplus
extern "C" {
Change History (most recent first):
$Log: dnsextd.c,v $
+Revision 1.84 2007/10/24 18:19:37 cheshire
+Fixed header byte order bug sending update responses
+
+Revision 1.83 2007/10/17 22:52:26 cheshire
+Get rid of unused mDNS_UpdateLLQs()
+
Revision 1.82 2007/09/27 17:42:49 cheshire
Fix naming: for consistency, "kDNSFlag1_RC" should be "kDNSFlag1_RC_Mask"
ptr = putUpdateLease(&reply->msg, ptr, lease);
if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; }
reply->len = ptr - (mDNSu8 *)&reply->msg;
+ HdrHToN(reply);
return reply;
}
int i, adds = 0, dels = 0;
const mDNSu8 *ptr, *end = (mDNSu8 *)&request->msg + request->len;
HdrNToH(request);
- lease = GetPktLease(NULL, &request->msg, end);
+ lease = GetPktLease(&mDNSStorage, &request->msg, end);
ptr = LocateAuthorities(&request->msg, end);
for (i = 0; i < request->msg.h.mDNS_numUpdates; i++)
{
const domainname *domain, const domainname *keyname, const char *b64keydata, mDNSBool AutoTunnel)
{ ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) AutoTunnel; return 0; }
mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
-void mDNS_UpdateLLQs(mDNS * const m) { ( void ) m; }
mDNS mDNSStorage;
Change History (most recent first):
$Log: uds_daemon.c,v $
+Revision 1.379 2007/11/01 19:32:14 cheshire
+Added "DEBUG_64BIT_SCM_RIGHTS" debugging code
+
+Revision 1.378 2007/10/31 19:21:40 cheshire
+Don't show Expire time for records and services that aren't currently registered
+
+Revision 1.377 2007/10/30 23:48:20 cheshire
+Improved SIGINFO listing of question state
+
+Revision 1.376 2007/10/30 20:43:54 cheshire
+Fixed compiler warning when LogAllOperations is turned off
+
+Revision 1.375 2007/10/26 22:51:38 cheshire
+Improved SIGINFO output to show timers for AuthRecords and ServiceRegistrations
+
+Revision 1.374 2007/10/25 22:45:02 cheshire
+Tidied up code for DNSServiceRegister callback status messages
+
+Revision 1.373 2007/10/25 21:28:43 cheshire
+Add ServiceRegistrations to SIGINFO output
+
+Revision 1.372 2007/10/25 21:21:45 cheshire
+<rdar://problem/5496734> BTMM: Need to retry registrations after failures
+Don't unlink_and_free_service_instance at the first error
+
+Revision 1.371 2007/10/18 23:34:40 cheshire
+<rdar://problem/5532821> Need "considerable burden on the network" warning in uds_daemon.c
+
+Revision 1.370 2007/10/17 18:44:23 cheshire
+<rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
+
+Revision 1.369 2007/10/16 17:18:27 cheshire
+Fixed Posix compile errors
+
+Revision 1.368 2007/10/16 16:58:58 cheshire
+Improved debugging error messages in read_msg()
+
+Revision 1.367 2007/10/15 22:55:14 cheshire
+Make read_msg return "void" (since request_callback just ignores the redundant return value anyway)
+
Revision 1.366 2007/10/10 00:48:54 cheshire
<rdar://problem/5526379> Daemon spins in an infinite loop when it doesn't get the control message it's expecting
DNSQuestion qsrv;
const ResourceRecord *txt;
const ResourceRecord *srv;
+ mDNSs32 ReportTime;
} resolve;
;
} u;
mDNSBool SuppressError = mDNSfalse;
service_instance *instance = srs->ServiceContext;
reply_state *rep;
+#if LogAllOperations || MDNS_DEBUGMSGS
+ char *fmt = (result == mStatus_NoError) ? "%3d: DNSServiceRegister(%##s, %u) REGISTERED" :
+ (result == mStatus_MemFree) ? "%3d: DNSServiceRegister(%##s, %u) DEREGISTERED" :
+ (result == mStatus_NameConflict) ? "%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT" :
+ "%3d: DNSServiceRegister(%##s, %u) %s %d";
+#endif
(void)m; // Unused
if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; }
if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
!instance->default_local)
SuppressError = mDNStrue;
- if (result == mStatus_NoError)
- LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
- else if (result == mStatus_MemFree)
- LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
- else if (result == mStatus_NameConflict)
- LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
- else
- LogOperation("%3d: DNSServiceRegister(%##s, %u) CALLBACK %d", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result);
+ LogOperation(fmt, instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), SuppressError ? "suppressed error" : "CALLBACK", result);
if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; }
}
else
{
- if (result != mStatus_NATTraversal)
- LogMsg("regservice_callback: Error %d%s for %s", result, SuppressError ? " (suppressed)" : "", ARDisplayString(m, &srs->RR_SRV));
if (!SuppressError)
{
if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->sd, srs->RR_SRV.resrec.name->c);
else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
}
- unlink_and_free_service_instance(instance);
}
}
mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
{
(void)m; // unused
- if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext);
+ if (result == mStatus_MemFree)
+ {
+ // On shutdown, mDNS_Close automatically deregisters all records
+ // Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record
+ // from the LocalDomainEnumRecords list, we do this here before we free the memory.
+ ARListElem **ptr = &LocalDomainEnumRecords;
+ while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
+ if (*ptr) *ptr = (*ptr)->next;
+ mDNSPlatformMemFree(rr->RecordContext);
+ }
}
mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
request->u.resolve.qtxt.ReturnIntermed = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
request->u.resolve.qtxt.QuestionContext = request;
+ request->u.resolve.ReportTime = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
// ask the questions
LogOperation("%3d: DNSServiceResolve(%##s) START", request->sd, request->u.resolve.qsrv.qname.c);
}
// read_msg may be called any time when the transfer state (req->ts) is t_morecoming.
-// returns the current state of the request (morecoming, error, complete, terminated.)
// if there is no data on the socket, the socket will be closed and t_terminated will be returned
-// *** NOTE return value is actually ignored -- should change return type to void ***
-mDNSlocal int read_msg(request_state *req)
+mDNSlocal void read_msg(request_state *req)
{
- mDNSu32 nleft;
- int nread;
-
if (req->ts == t_terminated || req->ts == t_error)
- {
- LogMsg("ERROR: read_msg called with transfer state terminated or error");
- req->ts = t_error;
- return t_error;
- }
+ { LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; }
if (req->ts == t_complete) // this must be death or something is wrong
{
char buf[4]; // dummy for death notification
- nread = recv(req->sd, buf, 4, 0);
- if (!nread) { req->ts = t_terminated; return t_terminated; }
+ int nread = recv(req->sd, buf, 4, 0);
+ if (!nread) { req->ts = t_terminated; return; }
if (nread < 0) goto rerror;
- LogMsg("ERROR: read data from a completed request.");
+ LogMsg("%3d: ERROR: read data from a completed request", req->sd);
req->ts = t_error;
- return t_error;
+ return;
}
if (req->ts != t_morecoming)
- {
- LogMsg("ERROR: read_msg called with invalid transfer state (%d)", req->ts);
- req->ts = t_error;
- return t_error;
- }
+ { LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; }
if (req->hdr_bytes < sizeof(ipc_msg_hdr))
{
- nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
- nread = recv(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0);
- if (nread == 0) { req->ts = t_terminated; return t_terminated; }
+ mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
+ int nread = recv(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0);
+ if (nread == 0) { req->ts = t_terminated; return; }
if (nread < 0) goto rerror;
req->hdr_bytes += nread;
if (req->hdr_bytes > sizeof(ipc_msg_hdr))
- {
- LogMsg("ERROR: read_msg - read too many header bytes");
- req->ts = t_error;
- return t_error;
- }
+ { LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; }
// only read data if header is complete
if (req->hdr_bytes == sizeof(ipc_msg_hdr))
{
ConvertHeaderBytes(&req->hdr);
if (req->hdr.version != VERSION)
- { LogMsg("ERROR: client version 0x%08X daemon version 0x%08X", req->hdr.version, VERSION); req->ts = t_error; return t_error; }
+ { LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; }
// Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord()
// with 64kB of rdata. Adding 1005 byte for a maximal domain name, plus a safety margin
// for other overhead, this means any message above 70kB is definitely bogus.
if (req->hdr.datalen > 70000)
- {
- LogMsg("ERROR: read_msg - hdr.datalen %lu (%X) > 70000", req->hdr.datalen, req->hdr.datalen);
- req->ts = t_error;
- return t_error;
- }
+ { LogMsg("%3d: ERROR: read_msg - hdr.datalen %lu (%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; }
req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES);
- if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return t_error; }
+ if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; }
req->msgptr = req->msgbuf;
req->msgend = req->msgbuf + req->hdr.datalen;
mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES);
// (even if only the one-byte empty C string placeholder for the old ctrl_path parameter)
if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen)
{
- nleft = req->hdr.datalen - req->data_bytes;
+ mDNSu32 nleft = req->hdr.datalen - req->data_bytes;
+ int nread;
struct iovec vec = { req->msgbuf + req->data_bytes, nleft }; // Tell recvmsg where we want the bytes put
struct msghdr msg;
struct cmsghdr *cmsg;
- char cbuf[sizeof(struct cmsghdr) + sizeof(dnssd_sock_t)];
+ char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &vec;
msg.msg_controllen = sizeof(cbuf);
msg.msg_flags = 0;
nread = recvmsg(req->sd, &msg, 0);
- if (nread == 0) { req->ts = t_terminated; return t_terminated; }
+ if (nread == 0) { req->ts = t_terminated; return; }
if (nread < 0) goto rerror;
req->data_bytes += nread;
if (req->data_bytes > req->hdr.datalen)
- {
- LogMsg("ERROR: read_msg - read too many data bytes");
- req->ts = t_error;
- return t_error;
- }
+ { LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; }
cmsg = CMSG_FIRSTHDR(&msg);
+#if DEBUG_64BIT_SCM_RIGHTS
+ LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf), sizeof(cbuf), SOL_SOCKET, SCM_RIGHTS);
+ LogMsg("%3d: Got %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
+#endif DEBUG_64BIT_SCM_RIGHTS
if (msg.msg_controllen == sizeof(cbuf) &&
cmsg->cmsg_len == sizeof(cbuf) &&
cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS)
{
req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+#if DEBUG_64BIT_SCM_RIGHTS
+ LogMsg("%3d: read req->errsd %d", req->sd, req->errsd);
+#endif DEBUG_64BIT_SCM_RIGHTS
if (req->data_bytes < req->hdr.datalen)
{
LogMsg("%3d: Client sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
req->sd, req->errsd, req->data_bytes, req->hdr.datalen);
req->ts = t_error;
- return t_error;
+ return;
}
}
}
if (ctrl_path[0] == 0)
{
if (req->errsd == req->sd)
- { LogMsg("%3d: request_callback: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return t_error; }
+ { LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; }
goto got_errfd;
}
#endif
req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0);
- if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return t_error; }
-
+ if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return; }
+
if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
{
#if !defined(USE_TCP_LOOPBACK)
struct stat sb;
- LogMsg("request_callback: Couldn't connect to error return path socket “%s” errno %d %s",
- cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+ LogMsg("%3d: read_msg: Couldn't connect to error return path socket “%s” errno %d %s",
+ req->sd, cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
if (stat(cliaddr.sun_path, &sb) < 0)
- LogMsg("request_callback: stat failed “%s” errno %d %s", cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+ LogMsg("%3d: read_msg: stat failed “%s” errno %d %s", req->sd, cliaddr.sun_path, dnssd_errno(), dnssd_strerror(dnssd_errno()));
else
- LogMsg("request_callback: file “%s” mode %o (octal) uid %d gid %d", cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid);
+ LogMsg("%3d: read_msg: file “%s” mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid);
#endif
req->ts = t_error;
- return t_error;
+ return;
}
got_errfd:
#else
if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0)
#endif
- { my_perror("ERROR: could not set control socket to non-blocking mode"); req->ts = t_error; return t_error; }
+ {
+ LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d %s",
+ req->sd, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+ req->ts = t_error;
+ return;
+ }
}
req->ts = t_complete;
}
- return req->ts;
+ return;
rerror:
- if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming;
- my_perror("ERROR: read_msg");
+ if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return;
+ LogMsg("%3d: ERROR: read_msg errno %d %s", req->sd, dnssd_errno(), dnssd_strerror(dnssd_errno()));
req->ts = t_error;
- return t_error;
}
#define RecordOrientedOp(X) \
if (req->hdr.version != VERSION)
{
- LogMsg("ERROR: client incompatible with daemon (client version = %d, "
- "daemon version = %d)\n", req->hdr.version, VERSION);
+ LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
AbortUnlinkAndFree(req);
return;
}
else
{
AuthRecord *ar;
+ LogMsgNoIdent(" Int Next Expire State");
for (ar = m->ResourceRecords; ar; ar=ar->next)
- LogMsgNoIdent("%s", ARDisplayString(m, ar));
+ LogMsgNoIdent("%7d %7d %7d %7d %s",
+ ar->ThisAPInterval / mDNSPlatformOneSecond,
+ AuthRecord_uDNS(ar) || ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+ AuthRecord_uDNS(ar) && ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
+ ar->state, ARDisplayString(m, ar));
+ }
+
+ LogMsgNoIdent("----- ServiceRegistrations -----");
+ if (!m->ServiceRegistrations) LogMsgNoIdent("<None>");
+ else
+ {
+ ServiceRecordSet *s;
+ LogMsgNoIdent(" Int Next Expire State");
+ for (s = m->ServiceRegistrations; s; s = s->uDNS_next)
+ LogMsgNoIdent("%7d %7d %7d %7d %s",
+ s->RR_SRV.ThisAPInterval / mDNSPlatformOneSecond,
+ (s->RR_SRV.LastAPTime + s->RR_SRV.ThisAPInterval - now) / mDNSPlatformOneSecond,
+ s->RR_SRV.expire ? (s->RR_SRV.expire - now) / mDNSPlatformOneSecond : 0,
+ s->state, ARDisplayString(m, &s->RR_SRV));
}
LogMsgNoIdent("---------- Questions -----------");
DNSQuestion *q;
CacheUsed = 0;
CacheActive = 0;
- LogMsgNoIdent(" Int Next if T NumAns Type Name");
+ LogMsgNoIdent(" Int Next if T NumAns Type Name");
for (q = m->Questions; q; q=q->next)
{
mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond;
NetworkInterfaceInfo *info = (NetworkInterfaceInfo *)q->InterfaceID;
CacheUsed++;
if (q->ThisQInterval) CacheActive++;
- LogMsgNoIdent("%6d%6d %-6s%s %5d %-6s%##s%s",
+ LogMsgNoIdent("%6d%6d %-6s%s%s %5d %-6s%##s%s",
i, n,
info ? info->ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
mDNSOpaque16IsZero(q->TargetQID) ? " " : q->LongLived ? "L" : "O", // mDNS, long-lived, or one-shot query?
+ q->AuthInfo ? "P" : " ",
q->CurrentAnswers,
DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
usleep(1000); // Limit rate a little so we don't flood syslog too fast
while (*req)
{
+ if ((*req)->terminate == resolve_termination_callback)
+ if ((*req)->u.resolve.ReportTime && now - (*req)->u.resolve.ReportTime >= 0)
+ {
+ (*req)->u.resolve.ReportTime = 0;
+ LogMsgNoIdent("Client application bug: DNSServiceResolver(%##s) active for over two minutes. "
+ "This places considerable burden on the network.", (*req)->u.resolve.qsrv.qname.c);
+ }
+
while ((*req)->replies) // Send queued replies
{
transfer_state result;
Change History (most recent first):
$Log: mDNSWin32.c,v $
+Revision 1.129 2007/10/17 22:52:26 cheshire
+Get rid of unused mDNS_UpdateLLQs()
+
Revision 1.128 2007/09/12 19:23:17 cheshire
Get rid of unnecessary mDNSPlatformTCPIsConnected() routine
err = uDNS_SetupDNSConfig( inMDNS );
check_noerr( err );
- // so that LLQs are restarted against the up to date name servers
-
- mDNS_UpdateLLQs( inMDNS );
-
mDNSPlatformUnlock( inMDNS );
// Inform clients of the change.
err = uDNS_SetupDNSConfig( inMDNS );
check_noerr( err );
- // so that LLQs are restarted against the up to date name servers
-
- mDNS_UpdateLLQs( inMDNS );
-
// and reset the event handler
if ( ( inMDNS->p->tcpipKey != NULL ) && ( inMDNS->p->tcpipChangedEvent ) )
err = uDNS_SetupDNSConfig( inMDNS );
check_noerr( err );
- // so that LLQs are restarted against the up to date name servers
-
- mDNS_UpdateLLQs( inMDNS );
-
// and reset the event handler
if ((inMDNS->p->ddnsKey != NULL) && (inMDNS->p->ddnsChangedEvent))