+Revision 1.642 2007/06/15 21:54:50 cheshire
+<rdar://problem/4883206> Add packet logging to help debugging private browsing over TLS
+
+Revision 1.641 2007/05/25 00:30:24 cheshire
+When checking for duplicate questions, make sure privacy (or not) status, and long-lived (or not)
+status matches. This is particularly important when doing a private query for an SOA record,
+which will result in a call StartGetZoneData which does a non-private query for the same SOA record.
+If the latter is tagged as a duplicate of the former, then we have deadlock, and neither will complete.
+
+Revision 1.640 2007/05/25 00:25:44 cheshire
+<rdar://problem/5227737> Need to enhance putRData to output all current known types
+
+Revision 1.639 2007/05/23 00:51:33 cheshire
+Increase threshold for shedding cache records from 512 to 3000. The 512 figure was calculated when
+each cache entry took about 700 bytes; now they're only 164 bytes. Also, machines have more RAM these
+days, and there are more services being advertised using DNS-SD, so it makes sense to cache more.
+
+Revision 1.638 2007/05/23 00:43:16 cheshire
+If uDNS UDP response has TC (truncated) bit set, don't interpret it as being the entire RRSet
+
+Revision 1.637 2007/05/14 23:53:00 cheshire
+Export mDNS_StartQuery_internal and mDNS_StopQuery_internal so they can be called from uDNS.c
+
+Revision 1.636 2007/05/10 23:27:15 cheshire
+Update mDNS_Deregister_internal debugging messages
+
+Revision 1.635 2007/05/07 20:43:45 cheshire
+<rdar://problem/4241419> Reduce the number of queries and announcements
+
+Revision 1.634 2007/05/04 22:09:08 cheshire
+Only do "restarting exponential backoff sequence" for mDNS questions
+In mDNS_RegisterInterface, only retrigger mDNS questions
+In uDNS_SetupDNSConfig, use ActivateUnicastQuery() instead of just setting q->ThisQInterval directly
+
+Revision 1.633 2007/05/04 21:45:12 cheshire
+Get rid of unused q->RestartTime; Get rid of uDNS_Close (synonym for uDNS_Sleep)
+
+Revision 1.632 2007/05/04 20:20:50 cheshire
+<rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData
+Need to set srs->nta = mDNSNULL; when regState_NoTarget
+
+Revision 1.631 2007/05/04 00:39:42 cheshire
+<rdar://problem/4410011> Eliminate looping SOA lookups
+When creating a cascade of negative SOA cache entries, CacheGroup pointer cg needs to be updated
+each time round the loop to reference the right CacheGroup for each newly fabricated SOA name
+
+Revision 1.630 2007/05/03 22:40:38 cheshire
+<rdar://problem/4669229> mDNSResponder ignores bogus null target in SRV record
+
+Revision 1.629 2007/05/03 00:15:51 cheshire
+<rdar://problem/4410011> Eliminate looping SOA lookups
+
+Revision 1.628 2007/05/02 22:21:33 cheshire
+<rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData
+
+Revision 1.627 2007/04/30 19:29:13 cheshire
+Fix display of port number in "Updating DNS Server" message
+
+Revision 1.626 2007/04/30 04:21:13 cheshire
+Can't safely call AnswerLocalQuestions() from within mDNS_Deregister() -- need to defer it until mDNS_Execute time
+
+Revision 1.625 2007/04/28 01:34:21 cheshire
+Fixed crashing bug: We need to update rr->CRActiveQuestion pointers for *all* questions
+(Code was explicitly ignoring wide-area unicast questions, leading to stale pointers and crashes)
+
+Revision 1.624 2007/04/27 21:04:30 cheshire
+On network configuration change, need to call uDNS_RegisterSearchDomains
+
+Revision 1.623 2007/04/27 19:28:01 cheshire
+Any code that calls StartGetZoneData needs to keep a handle to the structure, so
+it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
+-- it would start a query and then quickly cancel it, and then when
+StartGetZoneData completed, it had a dangling pointer and crashed.)
+
+Revision 1.622 2007/04/26 16:09:22 cheshire
+mDNS_StopQueryWithRemoves should ignore kDNSRecordTypePacketNegative records
+
+Revision 1.621 2007/04/26 15:43:22 cheshire
+Make sure DNSServer *s is non-null before using value in LogOperation
+
+Revision 1.620 2007/04/26 13:11:05 cheshire
+Fixed crash when logging out of VPN
+
+Revision 1.619 2007/04/26 00:35:15 cheshire
+<rdar://problem/5140339> uDNS: Domain discovery not working over VPN
+Fixes to make sure results update correctly when connectivity changes (e.g. a DNS server
+inside the firewall may give answers where a public one gives none, and vice versa.)
+
+Revision 1.618 2007/04/25 19:26:01 cheshire
+m->NextScheduledQuery was getting set too early in SendQueries()
+Improved "SendQueries didn't send all its queries" debugging message
+
+Revision 1.617 2007/04/25 17:48:22 cheshire
+Update debugging message
+
+Revision 1.616 2007/04/25 16:38:32 cheshire
+If negative cache entry already exists, reactivate it instead of creating a new one
+
+Revision 1.615 2007/04/25 02:14:38 cheshire
+<rdar://problem/4246187> uDNS: Identical client queries should reference a single shared core query
+Additional fixes to make LLQs work properly
+
+Revision 1.614 2007/04/23 21:52:45 cheshire
+<rdar://problem/5094009> IPv6 filtering in AirPort base station breaks Wide-Area Bonjour
+
+Revision 1.613 2007/04/23 04:58:20 cheshire
+<rdar://problem/5072548> Crash when setting extremely large TXT records
+
+Revision 1.612 2007/04/22 20:39:38 cheshire
+<rdar://problem/4633194> Add 20 to 120ms random delay to browses
+
+Revision 1.611 2007/04/22 18:16:29 cheshire
+Removed incorrect ActiveQuestion(q) check that was preventing suspended questions from getting reactivated
+
+Revision 1.610 2007/04/22 06:02:02 cheshire
+<rdar://problem/4615977> Query should immediately return failure when no server
+
+Revision 1.609 2007/04/20 21:17:24 cheshire
+For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
+
+Revision 1.608 2007/04/20 19:45:31 cheshire
+In LogAllOperations mode, dump out unknown DNS packets in their entirety
+
+Revision 1.607 2007/04/19 23:56:25 cheshire
+Don't do cache-flush processing for LLQ answers
+
+Revision 1.606 2007/04/19 22:50:53 cheshire
+<rdar://problem/4246187> Identical client queries should reference a single shared core query
+
+Revision 1.605 2007/04/19 20:06:41 cheshire
+Rename field 'Private' (sounds like a boolean) to more informative 'AuthInfo' (it's a DomainAuthInfo pointer)
+
+Revision 1.604 2007/04/19 18:03:04 cheshire
+Add "const" declaration
+
+Revision 1.603 2007/04/06 21:00:25 cheshire
+Fix log message typo
+
+Revision 1.602 2007/04/05 22:55:35 cheshire
+<rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
+
+Revision 1.601 2007/04/04 21:48:52 cheshire
+<rdar://problem/4720694> Combine unicast authoritative answer list with multicast list
+
+Revision 1.600 2007/04/04 01:31:33 cheshire
+Improve debugging message
+
+Revision 1.599 2007/04/04 00:03:26 cheshire
+<rdar://problem/5089862> DNSServiceQueryRecord is returning kDNSServiceErr_NoSuchRecord for empty rdata
+
+Revision 1.598 2007/04/03 19:43:16 cheshire
+Use mDNSSameIPPort (and similar) instead of accessing internal fields directly
+
+Revision 1.597 2007/03/31 00:32:32 cheshire
+After skipping OPT and TSIG, clear m->rec.r.resrec.RecordType
+
+Revision 1.596 2007/03/28 20:59:26 cheshire
+<rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
+
+Revision 1.595 2007/03/26 23:48:16 cheshire
+<rdar://problem/4848295> Advertise model information via Bonjour
+Refinements to reduce unnecessary transmissions of the DeviceInfo TXT record
+
+Revision 1.594 2007/03/26 23:05:05 cheshire
+<rdar://problem/5089257> Don't cache TSIG records
+
+Revision 1.593 2007/03/23 17:40:08 cheshire
+<rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision
+
+Revision 1.592 2007/03/22 18:31:48 cheshire
+Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
+
+Revision 1.591 2007/03/22 00:49:19 cheshire
+<rdar://problem/4848295> Advertise model information via Bonjour
+
+Revision 1.590 2007/03/21 23:05:59 cheshire
+Rename uDNS_HostnameInfo to HostnameInfo; deleted some unused fields
+
+Revision 1.589 2007/03/20 15:37:19 cheshire
+Delete unnecessary log message
+
+Revision 1.588 2007/03/20 00:24:44 cheshire
+<rdar://problem/4175213> Should deliver "name registered" callback slightly *before* announcing PTR record
+
+Revision 1.587 2007/03/16 22:10:56 cheshire
+<rdar://problem/4471307> mDNS: Query for *either* type A or AAAA should return both types
+
+Revision 1.586 2007/03/10 03:26:44 cheshire
+<rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
+
+Revision 1.585 2007/03/10 02:02:58 cheshire
+<rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
+Eliminate unnecessary "InternalResponseHndlr responseCallback" function pointer
+
+Revision 1.584 2007/02/28 01:51:27 cheshire
+Added comment about reverse-order IP address
+
+Revision 1.583 2007/01/27 03:19:33 cheshire
+Need to initialize question->sock
+
+Revision 1.582 2007/01/25 00:40:16 cheshire
+Unified CNAME-following functionality into cache management code (which means CNAME-following
+should now also work for mDNS queries too); deleted defunct pktResponseHndlr() routine.
+
+Revision 1.581 2007/01/23 02:56:11 cheshire
+Store negative results in the cache, instead of generating them out of pktResponseHndlr()
+
+Revision 1.580 2007/01/19 21:17:33 cheshire
+StartLLQPolling needs to call SetNextQueryTime() to cause query to be done in a timely fashion
+
+Revision 1.579 2007/01/19 18:39:10 cheshire
+Fix a bunch of parameters that should have been declared "const"
+
+Revision 1.578 2007/01/10 22:51:57 cheshire
+<rdar://problem/4917539> Add support for one-shot private queries as well as long-lived private queries
+
+Revision 1.577 2007/01/10 02:05:21 cheshire
+Delay uDNS_SetupDNSConfig() until *after* the platform layer
+has set up the interface list and security credentials
+
+Revision 1.576 2007/01/09 02:40:57 cheshire
+uDNS_SetupDNSConfig() shouldn't be called from mDNSMacOSX.c (platform support layer);
+moved it to mDNS_Init() in mDNS.c (core code)
+
+Revision 1.575 2007/01/09 00:17:25 cheshire
+Improve "ERROR m->CurrentRecord already set" debugging messages
+
+Revision 1.574 2007/01/05 08:30:41 cheshire
+Trim excessive "$Log" checkin history from before 2006
+(checkin history still available via "cvs log ..." of course)
+
+Revision 1.573 2007/01/05 06:34:03 cheshire
+Improve "ERROR m->CurrentQuestion already set" debugging messages
+
+Revision 1.572 2007/01/04 23:11:11 cheshire
+<rdar://problem/4720673> uDNS: Need to start caching unicast records
+When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries
+
+Revision 1.571 2007/01/04 21:45:20 cheshire
+Added mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback macros,
+to do additional lock sanity checking around callback invocations
+
+Revision 1.570 2007/01/04 20:57:47 cheshire
+Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates)
+
+Revision 1.569 2007/01/04 20:27:27 cheshire
+Change a LogMsg() to debugf()
+
+Revision 1.568 2007/01/04 02:39:53 cheshire
+<rdar://problem/4030599> Hostname passed into DNSServiceRegister ignored for Wide-Area service registrations
+
+Revision 1.567 2006/12/21 00:01:37 cheshire
+Tidy up code alignment
+
+Revision 1.566 2006/12/20 04:07:34 cheshire
+Remove uDNS_info substructure from AuthRecord_struct
+
+Revision 1.565 2006/12/19 22:49:23 cheshire
+Remove uDNS_info substructure from ServiceRecordSet_struct
+
+Revision 1.564 2006/12/19 02:38:20 cheshire
+Get rid of unnecessary duplicate query ID field from DNSQuestion_struct
+
+Revision 1.563 2006/12/19 02:18:48 cheshire
+Get rid of unnecessary duplicate "void *context" field from DNSQuestion_struct
+
+Revision 1.562 2006/12/16 01:58:31 cheshire
+<rdar://problem/4720673> uDNS: Need to start caching unicast records
+
+Revision 1.561 2006/12/01 07:38:53 herscher
+Only perform cache workaround fix if query is wide-area
+
+Revision 1.560 2006/11/30 23:07:56 herscher
+<rdar://problem/4765644> uDNS: Sync up with Lighthouse changes for Private DNS
+
+Revision 1.559 2006/11/27 08:20:57 cheshire
+Preliminary support for unifying the uDNS and mDNS code, including caching of uDNS answers
+
+Revision 1.558 2006/11/10 07:44:03 herscher
+<rdar://problem/4825493> Fix Daemon locking failures while toggling BTMM
+
+Revision 1.557 2006/11/10 01:12:51 cheshire
+<rdar://problem/4829718> Incorrect TTL corrections
+
+Revision 1.556 2006/11/10 00:54:14 cheshire
+<rdar://problem/4816598> Changing case of Computer Name doesn't work
+
+Revision 1.555 2006/10/30 20:03:37 cheshire
+<rdar://problem/4456945> After service restarts on different port, for a few seconds DNS-SD may return stale port number
+
+Revision 1.554 2006/10/20 05:35:04 herscher
+<rdar://problem/4720713> uDNS: Merge unicast active question list with multicast list.
+
+Revision 1.553 2006/10/05 03:42:43 herscher
+Remove embedded uDNS_info struct in DNSQuestion_struct
+
+Revision 1.552 2006/09/15 21:20:15 cheshire
+Remove uDNS_info substructure from mDNS_struct
+
+Revision 1.551 2006/08/14 23:24:22 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.550 2006/07/27 17:58:34 cheshire
+Improved text of "SendQueries didn't send all its queries; will try again" debugging message
+
+Revision 1.549 2006/07/20 22:07:31 mkrochma
+<rdar://problem/4633196> Wide-area browsing is currently broken in TOT
+More fixes for uninitialized variables
+
+Revision 1.548 2006/07/20 19:30:19 mkrochma
+<rdar://problem/4633196> Wide-area browsing sometimes doesn't work in TOT
+
+Revision 1.547 2006/07/15 02:31:30 cheshire
+<rdar://problem/4630812> Suppress log messages for certain old devices with inconsistent TXT RRSet TTLs
+
+Revision 1.546 2006/07/07 01:09:09 cheshire
+<rdar://problem/4472013> Add Private DNS server functionality to dnsextd
+Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd
+
+Revision 1.545 2006/07/05 23:10:30 cheshire
+<rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
+Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
+
+Revision 1.544 2006/06/29 07:42:14 cheshire
+<rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
+
+Revision 1.543 2006/06/29 01:38:43 cheshire
+<rdar://problem/4605285> Only request unicast responses on wake from sleep and network connection
+
+Revision 1.542 2006/06/27 23:40:29 cheshire
+Fix typo in comment: mis-spelled "compile"
+
+Revision 1.541 2006/06/27 19:46:24 cheshire
+Updated comments and debugging messages
+
+Revision 1.540 2006/06/15 21:35:16 cheshire
+Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
+from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
+
+Revision 1.539 2006/06/08 23:45:46 cheshire
+Change SimultaneousProbe messages from debugf() to LogOperation()
+
+Revision 1.538 2006/03/19 17:13:06 cheshire
+<rdar://problem/4483117> Need faster purging of stale records
+Shorten kDefaultReconfirmTimeForNoAnswer to five seconds
+and reconfirm whole chain of antecedents ot once
+
+Revision 1.537 2006/03/19 02:00:07 cheshire
+<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
+
+Revision 1.536 2006/03/08 23:29:53 cheshire
+<rdar://problem/4468716> Improve "Service Renamed" log message
+
+Revision 1.535 2006/03/02 20:41:17 cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+Minor code tidying and comments to reduce the risk of similar programming errors in future
+
+Revision 1.534 2006/03/02 03:25:46 cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+Code to harmonize RRSet TTLs was inadvertently rescuing expiring records
+
+Revision 1.533 2006/02/26 00:54:41 cheshire
+Fixes to avoid code generation warning/error on FreeBSD 7
+
+*/
+
+#include "DNSCommon.h" // Defines general DNS untility routines
+#include "uDNS.h" // Defines entry points into unicast-specific routines
+
+// Disable certain benign warnings with Microsoft compilers
+#if(defined(_MSC_VER))
+ // Disable "conditional expression is constant" warning for debug macros.
+ // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
+ // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
+ #pragma warning(disable:4127)
+
+ // Disable "assignment within conditional expression".
+ // Other compilers understand the convention that if you place the assignment expression within an extra pair
+ // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
+ // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
+ // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
+ #pragma warning(disable:4706)
+#endif
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Program Constants
+#endif
+
+#define NO_HINFO 1
+
+mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0;
+
+// Any records bigger than this are considered 'large' records
+#define SmallRecordLimit 1024
+
+#define kMaxUpdateCredits 10
+#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
+
+mDNSexport const char *const mDNS_DomainTypeNames[] =
+ {
+ "b._dns-sd._udp.", // Browse
+ "db._dns-sd._udp.", // Default Browse
+ "lb._dns-sd._udp.", // Automatic Browse
+ "r._dns-sd._udp.", // Registration
+ "dr._dns-sd._udp." // Default Registration
+ };
+
+#ifdef UNICAST_DISABLED
+#define uDNS_IsActiveQuery(q, u) mDNSfalse
+#endif
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
+#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
+
+mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
+ {
+ 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 (ActiveQuestion(q))
+ {
+ mDNSs32 sendtime = q->LastQTime + q->ThisQInterval;
+
+ // Don't allow sendtime to be earlier than SuppressStdPort53Queries
+ if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && m->SuppressStdPort53Queries && (sendtime - m->SuppressStdPort53Queries < 0))
+ sendtime = m->SuppressStdPort53Queries;
+
+ if (m->NextScheduledQuery - sendtime > 0)
+ m->NextScheduledQuery = sendtime;
+ }
+ }
+
+mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
+ {
+ CacheGroup *cg;
+ for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
+ if (cg->namehash == namehash && SameDomainName(cg->name, name))
+ break;
+ return(cg);
+ }
+
+mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
+ {
+ return(CacheGroupForName(m, slot, rr->namehash, rr->name));
+ }
+
+mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
+ {
+ NetworkInterfaceInfo *intf;
+
+ if (addr->type == mDNSAddrType_IPv4)
+ {
+ // Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception
+ if (mDNSv4AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue);
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
+ if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0)
+ return(mDNStrue);
+ }
+
+ if (addr->type == mDNSAddrType_IPv6)
+ {
+ if (mDNSv6AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue);
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
+ if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) &&
+ (((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) &&
+ (((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) &&
+ (((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0))
+ return(mDNStrue);
+ }
+
+ return(mDNSfalse);
+ }
+
+// For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
+// Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
+mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, QC_result AddRecord)
+ {
+ // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
+ if (AddRecord) rr->LocalAnswer = mDNStrue;
+ mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
+ if (q->QuestionCallback && !q->NoAnswer)
+ q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+ mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
+ }
+
+// When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers
+// to each, stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
+// If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
+// Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
+mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+ {
+ if (m->CurrentQuestion)
+ LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+ m->CurrentQuestion = m->LocalOnlyQuestions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
+ }
+
+ // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ {
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
+ }
+ }
+
+ m->CurrentQuestion = mDNSNULL;
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Resource Record Utility Functions
+#endif
+
+#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
+
+#define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
+ ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
+ ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
+ ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
+
+#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
+ (ResourceRecordIsValidAnswer(RR) && \
+ ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
+
+#define DefaultProbeCountForTypeUnique ((mDNSu8)3)
+#define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
+
+#define InitialAnnounceCount ((mDNSu8)8)
+
+// Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
+// This means that because the announce interval is doubled after sending the first packet, the first
+// observed on-the-wire inter-packet interval between announcements is actually one second.
+// The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
+#define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
+#define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
+#define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
+
+#define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
+ (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
+ (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
+
+#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
+#define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
+#define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
+#define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
+
+#define MaxUnansweredQueries 4
+
+// SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
+// (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
+// TTL and rdata may differ.
+// This is used for cache flush management:
+// When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
+// When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
+
+mDNSlocal mDNSBool SameResourceRecordSignature(const AuthRecord *const r1, const AuthRecord *const r2)
+ {
+ if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
+ if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
+ if (r1->resrec.InterfaceID &&
+ r2->resrec.InterfaceID &&
+ r1->resrec.InterfaceID != r2->resrec.InterfaceID) return(mDNSfalse);
+ return(mDNSBool)(
+ r1->resrec.rrtype == r2->resrec.rrtype &&
+ r1->resrec.rrclass == r2->resrec.rrclass &&
+ r1->resrec.namehash == r2->resrec.namehash &&
+ SameDomainName(r1->resrec.name, r2->resrec.name));
+ }
+
+// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
+// authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
+// complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
+// In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
+// so a response of any type should match, even if it is not actually the type the client plans to use.
+mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
+ {
+ if (!pktrr) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); }
+ if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); }
+ if (pktrr->resrec.InterfaceID &&
+ authrr->resrec.InterfaceID &&
+ pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse);
+ if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) && pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse);
+ return(mDNSBool)(
+ pktrr->resrec.rrclass == authrr->resrec.rrclass &&
+ pktrr->resrec.namehash == authrr->resrec.namehash &&
+ SameDomainName(pktrr->resrec.name, authrr->resrec.name));
+ }
+
+// IdenticalResourceRecord returns true if two resources records have
+// the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
+
+// IdenticalSameNameRecord is the same, except it skips the expensive SameDomainName() check,
+// which is at its most expensive and least useful in cases where we know in advance that the names match
+
+mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
+ {
+ if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); }
+ if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); }
+ if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name))
+ return(mDNSfalse);
+ return(SameRData(r1, r2));
+ }
+
+mDNSlocal mDNSBool IdenticalSameNameRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
+ {
+ if (!r1) { LogMsg("IdenticalSameNameRecord ERROR: r1 is NULL"); return(mDNSfalse); }
+ if (!r2) { LogMsg("IdenticalSameNameRecord ERROR: r2 is NULL"); return(mDNSfalse); }
+ if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass)
+ return(mDNSfalse);
+
+#if VerifySameNameAssumptions
+ if (r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name))
+ {
+ LogMsg("Bogus IdenticalSameNameRecord call: %##s does not match %##s", r1->name->c, r1->name->c);
+ return(mDNSfalse);
+ }
+#endif
+
+ return(SameRData(r1, r2));
+ }
+
+// CacheRecord *ks is the CacheRecord from the known answer list in the query.
+// This is the information that the requester believes to be correct.
+// AuthRecord *rr is the answer we are proposing to give, if not suppressed.
+// This is the information that we believe to be correct.
+// We've already determined that we plan to give this answer on this interface
+// (either the record is non-specific, or it is specific to this interface)
+// so now we just need to check the name, type, class, rdata and TTL.
+mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr)
+ {
+ // If RR signature is different, or data is different, then don't suppress our answer
+ if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse);
+
+ // If the requester's indicated TTL is less than half the real TTL,
+ // we need to give our answer before the requester's copy expires.
+ // If the requester's indicated TTL is at least half the real TTL,
+ // then we can suppress our answer this time.
+ // If the requester's indicated TTL is greater than the TTL we believe,
+ // then that's okay, and we don't need to do anything about it.
+ // (If two responders on the network are offering the same information,
+ // that's okay, and if they are offering the information with different TTLs,
+ // the one offering the lower TTL should defer to the one offering the higher TTL.)
+ return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2);
+ }
+
+mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+ {
+ //LogMsg("ProbeCount %d Next %ld %s",
+ // rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
+ if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+ m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
+ }
+ else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr))
+ {
+ if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+ m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
+ }
+ }
+
+mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
+ {
+ // To allow us to aggregate probes when a group of services are registered together,
+ // the first probe is delayed 1/4 second. This means the common-case behaviour is:
+ // 1/4 second wait; probe
+ // 1/4 second wait; probe
+ // 1/4 second wait; probe
+ // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
+
+ // If we have no probe suppression time set, or it is in the past, set it now
+ if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0)
+ {
+ m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique);
+ // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
+ if (m->SuppressProbes - m->NextScheduledProbe >= 0)
+ m->SuppressProbes = m->NextScheduledProbe;
+ // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
+ if (m->SuppressProbes - m->NextScheduledQuery >= 0)
+ m->SuppressProbes = m->NextScheduledQuery;
+ }
+
+ rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval;
+ // Set LastMCTime to now, to inhibit multicast responses
+ // (no need to send additional multicast responses when we're announcing anyway)
+ rr->LastMCTime = m->timenow;
+ rr->LastMCInterface = mDNSInterfaceMark;
+
+ // If this is a record type that's not going to probe, then delay its first announcement so that
+ // it will go out synchronized with the first announcement for the other records that *are* probing.
+ // This is a minor performance tweak that helps keep groups of related records synchronized together.
+ // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
+ // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
+ // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
+ // because they will meet the criterion of being at least half-way to their scheduled announcement time.
+ if (rr->resrec.RecordType != kDNSRecordTypeUnique)
+ rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
+
+ SetNextAnnounceProbeTime(m, rr);
+ }
+
+// Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname
+// Eventually we should unify this with GetServiceTarget() in uDNS.c
+mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
+ {
+ domainname *target = GetRRDomainNameTarget(&rr->resrec);
+
+ if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype);
+
+ if (target && SameDomainName(target, &m->MulticastHostname))
+ debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
+
+ if (target && !SameDomainName(target, &m->MulticastHostname))
+ {
+ AssignDomainName(target, &m->MulticastHostname);
+ SetNewRData(&rr->resrec, mDNSNULL, 0); // Update rdlength, rdestimate, rdatahash
+
+ // If we're in the middle of probing this record, we need to start again,
+ // because changing its rdata may change the outcome of the tie-breaker.
+ // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+
+ // If we've announced this record, we really should send a goodbye packet for the old rdata before
+ // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
+ // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
+ if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared)
+ debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+
+ rr->AnnounceCount = InitialAnnounceCount;
+ rr->RequireGoodbye = mDNSfalse;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m,rr);
+ }
+ }
+
+mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
+ {
+ if (rr->RecordCallback)
+ {
+ // 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.
+ rr->Acknowledged = mDNStrue;
+ mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
+ rr->RecordCallback(m, rr, mStatus_NoError);
+ mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
+ }
+ }
+
+// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
+#define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \
+ ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
+#define RecordIsLocalDuplicate(A,B) \
+ ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
+
+// Exported so uDNS.c can call this
+mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
+ {
+ domainname *target = GetRRDomainNameTarget(&rr->resrec);
+ AuthRecord *r;
+ AuthRecord **p = &m->ResourceRecords;
+ AuthRecord **d = &m->DuplicateRecords;
+
+ if ((mDNSs32)rr->resrec.rroriginalttl <= 0)
+ { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
+
+ if (!rr->resrec.RecordType)
+ { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
+
+ while (*p && *p != rr) p=&(*p)->next;
+ while (*d && *d != rr) d=&(*d)->next;
+ if (*d || *p)
+ {
+ LogMsg("Error! Tried to register AuthRecord %p %##s (%s) that's already in the list",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ return(mStatus_AlreadyRegistered);
+ }
+
+ if (rr->DependentOn)
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+ rr->resrec.RecordType = kDNSRecordTypeVerified;
+ else
+ {
+ LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ return(mStatus_Invalid);
+ }
+ if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
+ {
+ LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
+ return(mStatus_Invalid);
+ }
+ }
+
+ // If this resource record is referencing a specific interface, make sure it exists
+ if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
+ {
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == rr->resrec.InterfaceID) break;
+ if (!intf)
+ {
+ debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID);
+ return(mStatus_BadReferenceErr);
+ }
+ }
+
+ rr->next = mDNSNULL;
+
+ // Field Group 1: The actual information pertaining to this resource record
+ // Set up by client prior to call
+
+ // Field Group 2: Persistent metadata for Authoritative Records
+// rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
+// rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
+// rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
+// rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
+// rr->Callback = already set in mDNS_SetupResourceRecord
+// rr->Context = already set in mDNS_SetupResourceRecord
+// rr->RecordType = already set in mDNS_SetupResourceRecord
+// rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
+// rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
+ // Make sure target is not uninitialized data, or we may crash writing debugging log messages
+ if (rr->AutoTarget && target) target->c[0] = 0;
+
+ // Field Group 3: Transient state for Authoritative Records
+ rr->Acknowledged = mDNSfalse;
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+ rr->AnnounceCount = InitialAnnounceCount;
+ rr->RequireGoodbye = mDNSfalse;
+ rr->LocalAnswer = mDNSfalse;
+ rr->IncludeInProbe = mDNSfalse;
+ rr->ImmedAnswer = mDNSNULL;
+ rr->ImmedUnicast = mDNSfalse;
+ rr->ImmedAdditional = mDNSNULL;
+ rr->SendRNow = mDNSNULL;
+ rr->v4Requester = zerov4Addr;
+ rr->v6Requester = zerov6Addr;
+ rr->NextResponse = mDNSNULL;
+ rr->NR_AnswerTo = mDNSNULL;
+ rr->NR_AdditionalTo = mDNSNULL;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ if (!rr->AutoTarget) InitializeLastAPTime(m, rr);
+// rr->LastAPTime = Set for us in InitializeLastAPTime()
+// rr->LastMCTime = Set for us in InitializeLastAPTime()
+// rr->LastMCInterface = Set for us in InitializeLastAPTime()
+ rr->NewRData = mDNSNULL;
+ rr->newrdlength = 0;
+ rr->UpdateCallback = mDNSNULL;
+ rr->UpdateCredits = kMaxUpdateCredits;
+ rr->NextUpdateCredit = 0;
+ rr->UpdateBlocked = 0;
+
+ // Field Group 4: Transient uDNS state for Authoritative Records
+ rr->state = regState_Zero;
+ rr->uselease = 0;
+ rr->expire = 0;
+ rr->Private = 0;
+ rr->id = zeroID;
+ rr->zone.c[0] = 0;
+ rr->UpdateServer = zeroAddr;
+ rr->UpdatePort = zeroIPPort;
+ rr->nta = mDNSNULL;
+ rr->tcp = mDNSNULL;
+ rr->OrigRData = 0;
+ rr->OrigRDLen = 0;
+ rr->InFlightRData = 0;
+ rr->InFlightRDLen = 0;
+ rr->QueuedRData = 0;
+ rr->QueuedRDLen = 0;
+
+// rr->resrec.interface = already set in mDNS_SetupResourceRecord
+// rr->resrec.name->c = MUST be set by client
+// rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
+// rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
+// rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
+// rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
+
+ if (rr->AutoTarget)
+ SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
+ else
+ {
+ rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse);
+ rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
+ }
+
+ if (!ValidateDomainName(rr->resrec.name))
+ { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
+
+ // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
+ // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
+ // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
+ if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; }
+
+ // Don't do this until *after* we've set rr->resrec.rdlength
+ if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata))
+ { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
+
+ rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+ rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
+
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ {
+ // If this is supposed to be unique, make sure we don't have any name conflicts
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+ {
+ const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
+ for (r = m->ResourceRecords; r; r=r->next)
+ {
+ const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
+ if (s1 != s2 && SameResourceRecordSignature(r, rr) && !SameRData(&r->resrec, &rr->resrec))
+ break;
+ }
+ if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
+ {
+ debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+ rr->resrec.rroriginalttl = 0;
+ rr->ImmedAnswer = mDNSInterfaceMark;
+ m->NextScheduledResponse = m->timenow;
+ }
+ }
+ }
+
+ // Now that we've finished building our new record, make sure it's not identical to one we already have
+ for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break;
+
+ if (r)
+ {
+ debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr));
+ *d = rr;
+ // If the previous copy of this record is already verified unique,
+ // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
+ // Setting ProbeCount to zero will cause SendQueries() to advance this record to
+ // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified)
+ rr->ProbeCount = 0;
+ }
+ else
+ {
+ debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr));
+ if (!m->NewLocalRecords) m->NewLocalRecords = rr;
+ *p = rr;
+ }
+
+ if (rr->resrec.InterfaceID != mDNSInterface_Any || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
+ {
+ // For records that are not going to probe, acknowledge them right away
+ if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
+ AcknowledgeRecord(m, rr);
+ }
+#ifndef UNICAST_DISABLED
+ else
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
+ rr->ProbeCount = 0;
+ 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;
+ }
+#endif
+
+ return(mStatus_NoError);
+ }
+
+mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
+ {
+ m->ProbeFailTime = m->timenow;
+ m->NumFailedProbes++;
+ // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
+ // If a bunch of hosts have all been configured with the same name, then they'll all
+ // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
+ // up to name-10. After that they'll start adding random increments in the range 1-100,
+ // so they're more likely to branch out in the available namespace and settle on a set of
+ // unique names quickly. If after five more tries the host is still conflicting, then we
+ // may have a serious problem, so we start rate-limiting so we don't melt down the network.
+ if (m->NumFailedProbes >= 15)
+ {
+ m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
+ LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
+ m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ }
+ }
+
+mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr)
+ {
+ RData *OldRData = rr->resrec.rdata;
+ SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength); // Update our rdata
+ rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
+ if (rr->UpdateCallback)
+ rr->UpdateCallback(m, rr, OldRData); // ... and let the client know
+ }
+
+// NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+// Exported so uDNS.c can call this
+mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
+ {
+ AuthRecord *r2;
+ mDNSu8 RecordType = rr->resrec.RecordType;
+ AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records
+
+ while (*p && *p != rr) p=&(*p)->next;
+
+ if (*p)
+ {
+ // We found our record on the main list. See if there are any duplicates that need special handling.
+ if (drt == mDNS_Dereg_conflict) // If this was a conflict, see that all duplicates get the same treatment
+ {
+ // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
+ // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
+ for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF;
+ }
+ else
+ {
+ // Before we delete the record (and potentially send a goodbye packet)
+ // first see if we have a record on the duplicate list ready to take over from it.
+ AuthRecord **d = &m->DuplicateRecords;
+ while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next;
+ if (*d)
+ {
+ AuthRecord *dup = *d;
+ debugf("Duplicate record %p taking over from %p %##s (%s)",
+ dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ *d = dup->next; // Cut replacement record from DuplicateRecords list
+ dup->next = rr->next; // And then...
+ rr->next = dup; // ... splice it in right after the record we're about to delete
+ dup->resrec.RecordType = rr->resrec.RecordType;
+ dup->ProbeCount = rr->ProbeCount;
+ dup->AnnounceCount = rr->AnnounceCount;
+ dup->RequireGoodbye = rr->RequireGoodbye;
+ dup->ImmedAnswer = rr->ImmedAnswer;
+ dup->ImmedUnicast = rr->ImmedUnicast;
+ dup->ImmedAdditional = rr->ImmedAdditional;
+ dup->v4Requester = rr->v4Requester;
+ dup->v6Requester = rr->v6Requester;
+ dup->ThisAPInterval = rr->ThisAPInterval;
+ dup->LastAPTime = rr->LastAPTime;
+ dup->LastMCTime = rr->LastMCTime;
+ dup->LastMCInterface = rr->LastMCInterface;
+ rr->RequireGoodbye = mDNSfalse;
+ }
+ }
+ }
+ else
+ {
+ // We didn't find our record on the main list; try the DuplicateRecords list instead.
+ p = &m->DuplicateRecords;
+ while (*p && *p != rr) p=&(*p)->next;
+ // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
+ if (*p) rr->RequireGoodbye = mDNSfalse;
+ if (*p) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ }
+
+ if (!*p)
+ {
+ // No need to log an error message if we already know this is a potentially repeated deregistration
+ if (drt != mDNS_Dereg_repeat)
+ LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ return(mStatus_BadReferenceErr);
+ }
+
+ // If this is a shared record and we've announced it at least once,
+ // we need to retract that announcement before we delete the record
+
+ // If this is a record (including mDNSInterface_LocalOnly records) for which we've given local answers then
+ // it's tempting to just do "AnswerLocalQuestions(m, rr, mDNSfalse)" here, but that would not not be safe.
+ // The AnswerLocalQuestions routine walks the question list invoking client callbacks, using the "m->CurrentQuestion"
+ // mechanism to cope with the client callback modifying the question list while that's happening.
+ // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain)
+ // which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice.
+ // More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other
+ // records, thereby invoking yet more callbacks, without limit.
+ // The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending
+ // 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);
+ }
+#endif UNICAST_DISABLED
+
+ if (RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->LocalAnswer))
+ {
+ verbosedebugf("mDNS_Deregister_internal: Sending deregister for %s", ARDisplayString(m, rr));
+ rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+ rr->resrec.rroriginalttl = 0;
+ rr->ImmedAnswer = mDNSInterfaceMark;
+ if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
+ m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
+ }
+ else