1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * This code is completely 100% portable C. It does not depend on any external header files
18 * from outside the mDNS project -- all the types it expects to find are defined right here.
20 * The previous point is very important: This file does not depend on any external
21 * header files. It should compile on *any* platform that has a C compiler, without
22 * making *any* assumptions about availability of so-called "standard" C functions,
23 * routines, or types (which may or may not be present on any given platform).
26 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
27 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
28 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
29 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
30 * therefore common sense dictates that if they are part of a compound statement then they
31 * should be indented to the same level as everything else in that compound statement.
32 * Indenting curly braces at the same level as the "if" implies that curly braces are
33 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
34 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
35 * understand why variable y is not of type "char*" just proves the point that poor code
36 * layout leads people to unfortunate misunderstandings about how the C language really works.)
38 Change History (most recent first):
41 Revision 1.729 2007/10/05 17:56:10 cheshire
42 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
44 Revision 1.728 2007/10/04 23:18:14 cheshire
45 <rdar://problem/5523706> mDNSResponder flooding DNS servers with unreasonable query level
47 Revision 1.727 2007/10/04 22:51:57 cheshire
48 Added debugging LogOperation message to show when we're sending cache expiration queries
50 Revision 1.726 2007/10/03 00:14:24 cheshire
51 Removed write to null to generate stack trace for SetNextQueryTime locking failure
53 Revision 1.725 2007/10/02 21:11:08 cheshire
54 <rdar://problem/5518270> LLQ refreshes don't work, which breaks BTMM browsing
56 Revision 1.724 2007/10/02 20:10:23 cheshire
57 Additional debugging checks on shutdown -- list all records we didn't send a goodbye for, not just the first one
59 Revision 1.723 2007/10/02 19:56:54 cheshire
60 <rdar://problem/5518310> Double-dispose causes crash changing Dynamic DNS hostname
62 Revision 1.722 2007/10/01 22:59:46 cheshire
63 <rdar://problem/5516303> mDNSResponder did not shut down after 20 seconds
64 Need to shut down NATTraversals on exit
66 Revision 1.721 2007/10/01 18:42:07 cheshire
67 To make packet logs appear in a more intuitive order, dump received packets *before* handling them, not after
69 Revision 1.720 2007/09/29 20:40:19 cheshire
70 <rdar://problem/5513378> Crash in ReissueBlockedQuestions
72 Revision 1.719 2007/09/27 22:23:56 cheshire
73 <rdar://problem/4947392> uDNS: Use SOA to determine TTL for negative answers
74 Need to clear m->rec.r.resrec.RecordType after we've finished using m->rec
76 Revision 1.718 2007/09/27 22:02:33 cheshire
77 <rdar://problem/5464941> BTMM: Registered records in BTMM don't get removed from server after calling RemoveRecord
79 Revision 1.717 2007/09/27 21:21:39 cheshire
80 Export CompleteDeregistration so it's callable from other files
82 Revision 1.716 2007/09/27 02:12:21 cheshire
83 Updated GrantCacheExtensions degugging message to show new record lifetime
85 Revision 1.715 2007/09/27 01:20:06 cheshire
86 <rdar://problem/5500077> BTMM: Need to refresh LLQs based on lease life and not TTL of response
88 Revision 1.714 2007/09/27 00:37:01 cheshire
89 <rdar://problem/4947392> BTMM: Use SOA to determine TTL for negative answers
91 Revision 1.713 2007/09/27 00:25:39 cheshire
92 Added ttl_seconds parameter to MakeNegativeCacheRecord in preparation for:
93 <rdar://problem/4947392> uDNS: Use SOA to determine TTL for negative answers
95 Revision 1.712 2007/09/26 23:16:58 cheshire
96 <rdar://problem/5496399> BTMM: Leopard sending excessive LLQ registration requests to .Mac
98 Revision 1.711 2007/09/26 22:06:02 cheshire
99 <rdar://problem/5507399> BTMM: No immediate failure notifications for BTMM names
101 Revision 1.710 2007/09/26 00:49:46 cheshire
102 Improve packet logging to show sent and received packets,
103 transport protocol (UDP/TCP/TLS) and source/destination address:port
105 Revision 1.709 2007/09/21 21:12:36 cheshire
106 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
108 Revision 1.708 2007/09/20 23:13:37 cheshire
109 <rdar://problem/4038277> BTMM: Not getting LLQ remove events when logging out of VPN or disconnecting from network
110 Additional fix: If we have no DNS servers at all, then immediately purge all unicast cache records (including for LLQs)
112 Revision 1.707 2007/09/20 02:29:37 cheshire
113 <rdar://problem/4038277> BTMM: Not getting LLQ remove events when logging out of VPN or disconnecting from network
115 Revision 1.706 2007/09/20 01:13:19 cheshire
116 Export CacheGroupForName so it's callable from other files
118 Revision 1.705 2007/09/20 01:12:06 cheshire
119 Moved HashSlot(X) from mDNS.c to DNSCommon.h so it's usable in other files
121 Revision 1.704 2007/09/19 22:47:25 cheshire
122 <rdar://problem/5490182> Memory corruption freeing a "no such service" service record
124 Revision 1.703 2007/09/14 01:46:59 cheshire
125 Fix Posix build (#ifdef _LEGACY_NAT_TRAVERSAL_ section included a closing curly brace it should not have)
127 Revision 1.702 2007/09/13 22:06:46 cheshire
128 <rdar://problem/5480643> Tully's Free WiFi: DNS fails
129 Need to accept DNS responses where the query ID field matches, even if the source address does not
131 Revision 1.701 2007/09/12 23:22:32 cheshire
132 <rdar://problem/5476979> Only accept NAT Port Mapping packets from our default gateway
134 Revision 1.700 2007/09/12 23:03:08 cheshire
135 <rdar://problem/5476978> DNSServiceNATPortMappingCreate callback not giving correct interface index
137 Revision 1.699 2007/09/12 22:19:28 cheshire
138 <rdar://problem/5476977> Need to listen for port 5350 NAT-PMP announcements
140 Revision 1.698 2007/09/12 22:13:27 cheshire
141 Remove DynDNSHostNames cleanly on shutdown
143 Revision 1.697 2007/09/12 01:44:47 cheshire
144 <rdar://problem/5475938> Eliminate "Correcting TTL" syslog messages for unicast DNS records
146 Revision 1.696 2007/09/12 01:26:08 cheshire
147 Initialize LastNATReplyLocalTime to timenow, so that gateway uptime checks work more reliably
149 Revision 1.695 2007/09/11 19:19:16 cheshire
150 Correct capitalization of "uPNP" to "UPnP"
152 Revision 1.694 2007/09/10 22:06:51 cheshire
153 Rename uptime => upseconds and LastNATUptime => LastNATupseconds to make it clear these time values are in seconds
155 Revision 1.693 2007/09/07 22:24:36 vazquez
156 <rdar://problem/5466301> Need to stop spewing mDNSResponderHelper logs
158 Revision 1.692 2007/09/07 00:12:09 cheshire
159 <rdar://problem/5466010> Unicast DNS changes broke efficiency fix 3928456
161 Revision 1.691 2007/09/05 22:25:01 vazquez
162 <rdar://problem/5400521> update_record mDNSResponder leak
164 Revision 1.690 2007/09/05 21:48:01 cheshire
165 <rdar://problem/5385864> BTMM: mDNSResponder flushes wide-area Bonjour records after an hour for a zone.
166 Now that we're respecting the TTL of uDNS records in the cache, the LLQ maintenance cod needs
167 to update the cache lifetimes of all relevant records every time it successfully renews an LLQ,
168 otherwise those records will expire and vanish from the cache.
170 Revision 1.689 2007/09/05 02:29:06 cheshire
171 <rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions
172 Additional fixes to code implementing "NoAnswer" logic
174 Revision 1.688 2007/08/31 22:56:39 cheshire
175 <rdar://problem/5407080> BTMM: TTLs incorrect on cached BTMM records
177 Revision 1.687 2007/08/31 19:53:14 cheshire
178 <rdar://problem/5431151> BTMM: IPv6 address lookup should not succeed if autotunnel cannot be setup
179 If AutoTunnel setup fails, the code now generates a fake NXDomain error saying that the requested AAAA record does not exist
181 Revision 1.686 2007/08/30 00:01:56 cheshire
182 Added comment about SetTargetToHostName()
184 Revision 1.685 2007/08/29 01:19:24 cheshire
185 <rdar://problem/5400181> BTMM: Tunneled services do not need NAT port mappings
186 Set AutoTarget to Target_AutoHostAndNATMAP for non-AutoTunnel wide-area services
188 Revision 1.684 2007/08/28 23:58:42 cheshire
189 Rename HostTarget -> AutoTarget
191 Revision 1.683 2007/08/28 23:53:21 cheshire
192 Rename serviceRegistrationCallback -> ServiceRegistrationZoneDataComplete
194 Revision 1.682 2007/08/27 20:28:19 cheshire
195 Improve "suspect uDNS response" log message
197 Revision 1.681 2007/08/24 23:37:23 cheshire
198 Added debugging message to show when ExtraResourceRecord callback gets invoked
200 Revision 1.680 2007/08/24 00:15:19 cheshire
201 Renamed GetAuthInfoForName() to GetAuthInfoForName_internal() to make it clear that it may only be called with the lock held
203 Revision 1.679 2007/08/23 21:47:09 vazquez
204 <rdar://problem/5427316> BTMM: mDNSResponder sends NAT-PMP packets on public network
205 make sure we clean up port mappings on base stations by sending a lease value of 0,
206 and only send NAT-PMP packets on private networks; also save some memory by
207 not using packet structs in NATTraversals.
209 Revision 1.678 2007/08/01 16:09:13 cheshire
210 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
212 Revision 1.677 2007/08/01 01:58:24 cheshire
213 Added RecordType sanity check in mDNS_Register_internal
215 Revision 1.676 2007/08/01 00:04:13 cheshire
216 <rdar://problem/5261696> Crash in tcpKQSocketCallback
217 Half-open TCP connections were not being cancelled properly
219 Revision 1.675 2007/07/31 02:28:35 vazquez
220 <rdar://problem/3734269> NAT-PMP: Detect public IP address changes and base station reboot
222 Revision 1.674 2007/07/31 01:57:23 cheshire
223 Adding code to respect TTL received in uDNS responses turned out to
224 expose other problems; backing out change for now.
226 Revision 1.673 2007/07/30 23:31:26 cheshire
227 Code for respecting TTL received in uDNS responses should exclude LLQ-type responses
229 Revision 1.672 2007/07/28 01:25:56 cheshire
230 <rdar://problem/4780038> BTMM: Add explicit UDP event port to LLQ setup request, to fix LLQs not working behind NAT
232 Revision 1.671 2007/07/27 22:32:54 cheshire
233 When processing TTLs in uDNS responses, we'll currently impose a minimum effective TTL
234 of 2 seconds, or other stuff breaks (e.g. we end up making a negative cache entry).
236 Revision 1.670 2007/07/27 20:54:43 cheshire
237 Fixed code to respect real record TTL received in uDNS responses
239 Revision 1.669 2007/07/27 20:09:32 cheshire
240 Don't need to dump out all received mDNS packets; they're easily viewed using mDNSNetMonitor
242 Revision 1.668 2007/07/27 19:58:47 cheshire
243 Use symbolic names QC_add and QC_rmv instead of mDNStrue/mDNSfalse
245 Revision 1.667 2007/07/27 19:52:10 cheshire
246 Don't increment m->rrcache_active for no-cache add events
248 Revision 1.666 2007/07/27 19:30:39 cheshire
249 Changed mDNSQuestionCallback parameter from mDNSBool to QC_result,
250 to properly reflect tri-state nature of the possible responses
252 Revision 1.665 2007/07/27 18:44:01 cheshire
253 Rename "AnswerQuestionWithResourceRecord" to more informative "AnswerCurrentQuestionWithResourceRecord"
255 Revision 1.664 2007/07/27 18:38:56 cheshire
256 Rename "uDNS_CheckQuery" to more informative "uDNS_CheckCurrentQuestion"
258 Revision 1.663 2007/07/25 03:05:02 vazquez
260 <rdar://problem/5338913> LegacyNATTraversal: UPnP heap overflow
261 <rdar://problem/5338933> LegacyNATTraversal: UPnP stack buffer overflow
262 and a myriad of other security problems
264 Revision 1.662 2007/07/24 20:22:46 cheshire
265 Make sure all fields of main mDNS object are initialized correctly
267 Revision 1.661 2007/07/21 00:54:45 cheshire
268 <rdar://problem/5344576> Delay IPv6 address callback until AutoTunnel route and policy is configured
270 Revision 1.660 2007/07/20 20:00:45 cheshire
271 "Legacy Browse" is better called "Automatic Browse"
273 Revision 1.659 2007/07/20 00:54:18 cheshire
274 <rdar://problem/4641118> Need separate SCPreferences for per-user .Mac settings
276 Revision 1.658 2007/07/18 02:28:57 cheshire
277 Don't set AutoTunnel settings in uDNS_RegisterService; should be done in GetServiceTarget
279 Revision 1.657 2007/07/18 00:57:10 cheshire
280 <rdar://problem/5303834> Automatically configure IPSec policy when resolving services
281 Only need to call AddNewClientTunnel() for IPv6 addresses
283 Revision 1.656 2007/07/16 23:54:48 cheshire
284 <rdar://problem/5338850> Crash when removing or changing DNS keys
286 Revision 1.655 2007/07/16 20:11:37 vazquez
287 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
288 Init LNT stuff and handle SSDP packets
290 Revision 1.654 2007/07/12 23:30:23 cheshire
291 Changed some 'LogOperation' calls to 'debugf' to reduce verbosity in syslog
293 Revision 1.653 2007/07/12 02:51:27 cheshire
294 <rdar://problem/5303834> Automatically configure IPSec policy when resolving services
296 Revision 1.652 2007/07/11 23:43:42 cheshire
297 Rename PurgeCacheResourceRecord to mDNS_PurgeCacheResourceRecord
299 Revision 1.651 2007/07/11 22:44:40 cheshire
300 <rdar://problem/5328801> SIGHUP should purge the cache
302 Revision 1.650 2007/07/11 21:34:09 cheshire
303 <rdar://problem/5304766> Register IPSec tunnel with IPv4-only hostname and create NAT port mappings
304 Need to hold mDNS_Lock when calling mDNS_AddDynDNSHostName/mDNS_RemoveDynDNSHostName
306 Revision 1.649 2007/07/11 02:52:52 cheshire
307 <rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services
308 In uDNS_RegisterService, set HostTarget for AutoTunnel services
310 Revision 1.648 2007/07/09 23:48:12 cheshire
311 Add parentheses around bitwise operation for clarity
313 Revision 1.647 2007/07/06 21:17:55 cheshire
314 Initialize m->retryGetAddr to timenow + 0x78000000;
316 Revision 1.646 2007/07/06 18:55:49 cheshire
317 Initialize m->NextScheduledNATOp
319 Revision 1.645 2007/06/29 22:55:54 cheshire
320 Move declaration of DNSServer *s; Fixed incomplete comment.
322 Revision 1.644 2007/06/29 00:07:29 vazquez
323 <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes)
325 Revision 1.643 2007/06/20 01:10:12 cheshire
326 <rdar://problem/5280520> Sync iPhone changes into main mDNSResponder code
328 Revision 1.642 2007/06/15 21:54:50 cheshire
329 <rdar://problem/4883206> Add packet logging to help debugging private browsing over TLS
331 Revision 1.641 2007/05/25 00:30:24 cheshire
332 When checking for duplicate questions, make sure privacy (or not) status, and long-lived (or not)
333 status matches. This is particularly important when doing a private query for an SOA record,
334 which will result in a call StartGetZoneData which does a non-private query for the same SOA record.
335 If the latter is tagged as a duplicate of the former, then we have deadlock, and neither will complete.
337 Revision 1.640 2007/05/25 00:25:44 cheshire
338 <rdar://problem/5227737> Need to enhance putRData to output all current known types
340 Revision 1.639 2007/05/23 00:51:33 cheshire
341 Increase threshold for shedding cache records from 512 to 3000. The 512 figure was calculated when
342 each cache entry took about 700 bytes; now they're only 164 bytes. Also, machines have more RAM these
343 days, and there are more services being advertised using DNS-SD, so it makes sense to cache more.
345 Revision 1.638 2007/05/23 00:43:16 cheshire
346 If uDNS UDP response has TC (truncated) bit set, don't interpret it as being the entire RRSet
348 Revision 1.637 2007/05/14 23:53:00 cheshire
349 Export mDNS_StartQuery_internal and mDNS_StopQuery_internal so they can be called from uDNS.c
351 Revision 1.636 2007/05/10 23:27:15 cheshire
352 Update mDNS_Deregister_internal debugging messages
354 Revision 1.635 2007/05/07 20:43:45 cheshire
355 <rdar://problem/4241419> Reduce the number of queries and announcements
357 Revision 1.634 2007/05/04 22:09:08 cheshire
358 Only do "restarting exponential backoff sequence" for mDNS questions
359 In mDNS_RegisterInterface, only retrigger mDNS questions
360 In uDNS_SetupDNSConfig, use ActivateUnicastQuery() instead of just setting q->ThisQInterval directly
362 Revision 1.633 2007/05/04 21:45:12 cheshire
363 Get rid of unused q->RestartTime; Get rid of uDNS_Close (synonym for uDNS_Sleep)
365 Revision 1.632 2007/05/04 20:20:50 cheshire
366 <rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData
367 Need to set srs->nta = mDNSNULL; when regState_NoTarget
369 Revision 1.631 2007/05/04 00:39:42 cheshire
370 <rdar://problem/4410011> Eliminate looping SOA lookups
371 When creating a cascade of negative SOA cache entries, CacheGroup pointer cg needs to be updated
372 each time round the loop to reference the right CacheGroup for each newly fabricated SOA name
374 Revision 1.630 2007/05/03 22:40:38 cheshire
375 <rdar://problem/4669229> mDNSResponder ignores bogus null target in SRV record
377 Revision 1.629 2007/05/03 00:15:51 cheshire
378 <rdar://problem/4410011> Eliminate looping SOA lookups
380 Revision 1.628 2007/05/02 22:21:33 cheshire
381 <rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData
383 Revision 1.627 2007/04/30 19:29:13 cheshire
384 Fix display of port number in "Updating DNS Server" message
386 Revision 1.626 2007/04/30 04:21:13 cheshire
387 Can't safely call AnswerLocalQuestions() from within mDNS_Deregister() -- need to defer it until mDNS_Execute time
389 Revision 1.625 2007/04/28 01:34:21 cheshire
390 Fixed crashing bug: We need to update rr->CRActiveQuestion pointers for *all* questions
391 (Code was explicitly ignoring wide-area unicast questions, leading to stale pointers and crashes)
393 Revision 1.624 2007/04/27 21:04:30 cheshire
394 On network configuration change, need to call uDNS_RegisterSearchDomains
396 Revision 1.623 2007/04/27 19:28:01 cheshire
397 Any code that calls StartGetZoneData needs to keep a handle to the structure, so
398 it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
399 -- it would start a query and then quickly cancel it, and then when
400 StartGetZoneData completed, it had a dangling pointer and crashed.)
402 Revision 1.622 2007/04/26 16:09:22 cheshire
403 mDNS_StopQueryWithRemoves should ignore kDNSRecordTypePacketNegative records
405 Revision 1.621 2007/04/26 15:43:22 cheshire
406 Make sure DNSServer *s is non-null before using value in LogOperation
408 Revision 1.620 2007/04/26 13:11:05 cheshire
409 Fixed crash when logging out of VPN
411 Revision 1.619 2007/04/26 00:35:15 cheshire
412 <rdar://problem/5140339> uDNS: Domain discovery not working over VPN
413 Fixes to make sure results update correctly when connectivity changes (e.g. a DNS server
414 inside the firewall may give answers where a public one gives none, and vice versa.)
416 Revision 1.618 2007/04/25 19:26:01 cheshire
417 m->NextScheduledQuery was getting set too early in SendQueries()
418 Improved "SendQueries didn't send all its queries" debugging message
420 Revision 1.617 2007/04/25 17:48:22 cheshire
421 Update debugging message
423 Revision 1.616 2007/04/25 16:38:32 cheshire
424 If negative cache entry already exists, reactivate it instead of creating a new one
426 Revision 1.615 2007/04/25 02:14:38 cheshire
427 <rdar://problem/4246187> uDNS: Identical client queries should reference a single shared core query
428 Additional fixes to make LLQs work properly
430 Revision 1.614 2007/04/23 21:52:45 cheshire
431 <rdar://problem/5094009> IPv6 filtering in AirPort base station breaks Wide-Area Bonjour
433 Revision 1.613 2007/04/23 04:58:20 cheshire
434 <rdar://problem/5072548> Crash when setting extremely large TXT records
436 Revision 1.612 2007/04/22 20:39:38 cheshire
437 <rdar://problem/4633194> Add 20 to 120ms random delay to browses
439 Revision 1.611 2007/04/22 18:16:29 cheshire
440 Removed incorrect ActiveQuestion(q) check that was preventing suspended questions from getting reactivated
442 Revision 1.610 2007/04/22 06:02:02 cheshire
443 <rdar://problem/4615977> Query should immediately return failure when no server
445 Revision 1.609 2007/04/20 21:17:24 cheshire
446 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
448 Revision 1.608 2007/04/20 19:45:31 cheshire
449 In LogAllOperations mode, dump out unknown DNS packets in their entirety
451 Revision 1.607 2007/04/19 23:56:25 cheshire
452 Don't do cache-flush processing for LLQ answers
454 Revision 1.606 2007/04/19 22:50:53 cheshire
455 <rdar://problem/4246187> Identical client queries should reference a single shared core query
457 Revision 1.605 2007/04/19 20:06:41 cheshire
458 Rename field 'Private' (sounds like a boolean) to more informative 'AuthInfo' (it's a DomainAuthInfo pointer)
460 Revision 1.604 2007/04/19 18:03:04 cheshire
461 Add "const" declaration
463 Revision 1.603 2007/04/06 21:00:25 cheshire
466 Revision 1.602 2007/04/05 22:55:35 cheshire
467 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
469 Revision 1.601 2007/04/04 21:48:52 cheshire
470 <rdar://problem/4720694> Combine unicast authoritative answer list with multicast list
472 Revision 1.600 2007/04/04 01:31:33 cheshire
473 Improve debugging message
475 Revision 1.599 2007/04/04 00:03:26 cheshire
476 <rdar://problem/5089862> DNSServiceQueryRecord is returning kDNSServiceErr_NoSuchRecord for empty rdata
478 Revision 1.598 2007/04/03 19:43:16 cheshire
479 Use mDNSSameIPPort (and similar) instead of accessing internal fields directly
481 Revision 1.597 2007/03/31 00:32:32 cheshire
482 After skipping OPT and TSIG, clear m->rec.r.resrec.RecordType
484 Revision 1.596 2007/03/28 20:59:26 cheshire
485 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
487 Revision 1.595 2007/03/26 23:48:16 cheshire
488 <rdar://problem/4848295> Advertise model information via Bonjour
489 Refinements to reduce unnecessary transmissions of the DeviceInfo TXT record
491 Revision 1.594 2007/03/26 23:05:05 cheshire
492 <rdar://problem/5089257> Don't cache TSIG records
494 Revision 1.593 2007/03/23 17:40:08 cheshire
495 <rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision
497 Revision 1.592 2007/03/22 18:31:48 cheshire
498 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
500 Revision 1.591 2007/03/22 00:49:19 cheshire
501 <rdar://problem/4848295> Advertise model information via Bonjour
503 Revision 1.590 2007/03/21 23:05:59 cheshire
504 Rename uDNS_HostnameInfo to HostnameInfo; deleted some unused fields
506 Revision 1.589 2007/03/20 15:37:19 cheshire
507 Delete unnecessary log message
509 Revision 1.588 2007/03/20 00:24:44 cheshire
510 <rdar://problem/4175213> Should deliver "name registered" callback slightly *before* announcing PTR record
512 Revision 1.587 2007/03/16 22:10:56 cheshire
513 <rdar://problem/4471307> mDNS: Query for *either* type A or AAAA should return both types
515 Revision 1.586 2007/03/10 03:26:44 cheshire
516 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
518 Revision 1.585 2007/03/10 02:02:58 cheshire
519 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
520 Eliminate unnecessary "InternalResponseHndlr responseCallback" function pointer
522 Revision 1.584 2007/02/28 01:51:27 cheshire
523 Added comment about reverse-order IP address
525 Revision 1.583 2007/01/27 03:19:33 cheshire
526 Need to initialize question->sock
528 Revision 1.582 2007/01/25 00:40:16 cheshire
529 Unified CNAME-following functionality into cache management code (which means CNAME-following
530 should now also work for mDNS queries too); deleted defunct pktResponseHndlr() routine.
532 Revision 1.581 2007/01/23 02:56:11 cheshire
533 Store negative results in the cache, instead of generating them out of pktResponseHndlr()
535 Revision 1.580 2007/01/19 21:17:33 cheshire
536 StartLLQPolling needs to call SetNextQueryTime() to cause query to be done in a timely fashion
538 Revision 1.579 2007/01/19 18:39:10 cheshire
539 Fix a bunch of parameters that should have been declared "const"
541 Revision 1.578 2007/01/10 22:51:57 cheshire
542 <rdar://problem/4917539> Add support for one-shot private queries as well as long-lived private queries
544 Revision 1.577 2007/01/10 02:05:21 cheshire
545 Delay uDNS_SetupDNSConfig() until *after* the platform layer
546 has set up the interface list and security credentials
548 Revision 1.576 2007/01/09 02:40:57 cheshire
549 uDNS_SetupDNSConfig() shouldn't be called from mDNSMacOSX.c (platform support layer);
550 moved it to mDNS_Init() in mDNS.c (core code)
552 Revision 1.575 2007/01/09 00:17:25 cheshire
553 Improve "ERROR m->CurrentRecord already set" debugging messages
555 Revision 1.574 2007/01/05 08:30:41 cheshire
556 Trim excessive "$Log" checkin history from before 2006
557 (checkin history still available via "cvs log ..." of course)
559 Revision 1.573 2007/01/05 06:34:03 cheshire
560 Improve "ERROR m->CurrentQuestion already set" debugging messages
562 Revision 1.572 2007/01/04 23:11:11 cheshire
563 <rdar://problem/4720673> uDNS: Need to start caching unicast records
564 When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries
566 Revision 1.571 2007/01/04 21:45:20 cheshire
567 Added mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback macros,
568 to do additional lock sanity checking around callback invocations
570 Revision 1.570 2007/01/04 20:57:47 cheshire
571 Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates)
573 Revision 1.569 2007/01/04 20:27:27 cheshire
574 Change a LogMsg() to debugf()
576 Revision 1.568 2007/01/04 02:39:53 cheshire
577 <rdar://problem/4030599> Hostname passed into DNSServiceRegister ignored for Wide-Area service registrations
579 Revision 1.567 2006/12/21 00:01:37 cheshire
580 Tidy up code alignment
582 Revision 1.566 2006/12/20 04:07:34 cheshire
583 Remove uDNS_info substructure from AuthRecord_struct
585 Revision 1.565 2006/12/19 22:49:23 cheshire
586 Remove uDNS_info substructure from ServiceRecordSet_struct
588 Revision 1.564 2006/12/19 02:38:20 cheshire
589 Get rid of unnecessary duplicate query ID field from DNSQuestion_struct
591 Revision 1.563 2006/12/19 02:18:48 cheshire
592 Get rid of unnecessary duplicate "void *context" field from DNSQuestion_struct
594 Revision 1.562 2006/12/16 01:58:31 cheshire
595 <rdar://problem/4720673> uDNS: Need to start caching unicast records
597 Revision 1.561 2006/12/01 07:38:53 herscher
598 Only perform cache workaround fix if query is wide-area
600 Revision 1.560 2006/11/30 23:07:56 herscher
601 <rdar://problem/4765644> uDNS: Sync up with Lighthouse changes for Private DNS
603 Revision 1.559 2006/11/27 08:20:57 cheshire
604 Preliminary support for unifying the uDNS and mDNS code, including caching of uDNS answers
606 Revision 1.558 2006/11/10 07:44:03 herscher
607 <rdar://problem/4825493> Fix Daemon locking failures while toggling BTMM
609 Revision 1.557 2006/11/10 01:12:51 cheshire
610 <rdar://problem/4829718> Incorrect TTL corrections
612 Revision 1.556 2006/11/10 00:54:14 cheshire
613 <rdar://problem/4816598> Changing case of Computer Name doesn't work
615 Revision 1.555 2006/10/30 20:03:37 cheshire
616 <rdar://problem/4456945> After service restarts on different port, for a few seconds DNS-SD may return stale port number
618 Revision 1.554 2006/10/20 05:35:04 herscher
619 <rdar://problem/4720713> uDNS: Merge unicast active question list with multicast list.
621 Revision 1.553 2006/10/05 03:42:43 herscher
622 Remove embedded uDNS_info struct in DNSQuestion_struct
624 Revision 1.552 2006/09/15 21:20:15 cheshire
625 Remove uDNS_info substructure from mDNS_struct
627 Revision 1.551 2006/08/14 23:24:22 cheshire
628 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
630 Revision 1.550 2006/07/27 17:58:34 cheshire
631 Improved text of "SendQueries didn't send all its queries; will try again" debugging message
633 Revision 1.549 2006/07/20 22:07:31 mkrochma
634 <rdar://problem/4633196> Wide-area browsing is currently broken in TOT
635 More fixes for uninitialized variables
637 Revision 1.548 2006/07/20 19:30:19 mkrochma
638 <rdar://problem/4633196> Wide-area browsing sometimes doesn't work in TOT
640 Revision 1.547 2006/07/15 02:31:30 cheshire
641 <rdar://problem/4630812> Suppress log messages for certain old devices with inconsistent TXT RRSet TTLs
643 Revision 1.546 2006/07/07 01:09:09 cheshire
644 <rdar://problem/4472013> Add Private DNS server functionality to dnsextd
645 Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd
647 Revision 1.545 2006/07/05 23:10:30 cheshire
648 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
649 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
651 Revision 1.544 2006/06/29 07:42:14 cheshire
652 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
654 Revision 1.543 2006/06/29 01:38:43 cheshire
655 <rdar://problem/4605285> Only request unicast responses on wake from sleep and network connection
657 Revision 1.542 2006/06/27 23:40:29 cheshire
658 Fix typo in comment: mis-spelled "compile"
660 Revision 1.541 2006/06/27 19:46:24 cheshire
661 Updated comments and debugging messages
663 Revision 1.540 2006/06/15 21:35:16 cheshire
664 Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
665 from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
667 Revision 1.539 2006/06/08 23:45:46 cheshire
668 Change SimultaneousProbe messages from debugf() to LogOperation()
670 Revision 1.538 2006/03/19 17:13:06 cheshire
671 <rdar://problem/4483117> Need faster purging of stale records
672 Shorten kDefaultReconfirmTimeForNoAnswer to five seconds
673 and reconfirm whole chain of antecedents ot once
675 Revision 1.537 2006/03/19 02:00:07 cheshire
676 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
678 Revision 1.536 2006/03/08 23:29:53 cheshire
679 <rdar://problem/4468716> Improve "Service Renamed" log message
681 Revision 1.535 2006/03/02 20:41:17 cheshire
682 <rdar://problem/4111464> After record update, old record sometimes remains in cache
683 Minor code tidying and comments to reduce the risk of similar programming errors in future
685 Revision 1.534 2006/03/02 03:25:46 cheshire
686 <rdar://problem/4111464> After record update, old record sometimes remains in cache
687 Code to harmonize RRSet TTLs was inadvertently rescuing expiring records
689 Revision 1.533 2006/02/26 00:54:41 cheshire
690 Fixes to avoid code generation warning/error on FreeBSD 7
694 #include "DNSCommon.h" // Defines general DNS untility routines
695 #include "uDNS.h" // Defines entry points into unicast-specific routines
697 // Disable certain benign warnings with Microsoft compilers
698 #if(defined(_MSC_VER))
699 // Disable "conditional expression is constant" warning for debug macros.
700 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
701 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
702 #pragma warning(disable:4127)
704 // Disable "assignment within conditional expression".
705 // Other compilers understand the convention that if you place the assignment expression within an extra pair
706 // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
707 // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
708 // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
709 #pragma warning(disable:4706)
712 // ***************************************************************************
713 #if COMPILER_LIKES_PRAGMA_MARK
714 #pragma mark - Program Constants
719 mDNSlocal
const mDNSInterfaceID mDNSInterfaceMark
= (mDNSInterfaceID
)~0;
721 // Any records bigger than this are considered 'large' records
722 #define SmallRecordLimit 1024
724 #define kMaxUpdateCredits 10
725 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
727 mDNSexport
const char *const mDNS_DomainTypeNames
[] =
729 "b._dns-sd._udp.", // Browse
730 "db._dns-sd._udp.", // Default Browse
731 "lb._dns-sd._udp.", // Automatic Browse
732 "r._dns-sd._udp.", // Registration
733 "dr._dns-sd._udp." // Default Registration
736 #ifdef UNICAST_DISABLED
737 #define uDNS_IsActiveQuery(q, u) mDNSfalse
740 // ***************************************************************************
741 #if COMPILER_LIKES_PRAGMA_MARK
743 #pragma mark - General Utility Functions
746 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
747 #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
749 mDNSexport
void SetNextQueryTime(mDNS
*const m
, const DNSQuestion
*const q
)
751 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
+1)
752 LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
754 if (ActiveQuestion(q
))
756 mDNSs32 sendtime
= q
->LastQTime
+ q
->ThisQInterval
;
758 // Don't allow sendtime to be earlier than SuppressStdPort53Queries
759 if (!mDNSOpaque16IsZero(q
->TargetQID
) && !q
->LongLived
&& m
->SuppressStdPort53Queries
&& (sendtime
- m
->SuppressStdPort53Queries
< 0))
760 sendtime
= m
->SuppressStdPort53Queries
;
762 if (m
->NextScheduledQuery
- sendtime
> 0)
763 m
->NextScheduledQuery
= sendtime
;
767 mDNSexport CacheGroup
*CacheGroupForName(const mDNS
*const m
, const mDNSu32 slot
, const mDNSu32 namehash
, const domainname
*const name
)
770 for (cg
= m
->rrcache_hash
[slot
]; cg
; cg
=cg
->next
)
771 if (cg
->namehash
== namehash
&& SameDomainName(cg
->name
, name
))
776 mDNSlocal CacheGroup
*CacheGroupForRecord(const mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
778 return(CacheGroupForName(m
, slot
, rr
->namehash
, rr
->name
));
781 mDNSlocal mDNSBool
AddressIsLocalSubnet(mDNS
*const m
, const mDNSInterfaceID InterfaceID
, const mDNSAddr
*addr
)
783 NetworkInterfaceInfo
*intf
;
785 if (addr
->type
== mDNSAddrType_IPv4
)
787 // Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception
788 if (mDNSv4AddressIsLinkLocal(&addr
->ip
.v4
)) return(mDNStrue
);
789 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
790 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
791 if (((intf
->ip
.ip
.v4
.NotAnInteger
^ addr
->ip
.v4
.NotAnInteger
) & intf
->mask
.ip
.v4
.NotAnInteger
) == 0)
795 if (addr
->type
== mDNSAddrType_IPv6
)
797 if (mDNSv6AddressIsLinkLocal(&addr
->ip
.v4
)) return(mDNStrue
);
798 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
799 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
800 if ((((intf
->ip
.ip
.v6
.l
[0] ^ addr
->ip
.v6
.l
[0]) & intf
->mask
.ip
.v6
.l
[0]) == 0) &&
801 (((intf
->ip
.ip
.v6
.l
[1] ^ addr
->ip
.v6
.l
[1]) & intf
->mask
.ip
.v6
.l
[1]) == 0) &&
802 (((intf
->ip
.ip
.v6
.l
[2] ^ addr
->ip
.v6
.l
[2]) & intf
->mask
.ip
.v6
.l
[2]) == 0) &&
803 (((intf
->ip
.ip
.v6
.l
[3] ^ addr
->ip
.v6
.l
[3]) & intf
->mask
.ip
.v6
.l
[3]) == 0))
810 // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
811 // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
812 mDNSlocal
void AnswerLocalOnlyQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, AuthRecord
*rr
, QC_result AddRecord
)
814 // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
815 if (AddRecord
) rr
->LocalAnswer
= mDNStrue
;
816 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
817 if (q
->QuestionCallback
&& !q
->NoAnswer
)
818 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
819 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
822 // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers
823 // to each, stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
824 // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
825 // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
826 mDNSlocal
void AnswerLocalQuestions(mDNS
*const m
, AuthRecord
*rr
, QC_result AddRecord
)
828 if (m
->CurrentQuestion
)
829 LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
831 m
->CurrentQuestion
= m
->LocalOnlyQuestions
;
832 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewLocalOnlyQuestions
)
834 DNSQuestion
*q
= m
->CurrentQuestion
;
835 m
->CurrentQuestion
= q
->next
;
836 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
837 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
840 // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
841 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
843 m
->CurrentQuestion
= m
->Questions
;
844 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
846 DNSQuestion
*q
= m
->CurrentQuestion
;
847 m
->CurrentQuestion
= q
->next
;
848 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
849 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
853 m
->CurrentQuestion
= mDNSNULL
;
856 // ***************************************************************************
857 #if COMPILER_LIKES_PRAGMA_MARK
859 #pragma mark - Resource Record Utility Functions
862 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
864 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
865 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
866 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
867 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
869 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
870 (ResourceRecordIsValidAnswer(RR) && \
871 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
873 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
874 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
876 #define InitialAnnounceCount ((mDNSu8)8)
878 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
879 // This means that because the announce interval is doubled after sending the first packet, the first
880 // observed on-the-wire inter-packet interval between announcements is actually one second.
881 // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
882 #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
883 #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
884 #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
886 #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
887 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
888 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
890 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
891 #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
892 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
893 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
895 #define MaxUnansweredQueries 4
897 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
898 // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
899 // TTL and rdata may differ.
900 // This is used for cache flush management:
901 // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
902 // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
904 mDNSlocal mDNSBool
SameResourceRecordSignature(const AuthRecord
*const r1
, const AuthRecord
*const r2
)
906 if (!r1
) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse
); }
907 if (!r2
) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse
); }
908 if (r1
->resrec
.InterfaceID
&&
909 r2
->resrec
.InterfaceID
&&
910 r1
->resrec
.InterfaceID
!= r2
->resrec
.InterfaceID
) return(mDNSfalse
);
912 r1
->resrec
.rrtype
== r2
->resrec
.rrtype
&&
913 r1
->resrec
.rrclass
== r2
->resrec
.rrclass
&&
914 r1
->resrec
.namehash
== r2
->resrec
.namehash
&&
915 SameDomainName(r1
->resrec
.name
, r2
->resrec
.name
));
918 // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
919 // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
920 // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
921 // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
922 // so a response of any type should match, even if it is not actually the type the client plans to use.
923 mDNSlocal mDNSBool
PacketRRMatchesSignature(const CacheRecord
*const pktrr
, const AuthRecord
*const authrr
)
925 if (!pktrr
) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse
); }
926 if (!authrr
) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse
); }
927 if (pktrr
->resrec
.InterfaceID
&&
928 authrr
->resrec
.InterfaceID
&&
929 pktrr
->resrec
.InterfaceID
!= authrr
->resrec
.InterfaceID
) return(mDNSfalse
);
930 if (!(authrr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) && pktrr
->resrec
.rrtype
!= authrr
->resrec
.rrtype
) return(mDNSfalse
);
932 pktrr
->resrec
.rrclass
== authrr
->resrec
.rrclass
&&
933 pktrr
->resrec
.namehash
== authrr
->resrec
.namehash
&&
934 SameDomainName(pktrr
->resrec
.name
, authrr
->resrec
.name
));
937 // IdenticalResourceRecord returns true if two resources records have
938 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
940 // IdenticalSameNameRecord is the same, except it skips the expensive SameDomainName() check,
941 // which is at its most expensive and least useful in cases where we know in advance that the names match
943 mDNSlocal mDNSBool
IdenticalResourceRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
945 if (!r1
) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse
); }
946 if (!r2
) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse
); }
947 if (r1
->rrtype
!= r2
->rrtype
|| r1
->rrclass
!= r2
->rrclass
|| r1
->namehash
!= r2
->namehash
|| !SameDomainName(r1
->name
, r2
->name
))
949 return(SameRData(r1
, r2
));
952 mDNSlocal mDNSBool
IdenticalSameNameRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
954 if (!r1
) { LogMsg("IdenticalSameNameRecord ERROR: r1 is NULL"); return(mDNSfalse
); }
955 if (!r2
) { LogMsg("IdenticalSameNameRecord ERROR: r2 is NULL"); return(mDNSfalse
); }
956 if (r1
->rrtype
!= r2
->rrtype
|| r1
->rrclass
!= r2
->rrclass
)
959 #if VerifySameNameAssumptions
960 if (r1
->namehash
!= r2
->namehash
|| !SameDomainName(r1
->name
, r2
->name
))
962 LogMsg("Bogus IdenticalSameNameRecord call: %##s does not match %##s", r1
->name
->c
, r1
->name
->c
);
967 return(SameRData(r1
, r2
));
970 // CacheRecord *ks is the CacheRecord from the known answer list in the query.
971 // This is the information that the requester believes to be correct.
972 // AuthRecord *rr is the answer we are proposing to give, if not suppressed.
973 // This is the information that we believe to be correct.
974 // We've already determined that we plan to give this answer on this interface
975 // (either the record is non-specific, or it is specific to this interface)
976 // so now we just need to check the name, type, class, rdata and TTL.
977 mDNSlocal mDNSBool
ShouldSuppressKnownAnswer(const CacheRecord
*const ka
, const AuthRecord
*const rr
)
979 // If RR signature is different, or data is different, then don't suppress our answer
980 if (!IdenticalResourceRecord(&ka
->resrec
, &rr
->resrec
)) return(mDNSfalse
);
982 // If the requester's indicated TTL is less than half the real TTL,
983 // we need to give our answer before the requester's copy expires.
984 // If the requester's indicated TTL is at least half the real TTL,
985 // then we can suppress our answer this time.
986 // If the requester's indicated TTL is greater than the TTL we believe,
987 // then that's okay, and we don't need to do anything about it.
988 // (If two responders on the network are offering the same information,
989 // that's okay, and if they are offering the information with different TTLs,
990 // the one offering the lower TTL should defer to the one offering the higher TTL.)
991 return(mDNSBool
)(ka
->resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/ 2);
994 mDNSlocal
void SetNextAnnounceProbeTime(mDNS
*const m
, const AuthRecord
*const rr
)
996 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
998 //LogMsg("ProbeCount %d Next %ld %s",
999 // rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
1000 if (m
->NextScheduledProbe
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
1001 m
->NextScheduledProbe
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
1003 else if (rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
))
1005 if (m
->NextScheduledResponse
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
1006 m
->NextScheduledResponse
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
1010 mDNSlocal
void InitializeLastAPTime(mDNS
*const m
, AuthRecord
*const rr
)
1012 // To allow us to aggregate probes when a group of services are registered together,
1013 // the first probe is delayed 1/4 second. This means the common-case behaviour is:
1014 // 1/4 second wait; probe
1015 // 1/4 second wait; probe
1016 // 1/4 second wait; probe
1017 // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
1019 // If we have no probe suppression time set, or it is in the past, set it now
1020 if (m
->SuppressProbes
== 0 || m
->SuppressProbes
- m
->timenow
< 0)
1022 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ DefaultProbeIntervalForTypeUnique
);
1023 // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
1024 if (m
->SuppressProbes
- m
->NextScheduledProbe
>= 0)
1025 m
->SuppressProbes
= m
->NextScheduledProbe
;
1026 // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
1027 if (m
->SuppressProbes
- m
->NextScheduledQuery
>= 0)
1028 m
->SuppressProbes
= m
->NextScheduledQuery
;
1031 rr
->LastAPTime
= m
->SuppressProbes
- rr
->ThisAPInterval
;
1032 // Set LastMCTime to now, to inhibit multicast responses
1033 // (no need to send additional multicast responses when we're announcing anyway)
1034 rr
->LastMCTime
= m
->timenow
;
1035 rr
->LastMCInterface
= mDNSInterfaceMark
;
1037 // If this is a record type that's not going to probe, then delay its first announcement so that
1038 // it will go out synchronized with the first announcement for the other records that *are* probing.
1039 // This is a minor performance tweak that helps keep groups of related records synchronized together.
1040 // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
1041 // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
1042 // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
1043 // because they will meet the criterion of being at least half-way to their scheduled announcement time.
1044 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
)
1045 rr
->LastAPTime
+= DefaultProbeIntervalForTypeUnique
* DefaultProbeCountForTypeUnique
+ rr
->ThisAPInterval
/ 2;
1047 SetNextAnnounceProbeTime(m
, rr
);
1050 // Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname
1051 // Eventually we should unify this with GetServiceTarget() in uDNS.c
1052 mDNSlocal
void SetTargetToHostName(mDNS
*const m
, AuthRecord
*const rr
)
1054 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
1056 if (!target
) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr
->resrec
.rrtype
);
1058 if (target
&& SameDomainName(target
, &m
->MulticastHostname
))
1059 debugf("SetTargetToHostName: Target of %##s is already %##s", rr
->resrec
.name
->c
, target
->c
);
1061 if (target
&& !SameDomainName(target
, &m
->MulticastHostname
))
1063 AssignDomainName(target
, &m
->MulticastHostname
);
1064 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Update rdlength, rdestimate, rdatahash
1066 // If we're in the middle of probing this record, we need to start again,
1067 // because changing its rdata may change the outcome of the tie-breaker.
1068 // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
1069 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
1071 // If we've announced this record, we really should send a goodbye packet for the old rdata before
1072 // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
1073 // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
1074 if (rr
->RequireGoodbye
&& rr
->resrec
.RecordType
== kDNSRecordTypeShared
)
1075 debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating",
1076 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1078 rr
->AnnounceCount
= InitialAnnounceCount
;
1079 rr
->RequireGoodbye
= mDNSfalse
;
1080 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
1081 InitializeLastAPTime(m
,rr
);
1085 mDNSlocal
void AcknowledgeRecord(mDNS
*const m
, AuthRecord
*const rr
)
1087 if (rr
->RecordCallback
)
1089 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
1090 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
1091 rr
->Acknowledged
= mDNStrue
;
1092 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
1093 rr
->RecordCallback(m
, rr
, mStatus_NoError
);
1094 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
1098 // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
1099 #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \
1100 ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
1101 #define RecordIsLocalDuplicate(A,B) \
1102 ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
1104 // Exported so uDNS.c can call this
1105 mDNSexport mStatus
mDNS_Register_internal(mDNS
*const m
, AuthRecord
*const rr
)
1107 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
1109 AuthRecord
**p
= &m
->ResourceRecords
;
1110 AuthRecord
**d
= &m
->DuplicateRecords
;
1112 if ((mDNSs32
)rr
->resrec
.rroriginalttl
<= 0)
1113 { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m
, rr
)); return(mStatus_BadParamErr
); }
1115 if (!rr
->resrec
.RecordType
)
1116 { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m
, rr
)); return(mStatus_BadParamErr
); }
1118 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
1119 while (*d
&& *d
!= rr
) d
=&(*d
)->next
;
1122 LogMsg("Error! Tried to register AuthRecord %p %##s (%s) that's already in the list",
1123 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1124 return(mStatus_AlreadyRegistered
);
1127 if (rr
->DependentOn
)
1129 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
1130 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
1133 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
1134 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1135 return(mStatus_Invalid
);
1137 if (!(rr
->DependentOn
->resrec
.RecordType
& (kDNSRecordTypeUnique
| kDNSRecordTypeVerified
)))
1139 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
1140 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->DependentOn
->resrec
.RecordType
);
1141 return(mStatus_Invalid
);
1145 // If this resource record is referencing a specific interface, make sure it exists
1146 if (rr
->resrec
.InterfaceID
&& rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
1148 NetworkInterfaceInfo
*intf
;
1149 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
1150 if (intf
->InterfaceID
== rr
->resrec
.InterfaceID
) break;
1153 debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr
->resrec
.InterfaceID
);
1154 return(mStatus_BadReferenceErr
);
1158 rr
->next
= mDNSNULL
;
1160 // Field Group 1: The actual information pertaining to this resource record
1161 // Set up by client prior to call
1163 // Field Group 2: Persistent metadata for Authoritative Records
1164 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1165 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1166 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1167 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1168 // rr->Callback = already set in mDNS_SetupResourceRecord
1169 // rr->Context = already set in mDNS_SetupResourceRecord
1170 // rr->RecordType = already set in mDNS_SetupResourceRecord
1171 // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
1172 // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
1173 // Make sure target is not uninitialized data, or we may crash writing debugging log messages
1174 if (rr
->AutoTarget
&& target
) target
->c
[0] = 0;
1176 // Field Group 3: Transient state for Authoritative Records
1177 rr
->Acknowledged
= mDNSfalse
;
1178 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
1179 rr
->AnnounceCount
= InitialAnnounceCount
;
1180 rr
->RequireGoodbye
= mDNSfalse
;
1181 rr
->LocalAnswer
= mDNSfalse
;
1182 rr
->IncludeInProbe
= mDNSfalse
;
1183 rr
->ImmedAnswer
= mDNSNULL
;
1184 rr
->ImmedUnicast
= mDNSfalse
;
1185 rr
->ImmedAdditional
= mDNSNULL
;
1186 rr
->SendRNow
= mDNSNULL
;
1187 rr
->v4Requester
= zerov4Addr
;
1188 rr
->v6Requester
= zerov6Addr
;
1189 rr
->NextResponse
= mDNSNULL
;
1190 rr
->NR_AnswerTo
= mDNSNULL
;
1191 rr
->NR_AdditionalTo
= mDNSNULL
;
1192 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
1193 if (!rr
->AutoTarget
) InitializeLastAPTime(m
, rr
);
1194 // rr->LastAPTime = Set for us in InitializeLastAPTime()
1195 // rr->LastMCTime = Set for us in InitializeLastAPTime()
1196 // rr->LastMCInterface = Set for us in InitializeLastAPTime()
1197 rr
->NewRData
= mDNSNULL
;
1198 rr
->newrdlength
= 0;
1199 rr
->UpdateCallback
= mDNSNULL
;
1200 rr
->UpdateCredits
= kMaxUpdateCredits
;
1201 rr
->NextUpdateCredit
= 0;
1202 rr
->UpdateBlocked
= 0;
1204 // Field Group 4: Transient uDNS state for Authoritative Records
1205 rr
->state
= regState_Zero
;
1211 rr
->UpdateServer
= zeroAddr
;
1212 rr
->UpdatePort
= zeroIPPort
;
1217 rr
->InFlightRData
= 0;
1218 rr
->InFlightRDLen
= 0;
1219 rr
->QueuedRData
= 0;
1220 rr
->QueuedRDLen
= 0;
1222 // rr->resrec.interface = already set in mDNS_SetupResourceRecord
1223 // rr->resrec.name->c = MUST be set by client
1224 // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
1225 // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
1226 // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
1227 // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
1230 SetTargetToHostName(m
, rr
); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
1233 rr
->resrec
.rdlength
= GetRDLength(&rr
->resrec
, mDNSfalse
);
1234 rr
->resrec
.rdestimate
= GetRDLength(&rr
->resrec
, mDNStrue
);
1237 if (!ValidateDomainName(rr
->resrec
.name
))
1238 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
1240 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1241 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1242 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1243 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& rr
->resrec
.rdlength
== 0) { rr
->resrec
.rdlength
= 1; rr
->resrec
.rdata
->u
.txt
.c
[0] = 0; }
1245 // Don't do this until *after* we've set rr->resrec.rdlength
1246 if (!ValidateRData(rr
->resrec
.rrtype
, rr
->resrec
.rdlength
, rr
->resrec
.rdata
))
1247 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
1249 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
1250 rr
->resrec
.rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->resrec
.rdlength
, &rr
->resrec
.rdata
->u
);
1252 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
1254 // If this is supposed to be unique, make sure we don't have any name conflicts
1255 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1257 const AuthRecord
*s1
= rr
->RRSet
? rr
->RRSet
: rr
;
1258 for (r
= m
->ResourceRecords
; r
; r
=r
->next
)
1260 const AuthRecord
*s2
= r
->RRSet
? r
->RRSet
: r
;
1261 if (s1
!= s2
&& SameResourceRecordSignature(r
, rr
) && !SameRData(&r
->resrec
, &rr
->resrec
))
1264 if (r
) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
1266 debugf("Name conflict %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1267 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
1268 rr
->resrec
.rroriginalttl
= 0;
1269 rr
->ImmedAnswer
= mDNSInterfaceMark
;
1270 m
->NextScheduledResponse
= m
->timenow
;
1275 // Now that we've finished building our new record, make sure it's not identical to one we already have
1276 for (r
= m
->ResourceRecords
; r
; r
=r
->next
) if (RecordIsLocalDuplicate(r
, rr
)) break;
1280 debugf("Adding to duplicate list %p %s", rr
, ARDisplayString(m
,rr
));
1282 // If the previous copy of this record is already verified unique,
1283 // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
1284 // Setting ProbeCount to zero will cause SendQueries() to advance this record to
1285 // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
1286 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& r
->resrec
.RecordType
== kDNSRecordTypeVerified
)
1291 debugf("Adding to active record list %p %s", rr
, ARDisplayString(m
,rr
));
1292 if (!m
->NewLocalRecords
) m
->NewLocalRecords
= rr
;
1296 if (rr
->resrec
.InterfaceID
!= mDNSInterface_Any
|| rr
->ForceMCast
|| IsLocalDomain(rr
->resrec
.name
))
1298 // For records that are not going to probe, acknowledge them right away
1299 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
&& rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
1300 AcknowledgeRecord(m
, rr
);
1302 #ifndef UNICAST_DISABLED
1305 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
) rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
1307 rr
->AnnounceCount
= 0;
1308 rr
->state
= regState_FetchingZoneData
;
1309 rr
->uselease
= mDNStrue
;
1310 rr
->nta
= StartGetZoneData(m
, rr
->resrec
.name
, ZoneServiceUpdate
, RecordRegistrationCallback
, rr
);
1311 return rr
->nta
? mStatus_NoError
: mStatus_NoMemoryErr
;
1315 return(mStatus_NoError
);
1318 mDNSlocal
void RecordProbeFailure(mDNS
*const m
, const AuthRecord
*const rr
)
1320 m
->ProbeFailTime
= m
->timenow
;
1321 m
->NumFailedProbes
++;
1322 // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
1323 // If a bunch of hosts have all been configured with the same name, then they'll all
1324 // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
1325 // up to name-10. After that they'll start adding random increments in the range 1-100,
1326 // so they're more likely to branch out in the available namespace and settle on a set of
1327 // unique names quickly. If after five more tries the host is still conflicting, then we
1328 // may have a serious problem, so we start rate-limiting so we don't melt down the network.
1329 if (m
->NumFailedProbes
>= 15)
1331 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
* 5);
1332 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
1333 m
->NumFailedProbes
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1337 mDNSlocal
void CompleteRDataUpdate(mDNS
*const m
, AuthRecord
*const rr
)
1339 RData
*OldRData
= rr
->resrec
.rdata
;
1340 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
); // Update our rdata
1341 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
1342 if (rr
->UpdateCallback
)
1343 rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
1346 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
1347 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1348 // Exported so uDNS.c can call this
1349 mDNSexport mStatus
mDNS_Deregister_internal(mDNS
*const m
, AuthRecord
*const rr
, mDNS_Dereg_type drt
)
1352 mDNSu8 RecordType
= rr
->resrec
.RecordType
;
1353 AuthRecord
**p
= &m
->ResourceRecords
; // Find this record in our list of active records
1355 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
1359 // We found our record on the main list. See if there are any duplicates that need special handling.
1360 if (drt
== mDNS_Dereg_conflict
) // If this was a conflict, see that all duplicates get the same treatment
1362 // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
1363 // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
1364 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
) if (RecordIsLocalDuplicate(r2
, rr
)) r2
->ProbeCount
= 0xFF;
1368 // Before we delete the record (and potentially send a goodbye packet)
1369 // first see if we have a record on the duplicate list ready to take over from it.
1370 AuthRecord
**d
= &m
->DuplicateRecords
;
1371 while (*d
&& !RecordIsLocalDuplicate(*d
, rr
)) d
=&(*d
)->next
;
1374 AuthRecord
*dup
= *d
;
1375 debugf("Duplicate record %p taking over from %p %##s (%s)",
1376 dup
, rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1377 *d
= dup
->next
; // Cut replacement record from DuplicateRecords list
1378 dup
->next
= rr
->next
; // And then...
1379 rr
->next
= dup
; // ... splice it in right after the record we're about to delete
1380 dup
->resrec
.RecordType
= rr
->resrec
.RecordType
;
1381 dup
->ProbeCount
= rr
->ProbeCount
;
1382 dup
->AnnounceCount
= rr
->AnnounceCount
;
1383 dup
->RequireGoodbye
= rr
->RequireGoodbye
;
1384 dup
->ImmedAnswer
= rr
->ImmedAnswer
;
1385 dup
->ImmedUnicast
= rr
->ImmedUnicast
;
1386 dup
->ImmedAdditional
= rr
->ImmedAdditional
;
1387 dup
->v4Requester
= rr
->v4Requester
;
1388 dup
->v6Requester
= rr
->v6Requester
;
1389 dup
->ThisAPInterval
= rr
->ThisAPInterval
;
1390 dup
->LastAPTime
= rr
->LastAPTime
;
1391 dup
->LastMCTime
= rr
->LastMCTime
;
1392 dup
->LastMCInterface
= rr
->LastMCInterface
;
1393 rr
->RequireGoodbye
= mDNSfalse
;
1399 // We didn't find our record on the main list; try the DuplicateRecords list instead.
1400 p
= &m
->DuplicateRecords
;
1401 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
1402 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
1403 if (*p
) rr
->RequireGoodbye
= mDNSfalse
;
1404 if (*p
) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
1405 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1410 // No need to log an error message if we already know this is a potentially repeated deregistration
1411 if (drt
!= mDNS_Dereg_repeat
)
1412 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list",
1413 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1414 return(mStatus_BadReferenceErr
);
1417 // If this is a shared record and we've announced it at least once,
1418 // we need to retract that announcement before we delete the record
1420 // If this is a record (including mDNSInterface_LocalOnly records) for which we've given local answers then
1421 // it's tempting to just do "AnswerLocalQuestions(m, rr, mDNSfalse)" here, but that would not not be safe.
1422 // The AnswerLocalQuestions routine walks the question list invoking client callbacks, using the "m->CurrentQuestion"
1423 // mechanism to cope with the client callback modifying the question list while that's happening.
1424 // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain)
1425 // which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice.
1426 // More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other
1427 // records, thereby invoking yet more callbacks, without limit.
1428 // The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending
1429 // actual goodbye packets.
1431 #ifndef UNICAST_DISABLED
1432 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
&& !rr
->ForceMCast
&& !IsLocalDomain(rr
->resrec
.name
))
1433 if (rr
->RequireGoodbye
)
1435 if (rr
->tcp
) { DisposeTCPConn(rr
->tcp
); rr
->tcp
= mDNSNULL
; }
1436 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
1437 uDNS_DeregisterRecord(m
, rr
);
1438 // At this point unconditionally we bail out
1439 // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
1440 // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
1441 // process and will complete asynchronously. Either way we don't need to do anything more here.
1442 return(mStatus_NoError
);
1444 #endif UNICAST_DISABLED
1446 if (RecordType
== kDNSRecordTypeShared
&& (rr
->RequireGoodbye
|| rr
->LocalAnswer
))
1448 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %s", ARDisplayString(m
, rr
));
1449 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
1450 rr
->resrec
.rroriginalttl
= 0;
1451 rr
->ImmedAnswer
= mDNSInterfaceMark
;
1452 if (m
->NextScheduledResponse
- (m
->timenow
+ mDNSPlatformOneSecond
/10) >= 0)
1453 m
->NextScheduledResponse
= (m
->timenow
+ mDNSPlatformOneSecond
/10);
1457 *p
= rr
->next
; // Cut this record from the list
1458 // If someone is about to look at this, bump the pointer forward
1459 if (m
->CurrentRecord
== rr
) m
->CurrentRecord
= rr
->next
;
1460 if (m
->NewLocalRecords
== rr
) m
->NewLocalRecords
= rr
->next
;
1461 rr
->next
= mDNSNULL
;
1463 if (RecordType
== kDNSRecordTypeUnregistered
)
1464 LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m
, rr
));
1465 else if (RecordType
== kDNSRecordTypeDeregistering
)
1466 LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m
, rr
));
1469 verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m
, rr
));
1470 rr
->resrec
.RecordType
= kDNSRecordTypeUnregistered
;
1473 if ((drt
== mDNS_Dereg_conflict
|| drt
== mDNS_Dereg_repeat
) && RecordType
== kDNSRecordTypeShared
)
1474 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
1475 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1477 // If we have an update queued up which never executed, give the client a chance to free that memory
1478 if (rr
->NewRData
) CompleteRDataUpdate(m
, rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
1480 if (rr
->nta
) { CancelGetZoneData(m
, rr
->nta
); rr
->nta
= mDNSNULL
; }
1481 if (rr
->tcp
) { DisposeTCPConn(rr
->tcp
); rr
->tcp
= mDNSNULL
; }
1483 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
1484 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
1485 // In this case the likely client action to the mStatus_MemFree message is to free the memory,
1486 // so any attempt to touch rr after this is likely to lead to a crash.
1487 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
1488 if (drt
!= mDNS_Dereg_conflict
)
1490 if (rr
->RecordCallback
)
1491 rr
->RecordCallback(m
, rr
, mStatus_MemFree
); // MUST NOT touch rr after this
1495 RecordProbeFailure(m
, rr
);
1496 if (rr
->RecordCallback
)
1497 rr
->RecordCallback(m
, rr
, mStatus_NameConflict
); // MUST NOT touch rr after this
1498 // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
1499 // Note that with all the client callbacks going on, by the time we get here all the
1500 // records we marked may have been explicitly deregistered by the client anyway.
1501 r2
= m
->DuplicateRecords
;
1504 if (r2
->ProbeCount
!= 0xFF) r2
= r2
->next
;
1505 else { mDNS_Deregister_internal(m
, r2
, mDNS_Dereg_conflict
); r2
= m
->DuplicateRecords
; }
1508 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
1510 return(mStatus_NoError
);
1513 // ***************************************************************************
1514 #if COMPILER_LIKES_PRAGMA_MARK
1516 #pragma mark - Packet Sending Functions
1519 mDNSlocal
void AddRecordToResponseList(AuthRecord
***nrpp
, AuthRecord
*rr
, AuthRecord
*add
)
1521 if (rr
->NextResponse
== mDNSNULL
&& *nrpp
!= &rr
->NextResponse
)
1524 // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
1525 // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
1526 // The referenced record will definitely be acceptable (by recursive application of this rule)
1527 if (add
&& add
->NR_AdditionalTo
) add
= add
->NR_AdditionalTo
;
1528 rr
->NR_AdditionalTo
= add
;
1529 *nrpp
= &rr
->NextResponse
;
1531 debugf("AddRecordToResponseList: %##s (%s) already in list", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1534 mDNSlocal
void AddAdditionalsToResponseList(mDNS
*const m
, AuthRecord
*ResponseRecords
, AuthRecord
***nrpp
, const mDNSInterfaceID InterfaceID
)
1536 AuthRecord
*rr
, *rr2
;
1537 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // For each record we plan to put
1539 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
1540 // later in the "for" loop, and we will follow further "additional" links then.)
1541 if (rr
->Additional1
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional1
, InterfaceID
))
1542 AddRecordToResponseList(nrpp
, rr
->Additional1
, rr
);
1544 if (rr
->Additional2
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional2
, InterfaceID
))
1545 AddRecordToResponseList(nrpp
, rr
->Additional2
, rr
);
1547 // For SRV records, automatically add the Address record(s) for the target host
1548 if (rr
->resrec
.rrtype
== kDNSType_SRV
)
1550 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
1551 if (RRTypeIsAddressType(rr2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
1552 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceID
) && // ... which are valid for answer ...
1553 rr
->resrec
.rdatahash
== rr2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
1554 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, rr2
->resrec
.name
))
1555 AddRecordToResponseList(nrpp
, rr2
, rr
);
1557 else if (RRTypeIsAddressType(rr
->resrec
.rrtype
)) // For A or AAAA, put counterpart as additional
1559 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
1560 if (RRTypeIsAddressType(rr2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
1561 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceID
) && // ... which are valid for answer ...
1562 rr
->resrec
.namehash
== rr2
->resrec
.namehash
&& // ... and have the same name
1563 SameDomainName(rr
->resrec
.name
, rr2
->resrec
.name
))
1564 AddRecordToResponseList(nrpp
, rr2
, rr
);
1566 else if (rr
->resrec
.rrtype
== kDNSType_PTR
) // For service PTR, see if we want to add DeviceInfo record
1568 if (ResourceRecordIsValidInterfaceAnswer(&m
->DeviceInfo
, InterfaceID
) &&
1569 SameDomainLabel(rr
->resrec
.rdata
->u
.name
.c
, m
->DeviceInfo
.resrec
.name
->c
))
1570 AddRecordToResponseList(nrpp
, &m
->DeviceInfo
, rr
);
1575 mDNSlocal
void SendDelayedUnicastResponse(mDNS
*const m
, const mDNSAddr
*const dest
, const mDNSInterfaceID InterfaceID
)
1578 AuthRecord
*ResponseRecords
= mDNSNULL
;
1579 AuthRecord
**nrp
= &ResponseRecords
;
1581 // Make a list of all our records that need to be unicast to this destination
1582 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1584 // If we find we can no longer unicast this answer, clear ImmedUnicast
1585 if (rr
->ImmedAnswer
== mDNSInterfaceMark
||
1586 mDNSSameIPv4Address(rr
->v4Requester
, onesIPv4Addr
) ||
1587 mDNSSameIPv6Address(rr
->v6Requester
, onesIPv6Addr
) )
1588 rr
->ImmedUnicast
= mDNSfalse
;
1590 if (rr
->ImmedUnicast
&& rr
->ImmedAnswer
== InterfaceID
)
1591 if ((dest
->type
== mDNSAddrType_IPv4
&& mDNSSameIPv4Address(rr
->v4Requester
, dest
->ip
.v4
)) ||
1592 (dest
->type
== mDNSAddrType_IPv6
&& mDNSSameIPv6Address(rr
->v6Requester
, dest
->ip
.v6
)))
1594 rr
->ImmedAnswer
= mDNSNULL
; // Clear the state fields
1595 rr
->ImmedUnicast
= mDNSfalse
;
1596 rr
->v4Requester
= zerov4Addr
;
1597 rr
->v6Requester
= zerov6Addr
;
1598 if (rr
->NextResponse
== mDNSNULL
&& nrp
!= &rr
->NextResponse
) // rr->NR_AnswerTo
1599 { rr
->NR_AnswerTo
= (mDNSu8
*)~0; *nrp
= rr
; nrp
= &rr
->NextResponse
; }
1603 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
1605 while (ResponseRecords
)
1607 mDNSu8
*responseptr
= m
->omsg
.data
;
1609 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
1611 // Put answers in the packet
1612 while (ResponseRecords
&& ResponseRecords
->NR_AnswerTo
)
1614 rr
= ResponseRecords
;
1615 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1616 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1617 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
1618 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1619 if (!newptr
&& m
->omsg
.h
.numAnswers
) break; // If packet full, send it now
1620 if (newptr
) responseptr
= newptr
;
1621 ResponseRecords
= rr
->NextResponse
;
1622 rr
->NextResponse
= mDNSNULL
;
1623 rr
->NR_AnswerTo
= mDNSNULL
;
1624 rr
->NR_AdditionalTo
= mDNSNULL
;
1625 rr
->RequireGoodbye
= mDNStrue
;
1628 // Add additionals, if there's space
1629 while (ResponseRecords
&& !ResponseRecords
->NR_AnswerTo
)
1631 rr
= ResponseRecords
;
1632 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1633 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1634 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
1635 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1637 if (newptr
) responseptr
= newptr
;
1638 if (newptr
&& m
->omsg
.h
.numAnswers
) rr
->RequireGoodbye
= mDNStrue
;
1639 else if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) rr
->ImmedAnswer
= mDNSInterfaceMark
;
1640 ResponseRecords
= rr
->NextResponse
;
1641 rr
->NextResponse
= mDNSNULL
;
1642 rr
->NR_AnswerTo
= mDNSNULL
;
1643 rr
->NR_AdditionalTo
= mDNSNULL
;
1646 if (m
->omsg
.h
.numAnswers
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, mDNSInterface_Any
, dest
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
1650 mDNSexport
void CompleteDeregistration(mDNS
*const m
, AuthRecord
*rr
)
1652 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() that
1653 // it should go ahead and immediately dispose of this registration
1654 rr
->resrec
.RecordType
= kDNSRecordTypeShared
;
1655 rr
->RequireGoodbye
= mDNSfalse
;
1656 if (rr
->LocalAnswer
) { AnswerLocalQuestions(m
, rr
, mDNSfalse
); rr
->LocalAnswer
= mDNSfalse
; }
1657 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
); // Don't touch rr after this
1660 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
1661 // the record list and/or question list.
1662 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1663 mDNSlocal
void DiscardDeregistrations(mDNS
*const m
)
1665 if (m
->CurrentRecord
)
1666 LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
1667 m
->CurrentRecord
= m
->ResourceRecords
;
1669 while (m
->CurrentRecord
)
1671 AuthRecord
*rr
= m
->CurrentRecord
;
1672 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
1673 CompleteDeregistration(m
, rr
); // Don't touch rr after this
1675 m
->CurrentRecord
= rr
->next
;
1679 mDNSlocal
void GrantUpdateCredit(AuthRecord
*rr
)
1681 if (++rr
->UpdateCredits
>= kMaxUpdateCredits
) rr
->NextUpdateCredit
= 0;
1682 else rr
->NextUpdateCredit
= NonZeroTime(rr
->NextUpdateCredit
+ kUpdateCreditRefreshInterval
);
1685 // Note about acceleration of announcements to facilitate automatic coalescing of
1686 // multiple independent threads of announcements into a single synchronized thread:
1687 // The announcements in the packet may be at different stages of maturity;
1688 // One-second interval, two-second interval, four-second interval, and so on.
1689 // After we've put in all the announcements that are due, we then consider
1690 // whether there are other nearly-due announcements that are worth accelerating.
1691 // To be eligible for acceleration, a record MUST NOT be older (further along
1692 // its timeline) than the most mature record we've already put in the packet.
1693 // In other words, younger records can have their timelines accelerated to catch up
1694 // with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
1695 // Older records cannot have their timelines accelerated; this would just widen
1696 // the gap between them and their younger bretheren and get them even more out of sync.
1698 // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
1699 // the record list and/or question list.
1700 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1701 mDNSlocal
void SendResponses(mDNS
*const m
)
1704 AuthRecord
*rr
, *r2
;
1705 mDNSs32 maxExistingAnnounceInterval
= 0;
1706 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
1708 m
->NextScheduledResponse
= m
->timenow
+ 0x78000000;
1710 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1711 if (rr
->ImmedUnicast
)
1713 mDNSAddr v4
= { mDNSAddrType_IPv4
, {{{0}}} };
1714 mDNSAddr v6
= { mDNSAddrType_IPv6
, {{{0}}} };
1715 v4
.ip
.v4
= rr
->v4Requester
;
1716 v6
.ip
.v6
= rr
->v6Requester
;
1717 if (!mDNSIPv4AddressIsZero(rr
->v4Requester
)) SendDelayedUnicastResponse(m
, &v4
, rr
->ImmedAnswer
);
1718 if (!mDNSIPv6AddressIsZero(rr
->v6Requester
)) SendDelayedUnicastResponse(m
, &v6
, rr
->ImmedAnswer
);
1719 if (rr
->ImmedUnicast
)
1721 LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m
, rr
));
1722 rr
->ImmedUnicast
= mDNSfalse
;
1727 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
1730 // Run through our list of records, and decide which ones we're going to announce on all interfaces
1731 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1733 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
1734 if (TimeToAnnounceThisRecord(rr
, m
->timenow
) && ResourceRecordIsValidAnswer(rr
))
1736 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
1737 if (maxExistingAnnounceInterval
< rr
->ThisAPInterval
)
1738 maxExistingAnnounceInterval
= rr
->ThisAPInterval
;
1739 if (rr
->UpdateBlocked
) rr
->UpdateBlocked
= 0;
1743 // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
1744 // Eligible records that are more than half-way to their announcement time are accelerated
1745 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1746 if ((rr
->resrec
.InterfaceID
&& rr
->ImmedAnswer
) ||
1747 (rr
->ThisAPInterval
<= maxExistingAnnounceInterval
&&
1748 TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2) &&
1749 ResourceRecordIsValidAnswer(rr
)))
1750 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
1752 // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
1753 // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
1754 // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
1755 // then all that means is that it won't get sent -- which would not be the end of the world.
1756 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1758 if (rr
->ImmedAnswer
&& rr
->resrec
.rrtype
== kDNSType_SRV
)
1759 for (r2
=m
->ResourceRecords
; r2
; r2
=r2
->next
) // Scan list of resource records
1760 if (RRTypeIsAddressType(r2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
1761 ResourceRecordIsValidAnswer(r2
) && // ... which are valid for answer ...
1762 rr
->LastMCTime
- r2
->LastMCTime
>= 0 && // ... which we have not sent recently ...
1763 rr
->resrec
.rdatahash
== r2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
1764 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, r2
->resrec
.name
) &&
1765 (rr
->ImmedAnswer
== mDNSInterfaceMark
|| rr
->ImmedAnswer
== r2
->resrec
.InterfaceID
))
1766 r2
->ImmedAdditional
= r2
->resrec
.InterfaceID
; // ... then mark this address record for sending too
1767 // We also make sure we send the DeviceInfo TXT record too, if necessary
1768 // We check for RecordType == kDNSRecordTypeShared because we don't want to tag the
1769 // DeviceInfo TXT record onto a goodbye packet (RecordType == kDNSRecordTypeDeregistering).
1770 if (rr
->ImmedAnswer
&& rr
->resrec
.RecordType
== kDNSRecordTypeShared
&& rr
->resrec
.rrtype
== kDNSType_PTR
)
1771 if (ResourceRecordIsValidAnswer(&m
->DeviceInfo
) && SameDomainLabel(rr
->resrec
.rdata
->u
.name
.c
, m
->DeviceInfo
.resrec
.name
->c
))
1773 if (!m
->DeviceInfo
.ImmedAnswer
) m
->DeviceInfo
.ImmedAnswer
= rr
->ImmedAnswer
;
1774 else m
->DeviceInfo
.ImmedAnswer
= mDNSInterfaceMark
;
1778 // If there's a record which is supposed to be unique that we're going to send, then make sure that we give
1779 // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
1780 // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
1781 // record, then other RRSet members that have not been sent recently will get flushed out of client caches.
1782 // -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface
1783 // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
1784 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1785 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1787 if (rr
->ImmedAnswer
) // If we're sending this as answer, see that its whole RRSet is similarly marked
1789 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
1790 if (ResourceRecordIsValidAnswer(r2
))
1791 if (r2
->ImmedAnswer
!= mDNSInterfaceMark
&&
1792 r2
->ImmedAnswer
!= rr
->ImmedAnswer
&& SameResourceRecordSignature(r2
, rr
))
1793 r2
->ImmedAnswer
= rr
->ImmedAnswer
;
1795 else if (rr
->ImmedAdditional
) // If we're sending this as additional, see that its whole RRSet is similarly marked
1797 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
1798 if (ResourceRecordIsValidAnswer(r2
))
1799 if (r2
->ImmedAdditional
!= rr
->ImmedAdditional
&& SameResourceRecordSignature(r2
, rr
))
1800 r2
->ImmedAdditional
= rr
->ImmedAdditional
;
1804 // Now set SendRNow state appropriately
1805 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1807 if (rr
->ImmedAnswer
== mDNSInterfaceMark
) // Sending this record on all appropriate interfaces
1809 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
1810 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional if sending as answer
1811 rr
->LastMCTime
= m
->timenow
;
1812 rr
->LastMCInterface
= rr
->ImmedAnswer
;
1813 // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
1814 if (TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2))
1816 rr
->AnnounceCount
--;
1817 rr
->ThisAPInterval
*= 2;
1818 rr
->LastAPTime
= m
->timenow
;
1819 debugf("Announcing %##s (%s) %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->AnnounceCount
);
1822 else if (rr
->ImmedAnswer
) // Else, just respond to a single query on single interface:
1824 rr
->SendRNow
= rr
->ImmedAnswer
; // Just respond on that interface
1825 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional too
1826 rr
->LastMCTime
= m
->timenow
;
1827 rr
->LastMCInterface
= rr
->ImmedAnswer
;
1829 SetNextAnnounceProbeTime(m
, rr
);
1830 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
1834 // *** 2. Loop through interface list, sending records as appropriate
1840 int numAnnounce
= 0;
1842 mDNSu8
*responseptr
= m
->omsg
.data
;
1844 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
1846 // First Pass. Look for:
1847 // 1. Deregistering records that need to send their goodbye packet
1848 // 2. Updated records that need to retract their old data
1849 // 3. Answers and announcements we need to send
1850 // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
1851 // send this packet and then try again.
1852 // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
1853 // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
1854 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1855 if (rr
->SendRNow
== intf
->InterfaceID
)
1857 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
1859 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
1860 if (newptr
) { responseptr
= newptr
; numDereg
++; }
1861 else if (m
->omsg
.h
.numAnswers
) break;
1863 else if (rr
->NewRData
&& !m
->SleepState
) // If we have new data for this record
1865 RData
*OldRData
= rr
->resrec
.rdata
;
1866 mDNSu16 oldrdlength
= rr
->resrec
.rdlength
;
1867 // See if we should send a courtesy "goodbye" for the old data before we replace it.
1868 if (ResourceRecordIsValidAnswer(rr
) && rr
->RequireGoodbye
)
1870 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
1871 if (newptr
) { responseptr
= newptr
; numDereg
++; rr
->RequireGoodbye
= mDNSfalse
; }
1872 else if (m
->omsg
.h
.numAnswers
) break;
1874 // Now try to see if we can fit the update in the same packet (not fatal if we can't)
1875 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
);
1876 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1877 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1878 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
1879 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1880 if (newptr
) { responseptr
= newptr
; rr
->RequireGoodbye
= mDNStrue
; }
1881 SetNewRData(&rr
->resrec
, OldRData
, oldrdlength
);
1885 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1886 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1887 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, m
->SleepState
? 0 : rr
->resrec
.rroriginalttl
);
1888 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1891 responseptr
= newptr
;
1892 rr
->RequireGoodbye
= (mDNSu8
) (!m
->SleepState
);
1893 if (rr
->LastAPTime
== m
->timenow
) numAnnounce
++; else numAnswer
++;
1895 else if (m
->omsg
.h
.numAnswers
) break;
1897 // If sending on all interfaces, go to next interface; else we're finished now
1898 if (rr
->ImmedAnswer
== mDNSInterfaceMark
&& rr
->resrec
.InterfaceID
== mDNSInterface_Any
)
1899 rr
->SendRNow
= GetNextActiveInterfaceID(intf
);
1901 rr
->SendRNow
= mDNSNULL
;
1904 // Second Pass. Add additional records, if there's space.
1905 newptr
= responseptr
;
1906 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1907 if (rr
->ImmedAdditional
== intf
->InterfaceID
)
1908 if (ResourceRecordIsValidAnswer(rr
))
1910 // If we have at least one answer already in the packet, then plan to add additionals too
1911 mDNSBool SendAdditional
= (m
->omsg
.h
.numAnswers
> 0);
1913 // If we're not planning to send any additionals, but this record is a unique one, then
1914 // make sure we haven't already sent any other members of its RRSet -- if we have, then they
1915 // will have had the cache flush bit set, so now we need to finish the job and send the rest.
1916 if (!SendAdditional
&& (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
))
1918 const AuthRecord
*a
;
1919 for (a
= m
->ResourceRecords
; a
; a
=a
->next
)
1920 if (a
->LastMCTime
== m
->timenow
&&
1921 a
->LastMCInterface
== intf
->InterfaceID
&&
1922 SameResourceRecordSignature(a
, rr
)) { SendAdditional
= mDNStrue
; break; }
1924 if (!SendAdditional
) // If we don't want to send this after all,
1925 rr
->ImmedAdditional
= mDNSNULL
; // then cancel its ImmedAdditional field
1926 else if (newptr
) // Else, try to add it if we can
1928 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1929 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1930 newptr
= PutResourceRecord(&m
->omsg
, newptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
1931 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1934 responseptr
= newptr
;
1935 rr
->ImmedAdditional
= mDNSNULL
;
1936 rr
->RequireGoodbye
= mDNStrue
;
1937 // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
1938 // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
1939 // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
1940 // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
1941 rr
->LastMCTime
= m
->timenow
;
1942 rr
->LastMCInterface
= intf
->InterfaceID
;
1947 if (m
->omsg
.h
.numAnswers
> 0 || m
->omsg
.h
.numAdditionals
)
1949 debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
1950 numDereg
, numDereg
== 1 ? "" : "s",
1951 numAnnounce
, numAnnounce
== 1 ? "" : "s",
1952 numAnswer
, numAnswer
== 1 ? "" : "s",
1953 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s", intf
->InterfaceID
);
1954 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
1955 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
1956 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
1957 if (++pktcount
>= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount
); break; }
1958 // There might be more things to send on this interface, so go around one more time and try again.
1960 else // Nothing more to send on this interface; go to next
1962 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
1963 #if MDNS_DEBUGMSGS && 0
1964 const char *const msg
= next
? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
1965 debugf(msg
, intf
, next
);
1972 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
1975 if (m
->CurrentRecord
)
1976 LogMsg("SendResponses ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
1977 m
->CurrentRecord
= m
->ResourceRecords
;
1978 while (m
->CurrentRecord
)
1980 rr
= m
->CurrentRecord
;
1981 m
->CurrentRecord
= rr
->next
;
1985 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
1986 LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m
, rr
));
1987 rr
->SendRNow
= mDNSNULL
;
1990 if (rr
->ImmedAnswer
)
1992 if (rr
->NewRData
) CompleteRDataUpdate(m
,rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
1994 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
1995 CompleteDeregistration(m
, rr
); // Don't touch rr after this
1998 rr
->ImmedAnswer
= mDNSNULL
;
1999 rr
->ImmedUnicast
= mDNSfalse
;
2000 rr
->v4Requester
= zerov4Addr
;
2001 rr
->v6Requester
= zerov6Addr
;
2005 verbosedebugf("SendResponses: Next in %ld ticks", m
->NextScheduledResponse
- m
->timenow
);
2008 // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
2009 // so we want to be lazy about how frequently we do it.
2010 // 1. If a cache record is currently referenced by *no* active questions,
2011 // then we don't mind expiring it up to a minute late (who will know?)
2012 // 2. Else, if a cache record is due for some of its final expiration queries,
2013 // we'll allow them to be late by up to 2% of the TTL
2014 // 3. Else, if a cache record has completed all its final expiration queries without success,
2015 // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
2016 // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
2017 // so allow at most 1/10 second lateness
2018 #define CacheCheckGracePeriod(RR) ( \
2019 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
2020 ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \
2021 ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \
2022 ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10))
2024 // Note: MUST call SetNextCacheCheckTime any time we change:
2026 // rr->resrec.rroriginalttl
2027 // rr->UnansweredQueries
2028 // rr->CRActiveQuestion
2029 // Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary
2030 // Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime
2031 mDNSlocal
void SetNextCacheCheckTime(mDNS
*const m
, CacheRecord
*const rr
)
2033 rr
->NextRequiredQuery
= RRExpireTime(rr
);
2035 // If we have an active question, then see if we want to schedule a refresher query for this record.
2036 // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
2037 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
2039 rr
->NextRequiredQuery
-= TicksTTL(rr
)/20 * (MaxUnansweredQueries
- rr
->UnansweredQueries
);
2040 rr
->NextRequiredQuery
+= mDNSRandom((mDNSu32
)TicksTTL(rr
)/50);
2041 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks",
2042 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
),
2043 (rr
->NextRequiredQuery
- m
->timenow
) / mDNSPlatformOneSecond
, CacheCheckGracePeriod(rr
));
2046 if (m
->NextCacheCheck
- (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
)) > 0)
2047 m
->NextCacheCheck
= (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
));
2049 if (rr
->DelayDelivery
)
2050 if (m
->NextCacheCheck
- rr
->DelayDelivery
> 0)
2051 m
->NextCacheCheck
= rr
->DelayDelivery
;
2054 #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
2055 #define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5)
2056 #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 5)
2057 #define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30)
2059 mDNSlocal mStatus
mDNS_Reconfirm_internal(mDNS
*const m
, CacheRecord
*const rr
, mDNSu32 interval
)
2061 if (interval
< kMinimumReconfirmTime
)
2062 interval
= kMinimumReconfirmTime
;
2063 if (interval
> 0x10000000) // Make sure interval doesn't overflow when we multiply by four below
2064 interval
= 0x10000000;
2066 // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
2067 if (RRExpireTime(rr
) - m
->timenow
> (mDNSs32
)((interval
* 4) / 3))
2069 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
2070 // For all the reconfirmations in a given batch, we want to use the same random value
2071 // so that the reconfirmation questions can be grouped into a single query packet
2072 if (!m
->RandomReconfirmDelay
) m
->RandomReconfirmDelay
= 1 + mDNSRandom(0x3FFFFFFF);
2073 interval
+= mDNSRandomFromFixedSeed(m
->RandomReconfirmDelay
, interval
/3);
2074 rr
->TimeRcvd
= m
->timenow
- (mDNSs32
)interval
* 3;
2075 rr
->resrec
.rroriginalttl
= (interval
* 4 + mDNSPlatformOneSecond
- 1) / mDNSPlatformOneSecond
;
2076 SetNextCacheCheckTime(m
, rr
);
2078 debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s %p",
2079 RRExpireTime(rr
) - m
->timenow
, CRDisplayString(m
, rr
), rr
->CRActiveQuestion
);
2080 return(mStatus_NoError
);
2083 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
2085 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
2086 // It also appends to the list of known answer records that need to be included,
2087 // and updates the forcast for the size of the known answer section.
2088 mDNSlocal mDNSBool
BuildQuestion(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
, DNSQuestion
*q
,
2089 CacheRecord
***kalistptrptr
, mDNSu32
*answerforecast
)
2091 mDNSBool ucast
= (q
->LargeAnswers
|| q
->RequestUnicast
) && m
->CanReceiveUnicastOn5353
;
2092 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
2093 const mDNSu8
*const limit
= query
->data
+ NormalMaxDNSMessageData
;
2094 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &q
->qname
, q
->qtype
, (mDNSu16
)(q
->qclass
| ucbit
));
2097 debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
2100 else if (newptr
+ *answerforecast
>= limit
)
2102 verbosedebugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
2103 q
->qname
.c
, DNSTypeName(q
->qtype
), newptr
+ *answerforecast
- query
->data
);
2104 query
->h
.numQuestions
--;
2109 mDNSu32 forecast
= *answerforecast
;
2110 const mDNSu32 slot
= HashSlot(&q
->qname
);
2111 const CacheGroup
*const cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
2113 CacheRecord
**ka
= *kalistptrptr
; // Make a working copy of the pointer we're going to update
2115 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
2116 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
2117 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not already in the known answer list
2118 rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
2119 SameNameRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
2120 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
> // and its half-way-to-expiry time is at least 1 second away
2121 mDNSPlatformOneSecond
) // (also ensures we never include goodbye records with TTL=1)
2123 *ka
= rr
; // Link this record into our known answer chain
2124 ka
= &rr
->NextInKAList
;
2125 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
2126 forecast
+= 12 + rr
->resrec
.rdestimate
;
2127 // If we're trying to put more than one question in this packet, and it doesn't fit
2128 // then undo that last question and try again next time
2129 if (query
->h
.numQuestions
> 1 && newptr
+ forecast
>= limit
)
2131 debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
2132 q
->qname
.c
, DNSTypeName(q
->qtype
), newptr
+ forecast
- query
->data
);
2133 query
->h
.numQuestions
--;
2134 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
2135 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
2136 return(mDNSfalse
); // Return false, so we'll try again in the next packet
2140 // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
2141 *queryptr
= newptr
; // Update the packet pointer
2142 *answerforecast
= forecast
; // Update the forecast
2143 *kalistptrptr
= ka
; // Update the known answer list pointer
2144 if (ucast
) q
->ExpectUnicastResp
= NonZeroTime(m
->timenow
);
2146 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // For every resource record in our cache,
2147 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
2148 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not in the known answer list
2149 SameNameRecordAnswersQuestion(&rr
->resrec
, q
)) // which answers our question
2151 rr
->UnansweredQueries
++; // indicate that we're expecting a response
2152 rr
->LastUnansweredTime
= m
->timenow
;
2153 SetNextCacheCheckTime(m
, rr
);
2160 // When we have a query looking for a specified name, but there appear to be no answers with
2161 // that name, ReconfirmAntecedents() is called with depth=0 to start the reconfirmation process
2162 // for any records in our cache that reference the given name (e.g. PTR and SRV records).
2163 // For any such cache record we find, we also recursively call ReconfirmAntecedents() for *its* name.
2164 // We increment depth each time we recurse, to guard against possible infinite loops, with a limit of 5.
2165 // A typical reconfirmation scenario might go like this:
2166 // Depth 0: Name "myhost.local" has no address records
2167 // Depth 1: SRV "My Service._example._tcp.local." refers to "myhost.local"; may be stale
2168 // Depth 2: PTR "_example._tcp.local." refers to "My Service"; may be stale
2169 // Depth 3: PTR "_services._dns-sd._udp.local." refers to "_example._tcp.local."; may be stale
2170 // Currently depths 4 and 5 are not expected to occur; if we did get to depth 5 we'd reconfim any records we
2171 // found referring to the given name, but not recursively descend any further reconfirm *their* antecedents.
2172 mDNSlocal
void ReconfirmAntecedents(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const int depth
)
2177 debugf("ReconfirmAntecedents (depth=%d) for %##s", depth
, name
->c
);
2178 FORALL_CACHERECORDS(slot
, cg
, cr
)
2180 domainname
*crtarget
= GetRRDomainNameTarget(&cr
->resrec
);
2181 if (crtarget
&& cr
->resrec
.rdatahash
== namehash
&& SameDomainName(crtarget
, name
))
2183 LogOperation("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth
, CRDisplayString(m
, cr
));
2184 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
2185 if (depth
< 5) ReconfirmAntecedents(m
, cr
->resrec
.name
, cr
->resrec
.namehash
, depth
+1);
2190 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
2191 mDNSlocal
void ExpireDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
)
2194 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
2197 mDNSlocal
void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
, mDNSInterfaceID InterfaceID
)
2200 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
2203 mDNSlocal mDNSBool
SuppressOnThisInterface(const DupSuppressInfo ds
[DupSuppressInfoSize
], const NetworkInterfaceInfo
* const intf
)
2206 mDNSBool v4
= !intf
->IPv4Available
; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
2207 mDNSBool v6
= !intf
->IPv6Available
; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
2208 for (i
=0; i
<DupSuppressInfoSize
; i
++)
2209 if (ds
[i
].InterfaceID
== intf
->InterfaceID
)
2211 if (ds
[i
].Type
== mDNSAddrType_IPv4
) v4
= mDNStrue
;
2212 else if (ds
[i
].Type
== mDNSAddrType_IPv6
) v6
= mDNStrue
;
2213 if (v4
&& v6
) return(mDNStrue
);
2218 mDNSlocal
int RecordDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 Time
, mDNSInterfaceID InterfaceID
, mDNSs32 Type
)
2222 // See if we have this one in our list somewhere already
2223 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Type
== Type
) break;
2225 // If not, find a slot we can re-use
2226 if (i
>= DupSuppressInfoSize
)
2229 for (j
=1; j
<DupSuppressInfoSize
&& ds
[i
].InterfaceID
; j
++)
2230 if (!ds
[j
].InterfaceID
|| ds
[j
].Time
- ds
[i
].Time
< 0)
2234 // Record the info about this query we saw
2236 ds
[i
].InterfaceID
= InterfaceID
;
2242 mDNSlocal mDNSBool
AccelerateThisQuery(mDNS
*const m
, DNSQuestion
*q
)
2244 // If more than 90% of the way to the query time, we should unconditionally accelerate it
2245 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/10))
2248 // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
2249 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/2))
2251 // We forecast: qname (n) type (2) class (2)
2252 mDNSu32 forecast
= (mDNSu32
)DomainNameLength(&q
->qname
) + 4;
2253 const mDNSu32 slot
= HashSlot(&q
->qname
);
2254 const CacheGroup
*const cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
2256 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
2257 if (rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
2258 SameNameRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
2259 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
>= 0 && // and it is less than half-way to expiry
2260 rr
->NextRequiredQuery
- (m
->timenow
+ q
->ThisQInterval
) > 0)// and we'll ask at least once again before NextRequiredQuery
2262 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
2263 forecast
+= 12 + rr
->resrec
.rdestimate
;
2264 if (forecast
>= 512) return(mDNSfalse
); // If this would add 512 bytes or more to the packet, don't accelerate
2272 // How Standard Queries are generated:
2273 // 1. The Question Section contains the question
2274 // 2. The Additional Section contains answers we already know, to suppress duplicate responses
2276 // How Probe Queries are generated:
2277 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
2278 // if some other host is already using *any* records with this name, we want to know about it.
2279 // 2. The Authority Section contains the proposed values we intend to use for one or more
2280 // of our records with that name (analogous to the Update section of DNS Update packets)
2281 // because if some other host is probing at the same time, we each want to know what the other is
2282 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
2284 mDNSlocal
void SendQueries(mDNS
*const m
)
2292 // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
2293 mDNSs32 maxExistingQuestionInterval
= 0;
2294 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
2295 CacheRecord
*KnownAnswerList
= mDNSNULL
;
2297 // 1. If time for a query, work out what we need to do
2298 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
2302 // We're expecting to send a query anyway, so see if any expiring cache records are close enough
2303 // to their NextRequiredQuery to be worth batching them together with this one
2304 FORALL_CACHERECORDS(slot
, cg
, rr
)
2305 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
2306 if (m
->timenow
+ TicksTTL(rr
)/50 - rr
->NextRequiredQuery
>= 0)
2308 LogOperation("Sending %d%% cache expiration query for %s", 80 + 5 * rr
->UnansweredQueries
, CRDisplayString(m
, rr
));
2309 q
= rr
->CRActiveQuestion
;
2310 ExpireDupSuppressInfoOnInterface(q
->DupSuppress
, m
->timenow
- TicksTTL(rr
)/20, rr
->resrec
.InterfaceID
);
2311 if (q
->Target
.type
) q
->SendQNow
= mDNSInterfaceMark
; // If targeted query, mark it
2312 else if (!mDNSOpaque16IsZero(q
->TargetQID
)) q
->LastQTime
= m
->timenow
- q
->ThisQInterval
; // For uDNS, adjust LastQTime
2313 else if (q
->SendQNow
== mDNSNULL
) q
->SendQNow
= rr
->resrec
.InterfaceID
;
2314 else if (q
->SendQNow
!= rr
->resrec
.InterfaceID
) q
->SendQNow
= mDNSInterfaceMark
;
2317 if (m
->SuppressStdPort53Queries
&& m
->timenow
- m
->SuppressStdPort53Queries
>= 0)
2318 m
->SuppressStdPort53Queries
= 0; // If suppression time has passed, clear it
2320 // Scan our list of questions to see which:
2321 // *WideArea* queries need to be sent
2322 // *unicast* queries need to be sent
2323 // *multicast* queries we're definitely going to send
2324 if (m
->CurrentQuestion
)
2325 LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2326 m
->CurrentQuestion
= m
->Questions
;
2327 while (m
->CurrentQuestion
)
2329 q
= m
->CurrentQuestion
;
2330 if (ActiveQuestion(q
) && !mDNSOpaque16IsZero(q
->TargetQID
)) uDNS_CheckCurrentQuestion(m
);
2331 else if (mDNSOpaque16IsZero(q
->TargetQID
) && q
->Target
.type
&& (q
->SendQNow
|| TimeToSendThisQuestion(q
, m
->timenow
)))
2333 mDNSu8
*qptr
= m
->omsg
.data
;
2334 const mDNSu8
*const limit
= m
->omsg
.data
+ sizeof(m
->omsg
.data
);
2335 InitializeDNSMessage(&m
->omsg
.h
, q
->TargetQID
, QueryFlags
);
2336 qptr
= putQuestion(&m
->omsg
, qptr
, limit
, &q
->qname
, q
->qtype
, q
->qclass
);
2337 mDNSSendDNSMessage(m
, &m
->omsg
, qptr
, mDNSInterface_Any
, &q
->Target
, q
->TargetPort
, mDNSNULL
, mDNSNULL
);
2338 q
->ThisQInterval
*= QuestionIntervalStep
;
2339 if (q
->ThisQInterval
> MaxQuestionInterval
)
2340 q
->ThisQInterval
= MaxQuestionInterval
;
2341 q
->LastQTime
= m
->timenow
;
2342 q
->LastQTxTime
= m
->timenow
;
2343 q
->RecentAnswerPkts
= 0;
2344 q
->SendQNow
= mDNSNULL
;
2345 q
->ExpectUnicastResp
= NonZeroTime(m
->timenow
);
2347 else if (mDNSOpaque16IsZero(q
->TargetQID
) && !q
->Target
.type
&& TimeToSendThisQuestion(q
, m
->timenow
))
2349 //LogOperation("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - (q->LastQTime + q->ThisQInterval));
2350 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
2351 if (maxExistingQuestionInterval
< q
->ThisQInterval
)
2352 maxExistingQuestionInterval
= q
->ThisQInterval
;
2354 // If m->CurrentQuestion wasn't modified out from under us, advance it now
2355 // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having
2356 // m->CurrentQuestion point to the right question
2357 if (q
== m
->CurrentQuestion
) m
->CurrentQuestion
= m
->CurrentQuestion
->next
;
2360 // Scan our list of questions
2361 // (a) to see if there are any more that are worth accelerating, and
2362 // (b) to update the state variables for *all* the questions we're going to send
2363 // Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list,
2364 // which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very
2365 // next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value.
2366 m
->NextScheduledQuery
= m
->timenow
+ 0x78000000;
2367 for (q
= m
->Questions
; q
; q
=q
->next
)
2369 if (mDNSOpaque16IsZero(q
->TargetQID
) && (q
->SendQNow
||
2370 (!q
->Target
.type
&& ActiveQuestion(q
) && q
->ThisQInterval
<= maxExistingQuestionInterval
&& AccelerateThisQuery(m
,q
))))
2372 // If at least halfway to next query time, advance to next interval
2373 // If less than halfway to next query time, then
2374 // treat this as logically a repeat of the last transmission, without advancing the interval
2375 if (m
->timenow
- (q
->LastQTime
+ q
->ThisQInterval
/2) >= 0)
2377 //LogOperation("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - (q->LastQTime + q->ThisQInterval));
2378 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
2379 q
->ThisQInterval
*= QuestionIntervalStep
;
2380 if (q
->ThisQInterval
> MaxQuestionInterval
)
2381 q
->ThisQInterval
= MaxQuestionInterval
;
2382 else if (q
->CurrentAnswers
== 0 && q
->ThisQInterval
== InitialQuestionInterval
* QuestionIntervalStep2
)
2384 // Generally don't need to log this.
2385 // It's not especially noteworthy if a query finds no results -- this usually happens for domain
2386 // enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa")
2387 // and when there simply happen to be no instances of the service the client is looking
2388 // for (e.g. iTunes is set to look for RAOP devices, and the current network has none).
2389 debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents",
2390 q
->qname
.c
, DNSTypeName(q
->qtype
));
2391 // Sending third query, and no answers yet; time to begin doubting the source
2392 ReconfirmAntecedents(m
, &q
->qname
, q
->qnamehash
, 0);
2396 // Mark for sending. (If no active interfaces, then don't even try.)
2397 q
->SendOnAll
= (q
->SendQNow
== mDNSInterfaceMark
);
2400 q
->SendQNow
= !intf
? mDNSNULL
: (q
->InterfaceID
) ? q
->InterfaceID
: intf
->InterfaceID
;
2401 q
->LastQTime
= m
->timenow
;
2404 // If we recorded a duplicate suppression for this question less than half an interval ago,
2405 // then we consider it recent enough that we don't need to do an identical query ourselves.
2406 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
- q
->ThisQInterval
/2);
2408 q
->LastQTxTime
= m
->timenow
;
2409 q
->RecentAnswerPkts
= 0;
2410 if (q
->RequestUnicast
) q
->RequestUnicast
--;
2412 // For all questions (not just the ones we're sending) check what the next scheduled event will be
2413 SetNextQueryTime(m
,q
);
2417 // 2. Scan our authoritative RR list to see what probes we might need to send
2418 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
2420 m
->NextScheduledProbe
= m
->timenow
+ 0x78000000;
2422 if (m
->CurrentRecord
)
2423 LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
2424 m
->CurrentRecord
= m
->ResourceRecords
;
2425 while (m
->CurrentRecord
)
2427 AuthRecord
*rr
= m
->CurrentRecord
;
2428 m
->CurrentRecord
= rr
->next
;
2429 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
) // For all records that are still probing...
2431 // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
2432 if (m
->timenow
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) < 0)
2434 SetNextAnnounceProbeTime(m
, rr
);
2436 // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
2437 else if (rr
->ProbeCount
)
2439 // Mark for sending. (If no active interfaces, then don't even try.)
2440 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
2441 rr
->LastAPTime
= m
->timenow
;
2443 SetNextAnnounceProbeTime(m
, rr
);
2444 if (rr
->ProbeCount
== 0)
2446 // If this is the last probe for this record, then see if we have any matching records
2447 // on our duplicate list which should similarly have their ProbeCount cleared to zero...
2449 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
)
2450 if (r2
->resrec
.RecordType
== kDNSRecordTypeUnique
&& RecordIsLocalDuplicate(r2
, rr
))
2452 // ... then acknowledge this record to the client.
2453 // We do this optimistically, just as we're about to send the third probe.
2454 // This helps clients that both advertise and browse, and want to filter themselves
2455 // from the browse results list, because it helps ensure that the registration
2456 // confirmation will be delivered 1/4 second *before* the browse "add" event.
2457 // A potential downside is that we could deliver a registration confirmation and then find out
2458 // moments later that there's a name conflict, but applications have to be prepared to handle
2459 // late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new.
2460 if (!rr
->Acknowledged
) AcknowledgeRecord(m
, rr
);
2463 // else, if it has now finished probing, move it to state Verified,
2464 // and update m->NextScheduledResponse so it will be announced
2467 if (!rr
->Acknowledged
) AcknowledgeRecord(m
, rr
); // Defensive, just in case it got missed somehow
2468 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
2469 rr
->ThisAPInterval
= DefaultAnnounceIntervalForTypeUnique
;
2470 rr
->LastAPTime
= m
->timenow
- DefaultAnnounceIntervalForTypeUnique
;
2471 SetNextAnnounceProbeTime(m
, rr
);
2475 m
->CurrentRecord
= m
->DuplicateRecords
;
2476 while (m
->CurrentRecord
)
2478 AuthRecord
*rr
= m
->CurrentRecord
;
2479 m
->CurrentRecord
= rr
->next
;
2480 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& rr
->ProbeCount
== 0 && !rr
->Acknowledged
)
2481 AcknowledgeRecord(m
, rr
);
2485 // 3. Now we know which queries and probes we're sending,
2486 // go through our interface list sending the appropriate queries on each interface
2490 mDNSu8
*queryptr
= m
->omsg
.data
;
2491 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, QueryFlags
);
2492 if (KnownAnswerList
) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet");
2493 if (!KnownAnswerList
)
2495 // Start a new known-answer list
2496 CacheRecord
**kalistptr
= &KnownAnswerList
;
2497 mDNSu32 answerforecast
= 0;
2499 // Put query questions in this packet
2500 for (q
= m
->Questions
; q
; q
=q
->next
)
2502 if (mDNSOpaque16IsZero(q
->TargetQID
) && (q
->SendQNow
== intf
->InterfaceID
))
2504 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
2505 SuppressOnThisInterface(q
->DupSuppress
, intf
) ? "Suppressing" : "Putting ",
2506 q
->qname
.c
, DNSTypeName(q
->qtype
), queryptr
- m
->omsg
.data
, queryptr
+ answerforecast
- m
->omsg
.data
);
2507 // If we're suppressing this question, or we successfully put it, update its SendQNow state
2508 if (SuppressOnThisInterface(q
->DupSuppress
, intf
) ||
2509 BuildQuestion(m
, &m
->omsg
, &queryptr
, q
, &kalistptr
, &answerforecast
))
2510 q
->SendQNow
= (q
->InterfaceID
|| !q
->SendOnAll
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
2514 // Put probe questions in this packet
2515 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2516 if (rr
->SendRNow
== intf
->InterfaceID
)
2518 mDNSBool ucast
= (rr
->ProbeCount
>= DefaultProbeCountForTypeUnique
-1) && m
->CanReceiveUnicastOn5353
;
2519 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
2520 const mDNSu8
*const limit
= m
->omsg
.data
+ ((m
->omsg
.h
.numQuestions
) ? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
2521 mDNSu8
*newptr
= putQuestion(&m
->omsg
, queryptr
, limit
, rr
->resrec
.name
, kDNSQType_ANY
, (mDNSu16
)(rr
->resrec
.rrclass
| ucbit
));
2522 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
2523 mDNSu32 forecast
= answerforecast
+ 12 + rr
->resrec
.rdestimate
;
2524 if (newptr
&& newptr
+ forecast
< limit
)
2527 answerforecast
= forecast
;
2528 rr
->SendRNow
= (rr
->resrec
.InterfaceID
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
2529 rr
->IncludeInProbe
= mDNStrue
;
2530 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d",
2531 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->ProbeCount
);
2535 verbosedebugf("SendQueries: Retracting Question %##s (%s)",
2536 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2537 m
->omsg
.h
.numQuestions
--;
2542 // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
2543 while (KnownAnswerList
)
2545 CacheRecord
*rr
= KnownAnswerList
;
2546 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
2547 mDNSu8
*newptr
= PutResourceRecordTTL(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, rr
->resrec
.rroriginalttl
- SecsSinceRcvd
);
2550 verbosedebugf("SendQueries: Put %##s (%s) at %d - %d",
2551 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), queryptr
- m
->omsg
.data
, newptr
- m
->omsg
.data
);
2553 KnownAnswerList
= rr
->NextInKAList
;
2554 rr
->NextInKAList
= mDNSNULL
;
2558 // If we ran out of space and we have more than one question in the packet, that's an error --
2559 // we shouldn't have put more than one question if there was a risk of us running out of space.
2560 if (m
->omsg
.h
.numQuestions
> 1)
2561 LogMsg("SendQueries: Put %d answers; No more space for known answers", m
->omsg
.h
.numAnswers
);
2562 m
->omsg
.h
.flags
.b
[0] |= kDNSFlag0_TC
;
2567 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2568 if (rr
->IncludeInProbe
)
2570 mDNSu8
*newptr
= PutResourceRecord(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAuthorities
, &rr
->resrec
);
2571 rr
->IncludeInProbe
= mDNSfalse
;
2572 if (newptr
) queryptr
= newptr
;
2573 else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?",
2574 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2577 if (queryptr
> m
->omsg
.data
)
2579 if ((m
->omsg
.h
.flags
.b
[0] & kDNSFlag0_TC
) && m
->omsg
.h
.numQuestions
> 1)
2580 LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m
->omsg
.h
.numQuestions
);
2581 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
2582 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
2583 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
2584 m
->omsg
.h
.numAuthorities
, m
->omsg
.h
.numAuthorities
== 1 ? "" : "s", intf
->InterfaceID
);
2585 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
2586 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
2587 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
2588 if (++pktcount
>= 1000)
2589 { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount
); break; }
2590 // There might be more records left in the known answer list, or more questions to send
2591 // on this interface, so go around one more time and try again.
2593 else // Nothing more to send on this interface; go to next
2595 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
2596 #if MDNS_DEBUGMSGS && 0
2597 const char *const msg
= next
? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p";
2598 debugf(msg
, intf
, next
);
2604 // 4. Final housekeeping
2606 // 4a. Debugging check: Make sure we announced all our records
2607 for (ar
= m
->ResourceRecords
; ar
; ar
=ar
->next
)
2610 if (ar
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
2611 LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m
, ar
));
2612 ar
->SendRNow
= mDNSNULL
;
2615 // 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope
2616 // that their interface which went away might come back again, the logic will want to send queries
2617 // for those records, but we can't because their interface isn't here any more, so to keep the
2618 // state machine ticking over we just pretend we did so.
2619 // If the interface does not come back in time, the cache record will expire naturally
2620 FORALL_CACHERECORDS(slot
, cg
, cr
)
2621 if (cr
->CRActiveQuestion
&& cr
->UnansweredQueries
< MaxUnansweredQueries
&& m
->timenow
- cr
->NextRequiredQuery
>= 0)
2623 cr
->UnansweredQueries
++;
2624 cr
->CRActiveQuestion
->SendQNow
= mDNSNULL
;
2625 SetNextCacheCheckTime(m
, cr
);
2628 // 4c. Debugging check: Make sure we sent all our planned questions
2629 // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions
2630 // we legitimately couldn't send because the interface is no longer available
2631 for (q
= m
->Questions
; q
; q
=q
->next
)
2634 LogMsg("SendQueries: No active interface to send: %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
2635 q
->SendQNow
= mDNSNULL
;
2639 // ***************************************************************************
2640 #if COMPILER_LIKES_PRAGMA_MARK
2642 #pragma mark - RR List Management & Task Management
2645 // NOTE: AnswerCurrentQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
2646 // Any code walking either list must use the m->CurrentQuestion (and possibly m->CurrentRecord) mechanism to protect against this.
2647 // In fact, to enforce this, the routine will *only* answer the question currently pointed to by m->CurrentQuestion,
2648 // which will be auto-advanced (possibly to NULL) if the client callback cancels the question.
2649 mDNSexport
void AnswerCurrentQuestionWithResourceRecord(mDNS
*const m
, CacheRecord
*const rr
, const QC_result AddRecord
)
2651 DNSQuestion
*const q
= m
->CurrentQuestion
;
2652 mDNSBool followcname
= rr
->resrec
.RecordType
!= kDNSRecordTypePacketNegative
&& AddRecord
&&
2653 rr
->resrec
.rrtype
== kDNSType_CNAME
&& q
->qtype
!= kDNSType_CNAME
;
2654 verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", q
->CurrentAnswers
, AddRecord
? "Add" : "Rmv", rr
->resrec
.rroriginalttl
, CRDisplayString(m
, rr
));
2656 // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue)
2657 // may be called twice, once when the record is received, and again when it's time to notify local clients.
2658 // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
2660 rr
->LastUsed
= m
->timenow
;
2661 if (AddRecord
== QC_add
&& !q
->DuplicateOf
&& rr
->CRActiveQuestion
!= q
)
2663 if (!rr
->CRActiveQuestion
) m
->rrcache_active
++; // If not previously active, increment rrcache_active count
2664 debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion to %p for cache record %s", q
, CRDisplayString(m
,rr
));
2665 rr
->CRActiveQuestion
= q
; // We know q is non-null
2666 SetNextCacheCheckTime(m
, rr
);
2670 // (a) a no-cache add, where we've already done at least one 'QM' query, or
2671 // (b) a normal add, where we have at least one unique-type answer,
2672 // then there's no need to keep polling the network.
2673 // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
2674 // We do this for mDNS questions and uDNS one-shot questions, but not for
2675 // uDNS LongLived questions, because that would mess up our LLQ lease renewal timing.
2676 if ((AddRecord
== QC_addnocache
&& !q
->RequestUnicast
) ||
2677 (AddRecord
== QC_add
&& (q
->ExpectUnique
|| (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))))
2678 if (ActiveQuestion(q
) && (mDNSOpaque16IsZero(q
->TargetQID
) || !q
->LongLived
))
2680 q
->LastQTime
= m
->timenow
;
2681 q
->LastQTxTime
= m
->timenow
;
2682 q
->RecentAnswerPkts
= 0;
2683 q
->ThisQInterval
= MaxQuestionInterval
;
2684 q
->RequestUnicast
= mDNSfalse
;
2687 if (rr
->DelayDelivery
) return; // We'll come back later when CacheRecordDeferredAdd() calls us
2689 // Only deliver negative answers if client has explicitly requested them
2690 if (rr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
&& (!AddRecord
|| !q
->ReturnIntermed
)) return;
2692 // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that
2693 if (q
->QuestionCallback
&& !q
->NoAnswer
&& (!followcname
|| q
->ReturnIntermed
))
2695 mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls
2696 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
2697 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
2699 // NOTE: Proceed with caution here because client callback function is allowed to do anything,
2700 // including starting/stopping queries, registering/deregistering records, etc.
2702 if (followcname
&& m
->CurrentQuestion
== q
&& q
->CNAMEReferrals
< 10)
2704 const mDNSu32 c
= q
->CNAMEReferrals
+ 1;
2705 // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
2706 // and track CNAMEs coming and going, we should really create a subbordinate query here,
2707 // which we would subsequently cancel and retract if the CNAME referral record were removed.
2708 // In reality this is such a corner case we'll ignore it until someone actually needs it.
2709 LogOperation("AnswerCurrentQuestionWithResourceRecord: following CNAME referral for %s", CRDisplayString(m
, rr
));
2710 mDNS_StopQuery_internal(m
, q
); // Stop old query
2711 AssignDomainName(&q
->qname
, &rr
->resrec
.rdata
->u
.name
); // Update qname
2712 q
->qnamehash
= DomainNameHashValue(&q
->qname
); // and namehash
2713 mDNS_StartQuery_internal(m
, q
); // start new query
2714 q
->CNAMEReferrals
= c
; // and keep count of how many times we've done this
2718 mDNSlocal
void CacheRecordDeferredAdd(mDNS
*const m
, CacheRecord
*rr
)
2720 rr
->DelayDelivery
= 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
2721 if (m
->CurrentQuestion
)
2722 LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2723 m
->CurrentQuestion
= m
->Questions
;
2724 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2726 DNSQuestion
*q
= m
->CurrentQuestion
;
2727 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2728 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_add
);
2729 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2730 m
->CurrentQuestion
= q
->next
;
2732 m
->CurrentQuestion
= mDNSNULL
;
2735 mDNSlocal mDNSs32
CheckForSoonToExpireRecords(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const mDNSu32 slot
)
2737 const mDNSs32 threshhold
= m
->timenow
+ mDNSPlatformOneSecond
; // See if there are any records expiring within one second
2738 const mDNSs32 start
= m
->timenow
- 0x10000000;
2739 mDNSs32 delay
= start
;
2740 CacheGroup
*cg
= CacheGroupForName(m
, slot
, namehash
, name
);
2742 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
2743 if (threshhold
- RRExpireTime(rr
) >= 0) // If we have records about to expire within a second
2744 if (delay
- RRExpireTime(rr
) < 0) // then delay until after they've been deleted
2745 delay
= RRExpireTime(rr
);
2746 if (delay
- start
> 0) return(NonZeroTime(delay
));
2750 // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
2751 // If new questions are created as a result of invoking client callbacks, they will be added to
2752 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2753 // rr is a new CacheRecord just received into our cache
2754 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
2755 // NOTE: CacheRecordAdd calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
2756 // which may change the record list and/or question list.
2757 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2758 mDNSlocal
void CacheRecordAdd(mDNS
*const m
, CacheRecord
*rr
)
2761 for (q
= m
->Questions
; q
; q
=q
->next
)
2763 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2765 // If this question is one that's actively sending queries, and it's received ten answers within one
2766 // second of sending the last query packet, then that indicates some radical network topology change,
2767 // so reset its exponential backoff back to the start. We must be at least at the eight-second interval
2768 // to do this. If we're at the four-second interval, or less, there's not much benefit accelerating
2769 // because we will anyway send another query within a few seconds. The first reset query is sent out
2770 // randomized over the next four seconds to reduce possible synchronization between machines.
2771 if (q
->LastAnswerPktNum
!= m
->PktNum
)
2773 q
->LastAnswerPktNum
= m
->PktNum
;
2774 if (mDNSOpaque16IsZero(q
->TargetQID
) && ActiveQuestion(q
) && ++q
->RecentAnswerPkts
>= 10 &&
2775 q
->ThisQInterval
> InitialQuestionInterval
* QuestionIntervalStep3
&& m
->timenow
- q
->LastQTxTime
< mDNSPlatformOneSecond
)
2777 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
2778 q
->qname
.c
, DNSTypeName(q
->qtype
));
2779 q
->LastQTime
= m
->timenow
- InitialQuestionInterval
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*4);
2780 q
->ThisQInterval
= InitialQuestionInterval
;
2781 SetNextQueryTime(m
,q
);
2784 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu",
2785 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->resrec
.rroriginalttl
);
2786 q
->CurrentAnswers
++;
2787 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
2788 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
2789 if (q
->CurrentAnswers
> 4000)
2791 static int msgcount
= 0;
2792 if (msgcount
++ < 10)
2793 LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
2794 q
->qname
.c
, DNSTypeName(q
->qtype
), q
->CurrentAnswers
);
2795 rr
->resrec
.rroriginalttl
= 0;
2796 rr
->UnansweredQueries
= MaxUnansweredQueries
;
2801 if (!rr
->DelayDelivery
)
2803 if (m
->CurrentQuestion
)
2804 LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2805 m
->CurrentQuestion
= m
->Questions
;
2806 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2808 DNSQuestion
*q
= m
->CurrentQuestion
;
2809 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2810 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_add
);
2811 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2812 m
->CurrentQuestion
= q
->next
;
2814 m
->CurrentQuestion
= mDNSNULL
;
2817 SetNextCacheCheckTime(m
, rr
);
2820 // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
2821 // If new questions are created as a result of invoking client callbacks, they will be added to
2822 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2823 // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
2824 // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
2825 // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
2826 // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
2827 // NOTE: NoCacheAnswer calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
2828 // which may change the record list and/or question list.
2829 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2830 mDNSlocal
void NoCacheAnswer(mDNS
*const m
, CacheRecord
*rr
)
2832 LogMsg("No cache space: Delivering non-cached result for %##s", m
->rec
.r
.resrec
.name
->c
);
2833 if (m
->CurrentQuestion
)
2834 LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2835 m
->CurrentQuestion
= m
->Questions
;
2836 while (m
->CurrentQuestion
)
2838 DNSQuestion
*q
= m
->CurrentQuestion
;
2839 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2840 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_addnocache
); // QC_addnocache means "don't expect remove events for this"
2841 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2842 m
->CurrentQuestion
= q
->next
;
2844 m
->CurrentQuestion
= mDNSNULL
;
2847 // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
2848 // If new questions are created as a result of invoking client callbacks, they will be added to
2849 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2850 // rr is an existing cache CacheRecord that just expired and is being deleted
2851 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
2852 // NOTE: CacheRecordRmv calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
2853 // which may change the record list and/or question list.
2854 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2855 mDNSlocal
void CacheRecordRmv(mDNS
*const m
, CacheRecord
*rr
)
2857 if (m
->CurrentQuestion
)
2858 LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2859 m
->CurrentQuestion
= m
->Questions
;
2860 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2862 DNSQuestion
*q
= m
->CurrentQuestion
;
2863 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2865 verbosedebugf("CacheRecordRmv %p %s", rr
, CRDisplayString(m
, rr
));
2866 q
->FlappingInterface1
= mDNSNULL
;
2867 q
->FlappingInterface2
= mDNSNULL
;
2868 if (q
->CurrentAnswers
== 0)
2869 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?",
2870 q
, q
->qname
.c
, DNSTypeName(q
->qtype
));
2873 q
->CurrentAnswers
--;
2874 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
--;
2875 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
--;
2877 if (rr
->resrec
.rdata
->MaxRDLength
) // Never generate "remove" events for negative results
2879 if (q
->CurrentAnswers
== 0)
2881 LogOperation("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents",
2882 q
->qname
.c
, DNSTypeName(q
->qtype
));
2883 ReconfirmAntecedents(m
, &q
->qname
, q
->qnamehash
, 0);
2885 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_rmv
);
2888 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2889 m
->CurrentQuestion
= q
->next
;
2891 m
->CurrentQuestion
= mDNSNULL
;
2894 mDNSlocal
void ReleaseCacheEntity(mDNS
*const m
, CacheEntity
*e
)
2896 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
2898 for (i
=0; i
<sizeof(*e
); i
++) ((char*)e
)[i
] = 0xFF;
2900 e
->next
= m
->rrcache_free
;
2901 m
->rrcache_free
= e
;
2902 m
->rrcache_totalused
--;
2905 mDNSlocal
void ReleaseCacheGroup(mDNS
*const m
, CacheGroup
**cp
)
2907 CacheEntity
*e
= (CacheEntity
*)(*cp
);
2908 //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
2909 if ((*cp
)->rrcache_tail
!= &(*cp
)->members
)
2910 LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
2911 //if ((*cp)->name != (domainname*)((*cp)->namestorage))
2912 // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
2913 if ((*cp
)->name
!= (domainname
*)((*cp
)->namestorage
)) mDNSPlatformMemFree((*cp
)->name
);
2914 (*cp
)->name
= mDNSNULL
;
2915 *cp
= (*cp
)->next
; // Cut record from list
2916 ReleaseCacheEntity(m
, e
);
2919 mDNSlocal
void ReleaseCacheRecord(mDNS
*const m
, CacheRecord
*r
)
2921 //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r));
2922 if (r
->resrec
.rdata
&& r
->resrec
.rdata
!= (RData
*)&r
->rdatastorage
) mDNSPlatformMemFree(r
->resrec
.rdata
);
2923 r
->resrec
.rdata
= mDNSNULL
;
2924 ReleaseCacheEntity(m
, (CacheEntity
*)r
);
2927 // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering
2928 // CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all
2929 // callbacks for old records are delivered before callbacks for newer records.
2930 mDNSlocal
void CheckCacheExpiration(mDNS
*const m
, CacheGroup
*const cg
)
2932 CacheRecord
**rp
= &cg
->members
;
2934 if (m
->lock_rrcache
) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
2935 m
->lock_rrcache
= 1;
2939 CacheRecord
*const rr
= *rp
;
2940 mDNSs32 event
= RRExpireTime(rr
);
2941 if (m
->timenow
- event
>= 0) // If expired, delete it
2943 *rp
= rr
->next
; // Cut it from the list
2944 verbosedebugf("CheckCacheExpiration: Deleting%7d %4d %p %s",
2945 m
->timenow
- rr
->TimeRcvd
, rr
->resrec
.rroriginalttl
, rr
->CRActiveQuestion
, CRDisplayString(m
, rr
));
2946 if (rr
->CRActiveQuestion
) // If this record has one or more active questions, tell them it's going away
2948 CacheRecordRmv(m
, rr
);
2949 m
->rrcache_active
--;
2951 ReleaseCacheRecord(m
, rr
);
2953 else // else, not expired; see if we need to query
2955 if (rr
->DelayDelivery
&& rr
->DelayDelivery
- m
->timenow
> 0)
2956 event
= rr
->DelayDelivery
;
2959 if (rr
->DelayDelivery
) CacheRecordDeferredAdd(m
, rr
);
2960 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
2962 if (m
->timenow
- rr
->NextRequiredQuery
< 0) // If not yet time for next query
2963 event
= rr
->NextRequiredQuery
; // then just record when we want the next query
2964 else // else trigger our question to go out now
2966 // Set NextScheduledQuery to timenow so that SendQueries() will run.
2967 // SendQueries() will see that we have records close to expiration, and send FEQs for them.
2968 m
->NextScheduledQuery
= m
->timenow
;
2969 // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(),
2970 // which will correctly update m->NextCacheCheck for us.
2971 event
= m
->timenow
+ 0x3FFFFFFF;
2975 verbosedebugf("CheckCacheExpiration:%6d %5d %s",
2976 (event
- m
->timenow
) / mDNSPlatformOneSecond
, CacheCheckGracePeriod(rr
), CRDisplayString(m
, rr
));
2977 if (m
->NextCacheCheck
- (event
+ CacheCheckGracePeriod(rr
)) > 0)
2978 m
->NextCacheCheck
= (event
+ CacheCheckGracePeriod(rr
));
2982 if (cg
->rrcache_tail
!= rp
) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg
->rrcache_tail
, rp
);
2983 cg
->rrcache_tail
= rp
;
2984 m
->lock_rrcache
= 0;
2987 mDNSlocal
void AnswerNewQuestion(mDNS
*const m
)
2989 mDNSBool ShouldQueryImmediately
= mDNStrue
;
2991 DNSQuestion
*q
= m
->NewQuestions
; // Grab the question we're going to answer
2992 const mDNSu32 slot
= HashSlot(&q
->qname
);
2993 CacheGroup
*const cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
2995 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
2997 if (cg
) CheckCacheExpiration(m
, cg
);
2998 m
->NewQuestions
= q
->next
; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
3000 if (m
->lock_rrcache
) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
3001 // This should be safe, because calling the client's question callback may cause the
3002 // question list to be modified, but should not ever cause the rrcache list to be modified.
3003 // If the client's question callback deletes the question, then m->CurrentQuestion will
3004 // be advanced, and we'll exit out of the loop
3005 m
->lock_rrcache
= 1;
3006 if (m
->CurrentQuestion
)
3007 LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
3008 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
3010 if (q
->NoAnswer
== NoAnswer_Fail
)
3012 LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
3013 MakeNegativeCacheRecord(m
, &q
->qname
, q
->qnamehash
, q
->qtype
, q
->qclass
, 60);
3014 q
->NoAnswer
= NoAnswer_Normal
; // Temporarily turn off answer suppression
3015 AnswerCurrentQuestionWithResourceRecord(m
, &m
->rec
.r
, QC_addnocache
);
3016 q
->NoAnswer
= NoAnswer_Fail
; // Restore NoAnswer state
3017 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
3020 // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
3021 if (m
->CurrentQuestion
== q
&& q
->InterfaceID
== mDNSInterface_Any
)
3023 if (m
->CurrentRecord
)
3024 LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
3025 m
->CurrentRecord
= m
->ResourceRecords
;
3026 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
3028 AuthRecord
*rr
= m
->CurrentRecord
;
3029 m
->CurrentRecord
= rr
->next
;
3030 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
3031 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3033 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3034 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
3037 m
->CurrentRecord
= mDNSNULL
;
3040 if (m
->CurrentQuestion
== q
)
3042 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3043 if (SameNameRecordAnswersQuestion(&rr
->resrec
, q
))
3045 // SecsSinceRcvd is whole number of elapsed seconds, rounded down
3046 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
3047 if (rr
->resrec
.rroriginalttl
<= SecsSinceRcvd
)
3049 LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s) %d %d",
3050 rr
->resrec
.rroriginalttl
, SecsSinceRcvd
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), m
->timenow
, rr
->TimeRcvd
);
3051 continue; // Go to next one in loop
3054 // If this record set is marked unique, then that means we can reasonably assume we have the whole set
3055 // -- we don't need to rush out on the network and query immediately to see if there are more answers out there
3056 if ((rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) || (q
->ExpectUnique
))
3057 ShouldQueryImmediately
= mDNSfalse
;
3058 q
->CurrentAnswers
++;
3059 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
3060 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
3061 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_add
);
3062 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
3064 else if (RRTypeIsAddressType(rr
->resrec
.rrtype
) && RRTypeIsAddressType(q
->qtype
))
3065 if (rr
->resrec
.namehash
== q
->qnamehash
&& SameDomainName(rr
->resrec
.name
, &q
->qname
))
3066 ShouldQueryImmediately
= mDNSfalse
;
3069 if (m
->CurrentQuestion
== q
&& ShouldQueryImmediately
&& ActiveQuestion(q
))
3071 q
->ThisQInterval
= InitialQuestionInterval
;
3072 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
3073 if (mDNSOpaque16IsZero(q
->TargetQID
))
3075 // Compute random delay in the range 1-6 seconds, then divide by 50 to get 20-120ms
3076 if (!m
->RandomQueryDelay
)
3077 m
->RandomQueryDelay
= (mDNSPlatformOneSecond
+ mDNSRandom(mDNSPlatformOneSecond
*5) - 1) / 50 + 1;
3078 q
->LastQTime
+= m
->RandomQueryDelay
;
3081 m
->NextScheduledQuery
= m
->timenow
;
3084 m
->CurrentQuestion
= mDNSNULL
;
3085 m
->lock_rrcache
= 0;
3088 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any
3089 // appropriate answers, stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions
3090 mDNSlocal
void AnswerNewLocalOnlyQuestion(mDNS
*const m
)
3092 DNSQuestion
*q
= m
->NewLocalOnlyQuestions
; // Grab the question we're going to answer
3093 m
->NewLocalOnlyQuestions
= q
->next
; // Advance NewQuestions to the next (if any)
3095 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
3097 if (m
->CurrentQuestion
)
3098 LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
3099 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
3101 if (m
->CurrentRecord
)
3102 LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
3103 m
->CurrentRecord
= m
->ResourceRecords
;
3105 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
3107 AuthRecord
*rr
= m
->CurrentRecord
;
3108 m
->CurrentRecord
= rr
->next
;
3109 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3111 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3112 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
3116 m
->CurrentQuestion
= mDNSNULL
;
3117 m
->CurrentRecord
= mDNSNULL
;
3120 mDNSlocal CacheEntity
*GetCacheEntity(mDNS
*const m
, const CacheGroup
*const PreserveCG
)
3122 CacheEntity
*e
= mDNSNULL
;
3124 if (m
->lock_rrcache
) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL
); }
3125 m
->lock_rrcache
= 1;
3127 // If we have no free records, ask the client layer to give us some more memory
3128 if (!m
->rrcache_free
&& m
->MainCallback
)
3130 if (m
->rrcache_totalused
!= m
->rrcache_size
)
3131 LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
3132 m
->rrcache_totalused
, m
->rrcache_size
);
3134 // We don't want to be vulnerable to a malicious attacker flooding us with an infinite
3135 // number of bogus records so that we keep growing our cache until the machine runs out of memory.
3136 // To guard against this, if our cache grows above 512kB (approx 3168 records at 164 bytes each),
3137 // and we're actively using less than 1/32 of that cache, then we purge all the unused records
3138 // and recycle them, instead of allocating more memory.
3139 if (m
->rrcache_size
> 3000 && m
->rrcache_size
/ 32 > m
->rrcache_active
)
3140 LogOperation("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
3141 m
->rrcache_size
, m
->rrcache_active
);
3144 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
3145 m
->MainCallback(m
, mStatus_GrowCache
);
3146 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
3150 // If we still have no free records, recycle all the records we can.
3151 // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
3152 if (!m
->rrcache_free
)
3154 #if LogAllOperations || MDNS_DEBUGMSGS
3155 mDNSu32 oldtotalused
= m
->rrcache_totalused
;
3158 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
3160 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
3163 CacheRecord
**rp
= &(*cp
)->members
;
3166 // Records that answer still-active questions are not candidates for recycling
3167 // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
3168 if ((*rp
)->CRActiveQuestion
|| (*rp
)->NextInCFList
)
3172 CacheRecord
*rr
= *rp
;
3173 *rp
= (*rp
)->next
; // Cut record from list
3174 ReleaseCacheRecord(m
, rr
);
3177 if ((*cp
)->rrcache_tail
!= rp
)
3178 verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot
, (*cp
)->rrcache_tail
, rp
);
3179 (*cp
)->rrcache_tail
= rp
;
3180 if ((*cp
)->members
|| (*cp
)==PreserveCG
) cp
=&(*cp
)->next
;
3181 else ReleaseCacheGroup(m
, cp
);
3184 LogOperation("GetCacheEntity recycled %d records to reduce cache from %d to %d",
3185 oldtotalused
- m
->rrcache_totalused
, oldtotalused
, m
->rrcache_totalused
);
3188 if (m
->rrcache_free
) // If there are records in the free list, take one
3190 e
= m
->rrcache_free
;
3191 m
->rrcache_free
= e
->next
;
3192 if (++m
->rrcache_totalused
>= m
->rrcache_report
)
3194 LogOperation("RR Cache now using %ld objects", m
->rrcache_totalused
);
3195 if (m
->rrcache_report
< 100) m
->rrcache_report
+= 10;
3196 else m
->rrcache_report
+= 100;
3198 mDNSPlatformMemZero(e
, sizeof(*e
));
3201 m
->lock_rrcache
= 0;
3206 mDNSlocal CacheRecord
*GetCacheRecord(mDNS
*const m
, CacheGroup
*cg
, mDNSu16 RDLength
)
3208 CacheRecord
*r
= (CacheRecord
*)GetCacheEntity(m
, cg
);
3211 r
->resrec
.rdata
= (RData
*)&r
->rdatastorage
; // By default, assume we're usually going to be using local storage
3212 if (RDLength
> InlineCacheRDSize
) // If RDLength is too big, allocate extra storage
3214 r
->resrec
.rdata
= (RData
*)mDNSPlatformMemAllocate(sizeofRDataHeader
+ RDLength
);
3215 if (r
->resrec
.rdata
) r
->resrec
.rdata
->MaxRDLength
= r
->resrec
.rdlength
= RDLength
;
3216 else { ReleaseCacheEntity(m
, (CacheEntity
*)r
); r
= mDNSNULL
; }
3222 mDNSlocal CacheGroup
*GetCacheGroup(mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
3224 mDNSu16 namelen
= DomainNameLength(rr
->name
);
3225 CacheGroup
*cg
= (CacheGroup
*)GetCacheEntity(m
, mDNSNULL
);
3226 if (!cg
) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr
->name
->c
); return(mDNSNULL
); }
3227 cg
->next
= m
->rrcache_hash
[slot
];
3228 cg
->namehash
= rr
->namehash
;
3229 cg
->members
= mDNSNULL
;
3230 cg
->rrcache_tail
= &cg
->members
;
3231 cg
->name
= (domainname
*)cg
->namestorage
;
3232 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s",
3233 // (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
3234 if (namelen
> InlineCacheGroupNameSize
) cg
->name
= mDNSPlatformMemAllocate(namelen
);
3237 LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr
->name
->c
);
3238 ReleaseCacheEntity(m
, (CacheEntity
*)cg
);
3241 AssignDomainName(cg
->name
, rr
->name
);
3243 if (CacheGroupForRecord(m
, slot
, rr
)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr
->name
->c
);
3244 m
->rrcache_hash
[slot
] = cg
;
3245 if (CacheGroupForRecord(m
, slot
, rr
) != cg
) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr
->name
->c
);
3250 mDNSexport
void mDNS_PurgeCacheResourceRecord(mDNS
*const m
, CacheRecord
*rr
)
3252 // Make sure we mark this record as thoroughly expired -- we don't ever want to give
3253 // a positive answer using an expired record (e.g. from an interface that has gone away).
3254 // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
3255 // summary deletion without giving the proper callback to any questions that are monitoring it.
3256 // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
3257 rr
->TimeRcvd
= m
->timenow
- mDNSPlatformOneSecond
* 60;
3258 rr
->UnansweredQueries
= MaxUnansweredQueries
;
3259 rr
->resrec
.rroriginalttl
= 0;
3260 SetNextCacheCheckTime(m
, rr
);
3263 mDNSexport mDNSs32
mDNS_TimeNow(const mDNS
*const m
)
3266 mDNSPlatformLock(m
);
3269 LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
3270 if (!m
->timenow
) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
3273 if (m
->timenow
) time
= m
->timenow
;
3274 else time
= mDNS_TimeNow_NoLock(m
);
3275 mDNSPlatformUnlock(m
);
3279 mDNSexport mDNSs32
mDNS_Execute(mDNS
*const m
)
3281 mDNS_Lock(m
); // Must grab lock before trying to read m->timenow
3283 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
3287 verbosedebugf("mDNS_Execute");
3288 if (m
->CurrentQuestion
)
3289 LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
3291 // 1. If we're past the probe suppression time, we can clear it
3292 if (m
->SuppressProbes
&& m
->timenow
- m
->SuppressProbes
>= 0) m
->SuppressProbes
= 0;
3294 // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
3295 if (m
->NumFailedProbes
&& m
->timenow
- m
->ProbeFailTime
>= mDNSPlatformOneSecond
* 10) m
->NumFailedProbes
= 0;
3297 // 3. Purge our cache of stale old records
3298 if (m
->rrcache_size
&& m
->timenow
- m
->NextCacheCheck
>= 0)
3301 m
->NextCacheCheck
= m
->timenow
+ 0x3FFFFFFF;
3302 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
3304 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
3307 CheckCacheExpiration(m
, *cp
);
3308 if ((*cp
)->members
) cp
=&(*cp
)->next
;
3309 else ReleaseCacheGroup(m
, cp
);
3314 // 4. See if we can answer any of our new local questions from the cache
3315 for (i
=0; m
->NewQuestions
&& i
<1000; i
++)
3317 if (m
->NewQuestions
->DelayAnswering
&& m
->timenow
- m
->NewQuestions
->DelayAnswering
< 0) break;
3318 AnswerNewQuestion(m
);
3320 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
3322 for (i
=0; m
->NewLocalOnlyQuestions
&& i
<1000; i
++) AnswerNewLocalOnlyQuestion(m
);
3323 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
3325 for (i
=0; i
<1000 && m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
); i
++)
3327 AuthRecord
*rr
= m
->NewLocalRecords
;
3328 m
->NewLocalRecords
= m
->NewLocalRecords
->next
;
3329 AnswerLocalQuestions(m
, rr
, mDNStrue
);
3331 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
3333 // 5. See what packets we need to send
3334 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) DiscardDeregistrations(m
);
3335 else if (m
->SuppressSending
== 0 || m
->timenow
- m
->SuppressSending
>= 0)
3337 // If the platform code is ready, and we're not suppressing packet generation right now
3338 // then send our responses, probes, and questions.
3339 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them.
3340 // We send queries next, because there might be final-stage probes that complete their probing here, causing
3341 // them to advance to announcing state, and we want those to be included in any announcements we send out.
3342 // Finally, we send responses, including the previously mentioned records that just completed probing.
3343 m
->SuppressSending
= 0;
3345 // 6. Send Query packets. This may cause some probing records to advance to announcing state
3346 if (m
->timenow
- m
->NextScheduledQuery
>= 0 || m
->timenow
- m
->NextScheduledProbe
>= 0) SendQueries(m
);
3347 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3350 LogMsg("mDNS_Execute: SendQueries didn't send all its queries (%d - %d = %d) will try again in one second",
3351 m
->timenow
, m
->NextScheduledQuery
, m
->timenow
- m
->NextScheduledQuery
);
3352 m
->NextScheduledQuery
= m
->timenow
+ mDNSPlatformOneSecond
;
3353 for (q
= m
->Questions
; q
; q
=q
->next
)
3354 if (ActiveQuestion(q
) && q
->LastQTime
+ q
->ThisQInterval
- m
->timenow
<= 0)
3355 LogMsg("mDNS_Execute: SendQueries didn't send %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
3357 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3359 LogMsg("mDNS_Execute: SendQueries didn't send all its probes (%d - %d = %d) will try again in one second",
3360 m
->timenow
, m
->NextScheduledProbe
, m
->timenow
- m
->NextScheduledProbe
);
3361 m
->NextScheduledProbe
= m
->timenow
+ mDNSPlatformOneSecond
;
3364 // 7. Send Response packets, including probing records just advanced to announcing state
3365 if (m
->timenow
- m
->NextScheduledResponse
>= 0) SendResponses(m
);
3366 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
3368 LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
3369 m
->NextScheduledResponse
= m
->timenow
+ mDNSPlatformOneSecond
;
3373 // Clear RandomDelay values, ready to pick a new different value next time
3374 m
->RandomQueryDelay
= 0;
3375 m
->RandomReconfirmDelay
= 0;
3378 // Note about multi-threaded systems:
3379 // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
3380 // performing mDNS API operations that change our next scheduled event time.
3382 // On multi-threaded systems (like the current Windows implementation) that have a single main thread
3383 // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
3384 // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
3385 // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
3386 // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
3387 // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
3388 // does, the state of the signal will be noticed, causing the blocking primitive to return immediately
3389 // without blocking. This avoids the race condition between the signal from the other thread arriving
3390 // just *before* or just *after* the main thread enters the blocking primitive.
3392 // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
3393 // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
3394 // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
3395 // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
3396 // by the time it gets to the timer callback function).
3398 #ifndef UNICAST_DISABLED
3401 mDNS_Unlock(m
); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
3402 return(m
->NextScheduledEvent
);
3405 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
3406 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
3407 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
3408 // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
3409 // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
3410 // found itself in a new network environment. For example, if the Ethernet hardware indicates that the
3411 // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
3412 // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
3413 // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
3414 // traffic, so it should only be called when there is legitimate reason to believe the machine
3415 // may have become attached to a new network.
3416 mDNSexport
void mDNSCoreMachineSleep(mDNS
*const m
, mDNSBool sleepstate
)
3422 m
->SleepState
= sleepstate
;
3423 LogOperation("%s at %ld", sleepstate
? "Sleeping" : "Waking", m
->timenow
);
3427 #ifndef UNICAST_DISABLED
3430 // Mark all the records we need to deregister and send them
3431 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3432 if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
3433 rr
->ImmedAnswer
= mDNSInterfaceMark
;
3443 #ifndef UNICAST_DISABLED
3446 // 1. Retrigger all our questions
3447 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
3448 if (mDNSOpaque16IsZero(q
->TargetQID
) && ActiveQuestion(q
))
3450 q
->ThisQInterval
= InitialQuestionInterval
; // MUST be > zero for an active question
3451 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
3452 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
3453 q
->RecentAnswerPkts
= 0;
3454 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
);
3455 m
->NextScheduledQuery
= m
->timenow
;
3458 // 2. Re-validate our cache records
3459 m
->NextCacheCheck
= m
->timenow
;
3460 FORALL_CACHERECORDS(slot
, cg
, cr
)
3461 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForWake
);
3463 // 3. Retrigger probing and announcing for all our authoritative records
3464 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3466 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
3467 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
3468 rr
->AnnounceCount
= InitialAnnounceCount
;
3469 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
3470 InitializeLastAPTime(m
, rr
);
3477 // ***************************************************************************
3478 #if COMPILER_LIKES_PRAGMA_MARK
3480 #pragma mark - Packet Reception Functions
3483 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
3485 mDNSlocal mDNSu8
*GenerateUnicastResponse(const DNSMessage
*const query
, const mDNSu8
*const end
,
3486 const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, DNSMessage
*const response
, AuthRecord
*ResponseRecords
)
3488 mDNSu8
*responseptr
= response
->data
;
3489 const mDNSu8
*const limit
= response
->data
+ sizeof(response
->data
);
3490 const mDNSu8
*ptr
= query
->data
;
3492 mDNSu32 maxttl
= 0x70000000;
3495 // Initialize the response fields so we can answer the questions
3496 InitializeDNSMessage(&response
->h
, query
->h
.id
, ResponseFlags
);
3499 // *** 1. Write out the list of questions we are actually going to answer with this packet
3504 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
3507 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &q
); // get the question...
3508 if (!ptr
) return(mDNSNULL
);
3510 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // and search our list of proposed answers
3512 if (rr
->NR_AnswerTo
== ptr
) // If we're going to generate a record answering this question
3513 { // then put the question in the question section
3514 responseptr
= putQuestion(response
, responseptr
, limit
, &q
.qname
, q
.qtype
, q
.qclass
);
3515 if (!responseptr
) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL
); }
3516 break; // break out of the ResponseRecords loop, and go on to the next question
3521 if (response
->h
.numQuestions
== 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL
); }
3525 // *** 2. Write Answers
3527 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3528 if (rr
->NR_AnswerTo
)
3530 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAnswers
, &rr
->resrec
, maxttl
);
3531 if (p
) responseptr
= p
;
3532 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response
->h
.flags
.b
[0] |= kDNSFlag0_TC
; }
3536 // *** 3. Write Additionals
3538 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3539 if (rr
->NR_AdditionalTo
&& !rr
->NR_AnswerTo
)
3541 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAdditionals
, &rr
->resrec
, maxttl
);
3542 if (p
) responseptr
= p
;
3543 else debugf("GenerateUnicastResponse: No more space for additionals");
3546 return(responseptr
);
3549 // AuthRecord *our is our Resource Record
3550 // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
3551 // Returns 0 if there is no conflict
3552 // Returns +1 if there was a conflict and we won
3553 // Returns -1 if there was a conflict and we lost and have to rename
3554 mDNSlocal
int CompareRData(AuthRecord
*our
, CacheRecord
*pkt
)
3556 mDNSu8 ourdata
[256], *ourptr
= ourdata
, *ourend
;
3557 mDNSu8 pktdata
[256], *pktptr
= pktdata
, *pktend
;
3558 if (!our
) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
3559 if (!pkt
) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
3561 ourend
= putRData(mDNSNULL
, ourdata
, ourdata
+ sizeof(ourdata
), &our
->resrec
);
3562 pktend
= putRData(mDNSNULL
, pktdata
, pktdata
+ sizeof(pktdata
), &pkt
->resrec
);
3563 while (ourptr
< ourend
&& pktptr
< pktend
&& *ourptr
== *pktptr
) { ourptr
++; pktptr
++; }
3564 if (ourptr
>= ourend
&& pktptr
>= pktend
) return(0); // If data identical, not a conflict
3566 if (ourptr
>= ourend
) return(-1); // Our data ran out first; We lost
3567 if (pktptr
>= pktend
) return(+1); // Packet data ran out first; We won
3568 if (*pktptr
> *ourptr
) return(-1); // Our data is numerically lower; We lost
3569 if (*pktptr
< *ourptr
) return(+1); // Packet data is numerically lower; We won
3571 LogMsg("CompareRData ERROR: Invalid state");
3575 // See if we have an authoritative record that's identical to this packet record,
3576 // whose canonical DependentOn record is the specified master record.
3577 // The DependentOn pointer is typically used for the TXT record of service registrations
3578 // It indicates that there is no inherent conflict detection for the TXT record
3579 // -- it depends on the SRV record to resolve name conflicts
3580 // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
3581 // pointer chain (if any) to make sure we reach the canonical DependentOn record
3582 // If the record has no DependentOn, then just return that record's pointer
3583 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
3584 mDNSlocal mDNSBool
MatchDependentOn(const mDNS
*const m
, const CacheRecord
*const pktrr
, const AuthRecord
*const master
)
3586 const AuthRecord
*r1
;
3587 for (r1
= m
->ResourceRecords
; r1
; r1
=r1
->next
)
3589 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
3591 const AuthRecord
*r2
= r1
;
3592 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
3593 if (r2
== master
) return(mDNStrue
);
3596 for (r1
= m
->DuplicateRecords
; r1
; r1
=r1
->next
)
3598 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
3600 const AuthRecord
*r2
= r1
;
3601 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
3602 if (r2
== master
) return(mDNStrue
);
3608 // Find the canonical RRSet pointer for this RR received in a packet.
3609 // If we find any identical AuthRecord in our authoritative list, then follow its RRSet
3610 // pointers (if any) to make sure we return the canonical member of this name/type/class
3611 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
3612 mDNSlocal
const AuthRecord
*FindRRSet(const mDNS
*const m
, const CacheRecord
*const pktrr
)
3614 const AuthRecord
*rr
;
3615 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3617 if (IdenticalResourceRecord(&rr
->resrec
, &pktrr
->resrec
))
3619 while (rr
->RRSet
&& rr
!= rr
->RRSet
) rr
= rr
->RRSet
;
3626 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
3627 // as one of our records (our) but different rdata.
3628 // 1. If our record is not a type that's supposed to be unique, we don't care.
3629 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
3630 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
3631 // points to our record, ignore this conflict (e.g. the packet record matches one of our
3632 // TXT records, and that record is marked as dependent on 'our', its SRV record).
3633 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
3634 // are members of the same RRSet, then this is not a conflict.
3635 mDNSlocal mDNSBool
PacketRRConflict(const mDNS
*const m
, const AuthRecord
*const our
, const CacheRecord
*const pktrr
)
3637 const AuthRecord
*ourset
= our
->RRSet
? our
->RRSet
: our
;
3639 // If not supposed to be unique, not a conflict
3640 if (!(our
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)) return(mDNSfalse
);
3642 // If a dependent record, not a conflict
3643 if (our
->DependentOn
|| MatchDependentOn(m
, pktrr
, our
)) return(mDNSfalse
);
3645 // If the pktrr matches a member of ourset, not a conflict
3646 if (FindRRSet(m
, pktrr
) == ourset
) return(mDNSfalse
);
3648 // Okay, this is a conflict
3652 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
3653 // the record list and/or question list.
3654 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3655 mDNSlocal
void ResolveSimultaneousProbe(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
3656 DNSQuestion
*q
, AuthRecord
*our
)
3659 const mDNSu8
*ptr
= LocateAuthorities(query
, end
);
3660 mDNSBool FoundUpdate
= mDNSfalse
;
3662 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
3664 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, kDNSRecordTypePacketAuth
, &m
->rec
);
3666 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
3668 FoundUpdate
= mDNStrue
;
3669 if (PacketRRConflict(m
, our
, &m
->rec
.r
))
3671 int result
= (int)our
->resrec
.rrclass
- (int)m
->rec
.r
.resrec
.rrclass
;
3672 if (!result
) result
= (int)our
->resrec
.rrtype
- (int)m
->rec
.r
.resrec
.rrtype
;
3673 if (!result
) result
= CompareRData(our
, &m
->rec
.r
);
3675 LogOperation("ResolveSimultaneousProbe: %##s (%s): We won", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
3676 else if (result
< 0)
3678 LogOperation("ResolveSimultaneousProbe: %##s (%s): We lost", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
3679 mDNS_Deregister_internal(m
, our
, mDNS_Dereg_conflict
);
3684 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
3687 LogOperation("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
3689 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
3692 mDNSlocal CacheRecord
*FindIdenticalRecordInCache(const mDNS
*const m
, ResourceRecord
*pktrr
)
3694 mDNSu32 slot
= HashSlot(pktrr
->name
);
3695 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, pktrr
);
3697 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3698 if (pktrr
->InterfaceID
== rr
->resrec
.InterfaceID
&& IdenticalSameNameRecord(pktrr
, &rr
->resrec
)) break;
3702 // ProcessQuery examines a received query to see if we have any answers to give
3703 mDNSlocal mDNSu8
*ProcessQuery(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
3704 const mDNSAddr
*srcaddr
, const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, mDNSBool QueryWasMulticast
,
3705 mDNSBool QueryWasLocalUnicast
, DNSMessage
*const response
)
3707 mDNSBool FromLocalSubnet
= srcaddr
&& AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
3708 AuthRecord
*ResponseRecords
= mDNSNULL
;
3709 AuthRecord
**nrp
= &ResponseRecords
;
3710 CacheRecord
*ExpectedAnswers
= mDNSNULL
; // Records in our cache we expect to see updated
3711 CacheRecord
**eap
= &ExpectedAnswers
;
3712 DNSQuestion
*DupQuestions
= mDNSNULL
; // Our questions that are identical to questions in this packet
3713 DNSQuestion
**dqp
= &DupQuestions
;
3714 mDNSs32 delayresponse
= 0;
3715 mDNSBool SendLegacyResponse
= mDNSfalse
;
3716 const mDNSu8
*ptr
= query
->data
;
3717 mDNSu8
*responseptr
= mDNSNULL
;
3722 // *** 1. Parse Question Section and mark potential answers
3724 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
3726 mDNSBool QuestionNeedsMulticastResponse
;
3727 int NumAnswersForThisQuestion
= 0;
3728 DNSQuestion pktq
, *q
;
3729 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &pktq
); // get the question...
3730 if (!ptr
) goto exit
;
3732 // The only queries that *need* a multicast response are:
3733 // * Queries sent via multicast
3735 // * that don't have the kDNSQClass_UnicastResponse bit set
3736 // These queries need multicast responses because other clients will:
3737 // * suppress their own identical questions when they see these questions, and
3738 // * expire their cache records if they don't see the expected responses
3739 // For other queries, we may still choose to send the occasional multicast response anyway,
3740 // to keep our neighbours caches warm, and for ongoing conflict detection.
3741 QuestionNeedsMulticastResponse
= QueryWasMulticast
&& !LegacyQuery
&& !(pktq
.qclass
& kDNSQClass_UnicastResponse
);
3742 // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
3743 pktq
.qclass
&= ~kDNSQClass_UnicastResponse
;
3745 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
3746 // can result in user callbacks which may change the record list and/or question list.
3747 // Also note: we just mark potential answer records here, without trying to build the
3748 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
3749 // from that list while we're in the middle of trying to build it.
3750 if (m
->CurrentRecord
)
3751 LogMsg("ProcessQuery ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
3752 m
->CurrentRecord
= m
->ResourceRecords
;
3753 while (m
->CurrentRecord
)
3755 rr
= m
->CurrentRecord
;
3756 m
->CurrentRecord
= rr
->next
;
3757 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && (QueryWasMulticast
|| QueryWasLocalUnicast
|| rr
->AllowRemoteQuery
))
3759 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
3760 ResolveSimultaneousProbe(m
, query
, end
, &pktq
, rr
);
3761 else if (ResourceRecordIsValidAnswer(rr
))
3763 NumAnswersForThisQuestion
++;
3765 // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
3766 // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
3767 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
3768 // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
3769 // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link)
3770 // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
3771 if (QuestionNeedsMulticastResponse
|| (!FromLocalSubnet
&& QueryWasMulticast
&& !LegacyQuery
))
3773 // We only mark this question for sending if it is at least one second since the last time we multicast it
3774 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
3775 // This is to guard against the case where someone blasts us with queries as fast as they can.
3776 if (m
->timenow
- (rr
->LastMCTime
+ mDNSPlatformOneSecond
) >= 0 ||
3777 (rr
->LastMCInterface
!= mDNSInterfaceMark
&& rr
->LastMCInterface
!= InterfaceID
))
3778 rr
->NR_AnswerTo
= (mDNSu8
*)~0;
3780 else if (!rr
->NR_AnswerTo
) rr
->NR_AnswerTo
= LegacyQuery
? ptr
: (mDNSu8
*)~1;
3785 // If we couldn't answer this question, someone else might be able to,
3786 // so use random delay on response to reduce collisions
3787 if (NumAnswersForThisQuestion
== 0) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
3789 // We only do the following accelerated cache expiration processing and duplicate question suppression processing
3790 // for multicast queries with multicast responses.
3791 // For any query generating a unicast response we don't do this because we can't assume we will see the response
3792 if (QuestionNeedsMulticastResponse
)
3794 const mDNSu32 slot
= HashSlot(&pktq
.qname
);
3795 CacheGroup
*cg
= CacheGroupForName(m
, slot
, pktq
.qnamehash
, &pktq
.qname
);
3798 // Make a list indicating which of our own cache records we expect to see updated as a result of this query
3799 // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
3800 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3801 if (SameNameRecordAnswersQuestion(&rr
->resrec
, &pktq
) && rr
->resrec
.rdlength
<= SmallRecordLimit
)
3802 if (!rr
->NextInKAList
&& eap
!= &rr
->NextInKAList
)
3805 eap
= &rr
->NextInKAList
;
3806 if (rr
->MPUnansweredQ
== 0 || m
->timenow
- rr
->MPLastUnansweredQT
>= mDNSPlatformOneSecond
)
3808 // Although MPUnansweredQ is only really used for multi-packet query processing,
3809 // we increment it for both single-packet and multi-packet queries, so that it stays in sync
3810 // with the MPUnansweredKA value, which by necessity is incremented for both query types.
3811 rr
->MPUnansweredQ
++;
3812 rr
->MPLastUnansweredQT
= m
->timenow
;
3813 rr
->MPExpectingKA
= mDNStrue
;
3817 // Check if this question is the same as any of mine.
3818 // We only do this for non-truncated queries. Right now it would be too complicated to try
3819 // to keep track of duplicate suppression state between multiple packets, especially when we
3820 // can't guarantee to receive all of the Known Answer packets that go with a particular query.
3821 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
3822 for (q
= m
->Questions
; q
; q
=q
->next
)
3823 if (!q
->Target
.type
&& ActiveQuestion(q
) && m
->timenow
- q
->LastQTxTime
> mDNSPlatformOneSecond
/ 4)
3824 if (!q
->InterfaceID
|| q
->InterfaceID
== InterfaceID
)
3825 if (q
->NextInDQList
== mDNSNULL
&& dqp
!= &q
->NextInDQList
)
3826 if (q
->qtype
== pktq
.qtype
&&
3827 q
->qclass
== pktq
.qclass
&&
3828 q
->qnamehash
== pktq
.qnamehash
&& SameDomainName(&q
->qname
, &pktq
.qname
))
3829 { *dqp
= q
; dqp
= &q
->NextInDQList
; }
3834 // *** 2. Now we can safely build the list of marked answers
3836 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Now build our list of potential answers
3837 if (rr
->NR_AnswerTo
) // If we marked the record...
3838 AddRecordToResponseList(&nrp
, rr
, mDNSNULL
); // ... add it to the list
3841 // *** 3. Add additional records
3843 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
3846 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
3848 for (i
=0; i
<query
->h
.numAnswers
; i
++) // For each record in the query's answer section...
3850 // Get the record...
3852 CacheRecord
*ourcacherr
;
3853 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &m
->rec
);
3854 if (!ptr
) goto exit
;
3856 // See if this Known-Answer suppresses any of our currently planned answers
3857 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3858 if (MustSendRecord(rr
) && ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
3859 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
3861 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
3862 for (rr
=m
->ResourceRecords
; rr
; rr
=rr
->next
)
3864 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
3865 if (rr
->ImmedAnswer
== InterfaceID
&& ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
3867 if (srcaddr
->type
== mDNSAddrType_IPv4
)
3869 if (mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= zerov4Addr
;
3871 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
3873 if (mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= zerov6Addr
;
3875 if (mDNSIPv4AddressIsZero(rr
->v4Requester
) && mDNSIPv6AddressIsZero(rr
->v6Requester
))
3877 rr
->ImmedAnswer
= mDNSNULL
;
3878 rr
->ImmedUnicast
= mDNSfalse
;
3879 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
3880 LogMsg("Suppressed after%4d: %s", m
->timenow
- rr
->ImmedAnswerMarkTime
, ARDisplayString(m
, rr
));
3886 // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
3887 // even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list).
3888 ourcacherr
= FindIdenticalRecordInCache(m
, &m
->rec
.r
.resrec
);
3889 if (ourcacherr
&& ourcacherr
->MPExpectingKA
&& m
->timenow
- ourcacherr
->MPLastUnansweredQT
< mDNSPlatformOneSecond
)
3891 ourcacherr
->MPUnansweredKA
++;
3892 ourcacherr
->MPExpectingKA
= mDNSfalse
;
3895 // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
3896 // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
3897 // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
3898 eap
= &ExpectedAnswers
;
3901 CacheRecord
*rr
= *eap
;
3902 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
3903 { *eap
= rr
->NextInKAList
; rr
->NextInKAList
= mDNSNULL
; }
3904 else eap
= &rr
->NextInKAList
;
3907 // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
3910 dqp
= &DupQuestions
;
3913 DNSQuestion
*q
= *dqp
;
3914 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
3915 { *dqp
= q
->NextInDQList
; q
->NextInDQList
= mDNSNULL
; }
3916 else dqp
= &q
->NextInDQList
;
3919 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
3923 // *** 5. Cancel any additionals that were added because of now-deleted records
3925 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3926 if (rr
->NR_AdditionalTo
&& !MustSendRecord(rr
->NR_AdditionalTo
))
3927 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
3930 // *** 6. Mark the send flags on the records we plan to send
3932 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3934 if (rr
->NR_AnswerTo
)
3936 mDNSBool SendMulticastResponse
= mDNSfalse
; // Send modern multicast response
3937 mDNSBool SendUnicastResponse
= mDNSfalse
; // Send modern unicast response (not legacy unicast response)
3939 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
3940 if (m
->timenow
- (rr
->LastMCTime
+ TicksTTL(rr
)/4) >= 0)
3942 SendMulticastResponse
= mDNStrue
;
3943 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
3944 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
3945 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
3946 if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) rr
->NR_AnswerTo
= (mDNSu8
*)~0;
3949 // If the client insists on a multicast response, then we'd better send one
3950 if (rr
->NR_AnswerTo
== (mDNSu8
*)~0) SendMulticastResponse
= mDNStrue
;
3951 else if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) SendUnicastResponse
= mDNStrue
;
3952 else if (rr
->NR_AnswerTo
) SendLegacyResponse
= mDNStrue
;
3954 if (SendMulticastResponse
|| SendUnicastResponse
)
3956 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
3957 rr
->ImmedAnswerMarkTime
= m
->timenow
;
3959 m
->NextScheduledResponse
= m
->timenow
;
3960 // If we're already planning to send this on another interface, just send it on all interfaces
3961 if (rr
->ImmedAnswer
&& rr
->ImmedAnswer
!= InterfaceID
)
3962 rr
->ImmedAnswer
= mDNSInterfaceMark
;
3965 rr
->ImmedAnswer
= InterfaceID
; // Record interface to send it on
3966 if (SendUnicastResponse
) rr
->ImmedUnicast
= mDNStrue
;
3967 if (srcaddr
->type
== mDNSAddrType_IPv4
)
3969 if (mDNSIPv4AddressIsZero(rr
->v4Requester
)) rr
->v4Requester
= srcaddr
->ip
.v4
;
3970 else if (!mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= onesIPv4Addr
;
3972 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
3974 if (mDNSIPv6AddressIsZero(rr
->v6Requester
)) rr
->v6Requester
= srcaddr
->ip
.v6
;
3975 else if (!mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= onesIPv6Addr
;
3979 // If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
3980 // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
3981 // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
3982 // else, for a simple unique record reply, we can reply immediately; no need for delay
3983 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
) delayresponse
= mDNSPlatformOneSecond
* 20; // Divided by 50 = 400ms
3984 else if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
3986 else if (rr
->NR_AdditionalTo
&& rr
->NR_AdditionalTo
->NR_AnswerTo
== (mDNSu8
*)~0)
3988 // Since additional records are an optimization anyway, we only ever send them on one interface at a time
3989 // If two clients on different interfaces do queries that invoke the same optional additional answer,
3990 // then the earlier client is out of luck
3991 rr
->ImmedAdditional
= InterfaceID
;
3992 // No need to set m->NextScheduledResponse here
3993 // We'll send these additional records when we send them, or not, as the case may be
3998 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
4000 if (delayresponse
&& (!m
->SuppressSending
|| (m
->SuppressSending
- m
->timenow
) < (delayresponse
+ 49) / 50))
4002 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4003 mDNSs32 oldss
= m
->SuppressSending
;
4004 if (oldss
&& delayresponse
)
4005 LogMsg("Current SuppressSending delay%5ld; require%5ld", m
->SuppressSending
- m
->timenow
, (delayresponse
+ 49) / 50);
4007 // Pick a random delay:
4008 // We start with the base delay chosen above (typically either 1 second or 20 seconds),
4009 // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
4010 // This is an integer value, with resolution determined by the platform clock rate.
4011 // We then divide that by 50 to get the delay value in ticks. We defer the division until last
4012 // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
4013 // The +49 before dividing is to ensure we round up, not down, to ensure that even
4014 // on platforms where the native clock rate is less than fifty ticks per second,
4015 // we still guarantee that the final calculated delay is at least one platform tick.
4016 // We want to make sure we don't ever allow the delay to be zero ticks,
4017 // because if that happens we'll fail the Bonjour Conformance Test.
4018 // Our final computed delay is 20-120ms for normal delayed replies,
4019 // or 400-500ms in the case of multi-packet known-answer lists.
4020 m
->SuppressSending
= m
->timenow
+ (delayresponse
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*5) + 49) / 50;
4021 if (m
->SuppressSending
== 0) m
->SuppressSending
= 1;
4022 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4023 if (oldss
&& delayresponse
)
4024 LogMsg("Set SuppressSending to %5ld", m
->SuppressSending
- m
->timenow
);
4029 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
4031 if (SendLegacyResponse
)
4032 responseptr
= GenerateUnicastResponse(query
, end
, InterfaceID
, LegacyQuery
, response
, ResponseRecords
);
4035 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4038 // *** 9. Finally, clear our link chains ready for use next time
4040 while (ResponseRecords
)
4042 rr
= ResponseRecords
;
4043 ResponseRecords
= rr
->NextResponse
;
4044 rr
->NextResponse
= mDNSNULL
;
4045 rr
->NR_AnswerTo
= mDNSNULL
;
4046 rr
->NR_AdditionalTo
= mDNSNULL
;
4049 while (ExpectedAnswers
)
4052 rr
= ExpectedAnswers
;
4053 ExpectedAnswers
= rr
->NextInKAList
;
4054 rr
->NextInKAList
= mDNSNULL
;
4056 // For non-truncated queries, we can definitively say that we should expect
4057 // to be seeing a response for any records still left in the ExpectedAnswers list
4058 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
4059 if (rr
->UnansweredQueries
== 0 || m
->timenow
- rr
->LastUnansweredTime
>= mDNSPlatformOneSecond
)
4061 rr
->UnansweredQueries
++;
4062 rr
->LastUnansweredTime
= m
->timenow
;
4063 if (rr
->UnansweredQueries
> 1)
4064 debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
4065 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
4066 SetNextCacheCheckTime(m
, rr
);
4069 // If we've seen multiple unanswered queries for this record,
4070 // then mark it to expire in five seconds if we don't get a response by then.
4071 if (rr
->UnansweredQueries
>= MaxUnansweredQueries
)
4073 // Only show debugging message if this record was not about to expire anyway
4074 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
4075 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
4076 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
4077 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
4079 // Make a guess, based on the multi-packet query / known answer counts, whether we think we
4080 // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
4081 // possible packet loss of up to 20% of the additional KA packets.)
4082 else if (rr
->MPUnansweredQ
* 4 > rr
->MPUnansweredKA
* 5 + 8)
4084 // We want to do this conservatively.
4085 // If there are so many machines on the network that they have to use multi-packet known-answer lists,
4086 // then we don't want them to all hit the network simultaneously with their final expiration queries.
4087 // By setting the record to expire in four minutes, we achieve two things:
4088 // (a) the 90-95% final expiration queries will be less bunched together
4089 // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
4090 mDNSu32 remain
= (mDNSu32
)(RRExpireTime(rr
) - m
->timenow
) / 4;
4091 if (remain
> 240 * (mDNSu32
)mDNSPlatformOneSecond
)
4092 remain
= 240 * (mDNSu32
)mDNSPlatformOneSecond
;
4094 // Only show debugging message if this record was not about to expire anyway
4095 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
4096 debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
4097 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
4099 if (remain
<= 60 * (mDNSu32
)mDNSPlatformOneSecond
)
4100 rr
->UnansweredQueries
++; // Treat this as equivalent to one definite unanswered query
4101 rr
->MPUnansweredQ
= 0; // Clear MPQ/MPKA statistics
4102 rr
->MPUnansweredKA
= 0;
4103 rr
->MPExpectingKA
= mDNSfalse
;
4105 if (remain
< kDefaultReconfirmTimeForNoAnswer
)
4106 remain
= kDefaultReconfirmTimeForNoAnswer
;
4107 mDNS_Reconfirm_internal(m
, rr
, remain
);
4111 while (DupQuestions
)
4114 DNSQuestion
*q
= DupQuestions
;
4115 DupQuestions
= q
->NextInDQList
;
4116 q
->NextInDQList
= mDNSNULL
;
4117 i
= RecordDupSuppressInfo(q
->DupSuppress
, m
->timenow
, InterfaceID
, srcaddr
->type
);
4118 debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q
->qname
.c
, DNSTypeName(q
->qtype
), InterfaceID
,
4119 srcaddr
->type
== mDNSAddrType_IPv4
? "v4" : "v6", i
);
4122 return(responseptr
);
4125 mDNSlocal
void mDNSCoreReceiveQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
4126 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
4127 const mDNSInterfaceID InterfaceID
)
4129 mDNSu8
*responseend
= mDNSNULL
;
4130 mDNSBool QueryWasLocalUnicast
= srcaddr
&& dstaddr
&&
4131 !mDNSAddrIsDNSMulticast(dstaddr
) && AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
4133 if (!InterfaceID
&& dstaddr
&& mDNSAddrIsDNSMulticast(dstaddr
))
4135 LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
4136 "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)",
4137 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
4138 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
4139 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
4140 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
4141 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
4145 verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
4146 "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
4147 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
4148 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
4149 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
4150 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
4151 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
4153 responseend
= ProcessQuery(m
, msg
, end
, srcaddr
, InterfaceID
,
4154 !mDNSSameIPPort(srcport
, MulticastDNSPort
), mDNSAddrIsDNSMulticast(dstaddr
), QueryWasLocalUnicast
, &m
->omsg
);
4156 if (responseend
) // If responseend is non-null, that means we built a unicast response packet
4158 debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
4159 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
4160 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
4161 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s",
4162 srcaddr
, mDNSVal16(srcport
), InterfaceID
, srcaddr
->type
);
4163 mDNSSendDNSMessage(m
, &m
->omsg
, responseend
, InterfaceID
, srcaddr
, srcport
, mDNSNULL
, mDNSNULL
);
4167 mDNSlocal mDNSBool
TrustedSource(const mDNS
*const m
, const mDNSAddr
*const srcaddr
)
4171 (void)srcaddr
; // Unused
4172 for (s
= m
->DNSServers
; s
; s
= s
->next
)
4173 if (mDNSSameAddress(srcaddr
, &s
->addr
)) return(mDNStrue
);
4177 mDNSlocal
const DNSQuestion
*ExpectingUnicastResponseForQuestion(const mDNS
*const m
, const mDNSOpaque16 id
, const DNSQuestion
*const question
)
4180 for (q
= m
->Questions
; q
; q
=q
->next
)
4181 if (mDNSSameOpaque16(q
->TargetQID
, id
) &&
4182 q
->qtype
== question
->qtype
&&
4183 q
->qclass
== question
->qclass
&&
4184 q
->qnamehash
== question
->qnamehash
&&
4185 SameDomainName(&q
->qname
, &question
->qname
))
4190 mDNSlocal mDNSBool
ExpectingUnicastResponseForRecord(mDNS
*const m
, const mDNSAddr
*const srcaddr
, const mDNSBool SrcLocal
, const mDNSOpaque16 id
, const CacheRecord
*const rr
)
4194 for (q
= m
->Questions
; q
; q
=q
->next
)
4195 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4197 if (!mDNSOpaque16IsZero(q
->TargetQID
))
4199 // For now we don't do this check -- for LLQ updates, the ID doesn't seem to match the ID in the question
4200 // if (mDNSSameOpaque16(q->TargetQID, id)
4202 if (mDNSSameAddress(srcaddr
, &q
->Target
)) return(mDNStrue
);
4203 if (mDNSSameOpaque16(q
->TargetQID
, id
)) return(mDNStrue
);
4204 // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking
4205 if (TrustedSource(m
, srcaddr
)) return(mDNStrue
);
4206 LogOperation("WARNING: Ignoring suspect uDNS response for %##s (%s) %#a from %#a: %s",
4207 q
->qname
.c
, DNSTypeName(q
->qtype
), &q
->Target
, srcaddr
, CRDisplayString(m
, rr
));
4213 if (SrcLocal
&& q
->ExpectUnicastResp
&& (mDNSu32
)(m
->timenow
- q
->ExpectUnicastResp
) < (mDNSu32
)(mDNSPlatformOneSecond
*2))
4220 mDNSexport CacheRecord
*CreateNewCacheEntry(mDNS
*const m
, const mDNSu32 slot
, CacheGroup
*cg
)
4222 CacheRecord
*rr
= mDNSNULL
;
4224 // Certain data types need more space for in-memory storage than their in-packet rdlength would imply
4225 // Currently this applies only to rdata types containing more than one domainname,
4226 // or types where the domainname is not the last item in the structure
4228 switch (m
->rec
.r
.resrec
.rrtype
)
4230 case kDNSType_SOA
: RDLength
= sizeof(rdataSOA
); break;
4231 case kDNSType_RP
: RDLength
= sizeof(rdataRP
); break;
4232 case kDNSType_PX
: RDLength
= sizeof(rdataPX
); break;
4233 default: RDLength
= m
->rec
.r
.resrec
.rdlength
; break;
4236 //if (RDLength > InlineCacheRDSize)
4237 // LogOperation("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r));
4239 if (!cg
) cg
= GetCacheGroup(m
, slot
, &m
->rec
.r
.resrec
); // If we don't have a CacheGroup for this name, make one now
4240 if (cg
) rr
= GetCacheRecord(m
, cg
, RDLength
); // Make a cache record, being careful not to recycle cg
4241 if (!rr
) NoCacheAnswer(m
, &m
->rec
.r
);
4244 RData
*saveptr
= rr
->resrec
.rdata
; // Save the rr->resrec.rdata pointer
4245 *rr
= m
->rec
.r
; // Block copy the CacheRecord object
4246 rr
->resrec
.rdata
= saveptr
; // Restore rr->resrec.rdata after the structure assignment
4247 rr
->resrec
.name
= cg
->name
; // And set rr->resrec.name to point into our CacheGroup header
4249 // If this is an oversized record with external storage allocated, copy rdata to external storage
4250 if (rr
->resrec
.rdata
== (RData
*)&rr
->rdatastorage
&& RDLength
> InlineCacheRDSize
)
4251 LogMsg("rr->resrec.rdata == &rr->rdatastorage but length > InlineCacheRDSize %##s", m
->rec
.r
.resrec
.name
->c
);
4252 else if (rr
->resrec
.rdata
!= (RData
*)&rr
->rdatastorage
&& RDLength
<= InlineCacheRDSize
)
4253 LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m
->rec
.r
.resrec
.name
->c
);
4254 if (RDLength
> InlineCacheRDSize
)
4255 mDNSPlatformMemCopy(rr
->resrec
.rdata
, m
->rec
.r
.resrec
.rdata
, sizeofRDataHeader
+ RDLength
);
4257 rr
->next
= mDNSNULL
; // Clear 'next' pointer
4258 *(cg
->rrcache_tail
) = rr
; // Append this record to tail of cache slot list
4259 cg
->rrcache_tail
= &(rr
->next
); // Advance tail pointer
4260 if (rr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
)
4261 rr
->DelayDelivery
= NonZeroTime(m
->timenow
);
4262 else if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
&& // If marked unique,
4263 rr
->resrec
.rdata
->MaxRDLength
!= 0) // and non-negative, assume we may have
4264 rr
->DelayDelivery
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
); // to delay delivery of this 'add' event
4266 rr
->DelayDelivery
= CheckForSoonToExpireRecords(m
, rr
->resrec
.name
, rr
->resrec
.namehash
, slot
);
4268 CacheRecordAdd(m
, rr
); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us
4273 mDNSlocal
void RefreshCacheRecord(mDNS
*const m
, CacheRecord
*rr
, mDNSu32 ttl
)
4275 rr
->TimeRcvd
= m
->timenow
;
4276 rr
->resrec
.rroriginalttl
= ttl
;
4277 rr
->UnansweredQueries
= 0;
4278 rr
->MPUnansweredQ
= 0;
4279 rr
->MPUnansweredKA
= 0;
4280 rr
->MPExpectingKA
= mDNSfalse
;
4281 SetNextCacheCheckTime(m
, rr
);
4284 mDNSexport
void GrantCacheExtensions(mDNS
*const m
, DNSQuestion
*q
, mDNSu32 lease
)
4287 const mDNSu32 slot
= HashSlot(&q
->qname
);
4288 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
4289 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4290 if (rr
->CRActiveQuestion
== q
)
4292 //LogOperation("GrantCacheExtensions: new lease %d / %s", lease, CRDisplayString(m, rr));
4293 RefreshCacheRecord(m
, rr
, lease
);
4297 mDNSlocal mDNSu32
GetEffectiveTTL(const uDNS_LLQType LLQType
, mDNSu32 ttl
) // TTL in seconds
4299 if (LLQType
== uDNS_LLQ_Poll
) ttl
= LLQ_POLL_INTERVAL
* 2 / mDNSPlatformOneSecond
;
4300 else if (LLQType
== uDNS_LLQ_Setup
) ttl
= kLLQ_DefLease
;
4301 else if (LLQType
== uDNS_LLQ_Events
)
4303 // If the TTL is -1 for uDNS LLQ event packet, that means "remove"
4304 if (ttl
== 0xFFFFFFFF) ttl
= 0;
4305 else ttl
= kLLQ_DefLease
;
4307 else // else not LLQ (standard uDNS response)
4309 // The TTL is already capped to a maximum value in GetLargeResourceRecord, but just to be extra safe we
4310 // also do this check here to make sure we can't get integer overflow below
4311 if (ttl
> 0x8000000UL
) ttl
= 0x8000000UL
;
4313 // Adjustment factor to avoid race condition:
4314 // Suppose real record as TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100.
4315 // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another
4316 // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox.
4317 // To avoid this, we extend the record's effective TTL to give it a little extra grace period.
4318 // We adjust the 100 second TTL to 126. This means that when we do our 80% query at 101 seconds,
4319 // the cached copy at our local caching server will already have expired, so the server will be forced
4320 // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds.
4323 // For mDNS, TTL zero means "delete this record"
4324 // For uDNS, TTL zero means: this data is true at this moment, but don't cache it.
4325 // For the sake of network efficiency, we impose a minimum effective TTL of 15 seconds.
4326 // If we allow a TTL of less than 2 seconds things really break (e.g. we end up making a negative cache entry).
4327 // In the future we may want to revisit this and consider properly supporting non-cached (TTL=0) uDNS answers.
4328 if (ttl
< 15) ttl
= 15;
4334 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
4335 // the record list and/or question list.
4336 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4337 mDNSlocal
void mDNSCoreReceiveResponse(mDNS
*const m
,
4338 const DNSMessage
*const response
, const mDNSu8
*end
,
4339 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
4340 const mDNSInterfaceID InterfaceID
)
4343 mDNSBool ResponseMCast
= dstaddr
&& mDNSAddrIsDNSMulticast(dstaddr
);
4344 mDNSBool ResponseSrcLocal
= !srcaddr
|| AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
4345 uDNS_LLQType LLQType
= uDNS_recvLLQResponse(m
, response
, end
, srcaddr
, srcport
);
4347 // "(CacheRecord*)1" is a special (non-zero) end-of-list marker
4348 // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList
4349 // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling.
4350 CacheRecord
*CacheFlushRecords
= (CacheRecord
*)1;
4351 CacheRecord
**cfp
= &CacheFlushRecords
;
4353 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
4354 // to guard against spoof responses, then the only credible protection against that is cryptographic
4355 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
4356 int totalrecords
= response
->h
.numAnswers
+ response
->h
.numAuthorities
+ response
->h
.numAdditionals
;
4357 const mDNSu8
*ptr
= response
->data
;
4359 // Currently used only for display in debugging message
4363 debugf("Received Response from %#-15a addressed to %#-15a on %p with "
4364 "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s LLQType %d",
4365 srcaddr
, dstaddr
, InterfaceID
,
4366 response
->h
.numQuestions
, response
->h
.numQuestions
== 1 ? ", " : "s,",
4367 response
->h
.numAnswers
, response
->h
.numAnswers
== 1 ? ", " : "s,",
4368 response
->h
.numAuthorities
, response
->h
.numAuthorities
== 1 ? "y, " : "ies,",
4369 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s", LLQType
);
4371 // 1. We ignore questions (if any) in mDNS response packets
4372 // 2. If this is an LLQ response, we handle it much the same
4373 // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this
4374 // answer as being the authoritative complete RRSet, and respond by deleting all other
4375 // matching cache records that don't appear in this packet.
4376 // Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged
4377 if (ResponseMCast
|| LLQType
== uDNS_LLQ_Events
|| (response
->h
.flags
.b
[0] & kDNSFlag0_TC
))
4378 ptr
= LocateAnswers(response
, end
);
4379 // Otherwise, for one-shot queries, any answers in our cache that are not also contained
4380 // in this response packet are immediately deemed to be invalid.
4383 // We could possibly combine this with the similar loop at the end of this function --
4384 // instead of tagging cache records here and then rescuing them if we find them in the answer section,
4385 // we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in
4386 // which it was received (or refreshed), and then at the end if we find any cache records which
4387 // answer questions in this packet's question section, but which aren't tagged with this packet's
4388 // packet number, then we deduce they are old and delete them
4389 for (i
= 0; i
< response
->h
.numQuestions
&& ptr
&& ptr
< end
; i
++)
4392 ptr
= getQuestion(response
, ptr
, end
, InterfaceID
, &q
);
4393 if (ptr
&& ExpectingUnicastResponseForQuestion(m
, response
->h
.id
, &q
))
4396 const mDNSu32 slot
= HashSlot(&q
.qname
);
4397 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
.qnamehash
, &q
.qname
);
4398 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4399 if (SameNameRecordAnswersQuestion(&rr
->resrec
, &q
))
4401 //LogMsg("uDNS Q for %s", CRDisplayString(m, rr));
4402 // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm
4403 rr
->TimeRcvd
= m
->timenow
- rr
->resrec
.rroriginalttl
* mDNSPlatformOneSecond
;
4404 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4410 for (i
= 0; i
< totalrecords
&& ptr
&& ptr
< end
; i
++)
4412 // All responses sent via LL multicast are acceptable for caching
4413 // All responses received over our outbound TCP connections are acceptable for caching
4414 mDNSBool AcceptableResponse
= ResponseMCast
|| !dstaddr
|| LLQType
;
4415 // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer
4416 // to any specific question -- any code reading records from the cache needs to make that determination for itself.)
4418 const mDNSu8 RecordType
= (mDNSu8
)((i
< response
->h
.numAnswers
) ? kDNSRecordTypePacketAns
: kDNSRecordTypePacketAdd
);
4419 ptr
= GetLargeResourceRecord(m
, response
, ptr
, end
, InterfaceID
, RecordType
, &m
->rec
);
4420 if (!ptr
) goto exit
; // Break out of the loop and clean up our CacheFlushRecords list before exiting
4421 // Don't want to cache OPT or TSIG pseudo-RRs
4422 if (m
->rec
.r
.resrec
.rrtype
== kDNSType_OPT
|| m
->rec
.r
.resrec
.rrtype
== kDNSType_TSIG
)
4423 { m
->rec
.r
.resrec
.RecordType
= 0; continue; }
4425 // When we receive uDNS LLQ responses, we assume a long cache lifetime --
4426 // In the case of active LLQs, we'll get remove events when the records actually do go away
4427 // In the case of polling LLQs, we assume the record remains valid until the next poll
4428 if (!mDNSOpaque16IsZero(response
->h
.id
))
4429 m
->rec
.r
.resrec
.rroriginalttl
= GetEffectiveTTL(LLQType
, m
->rec
.r
.resrec
.rroriginalttl
);
4431 // If response was not sent via LL multicast,
4432 // then see if it answers a recent query of ours, which would also make it acceptable for caching.
4433 if (!AcceptableResponse
) AcceptableResponse
= ExpectingUnicastResponseForRecord(m
, srcaddr
, ResponseSrcLocal
, response
->h
.id
, &m
->rec
.r
);
4435 // 1. Check that this packet resource record does not conflict with any of ours
4436 if (mDNSOpaque16IsZero(response
->h
.id
))
4438 if (m
->CurrentRecord
)
4439 LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
4440 m
->CurrentRecord
= m
->ResourceRecords
;
4441 while (m
->CurrentRecord
)
4443 AuthRecord
*rr
= m
->CurrentRecord
;
4444 m
->CurrentRecord
= rr
->next
;
4445 // We accept all multicast responses, and unicast responses resulting from queries we issued
4446 // For other unicast responses, this code accepts them only for responses with an
4447 // (apparently) local source address that pertain to a record of our own that's in probing state
4448 if (!AcceptableResponse
&& !(ResponseSrcLocal
&& rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)) continue;
4449 if (PacketRRMatchesSignature(&m
->rec
.r
, rr
)) // If interface, name, type (if shared record) and class match...
4451 // ... check to see if type and rdata are identical
4452 if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
&& SameRData(&m
->rec
.r
.resrec
, &rr
->resrec
))
4454 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
4455 if (m
->rec
.r
.resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/2 || m
->SleepState
)
4457 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
4458 if (rr
->ImmedAnswer
== InterfaceID
) { rr
->ImmedAnswer
= mDNSNULL
; rr
->ImmedUnicast
= mDNSfalse
; }
4462 if (rr
->ImmedAnswer
== mDNSNULL
) { rr
->ImmedAnswer
= InterfaceID
; m
->NextScheduledResponse
= m
->timenow
; }
4463 else if (rr
->ImmedAnswer
!= InterfaceID
) { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
4466 // else, the packet RR has different type or different rdata -- check to see if this is a conflict
4467 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0 && PacketRRConflict(m
, rr
, &m
->rec
.r
))
4469 debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr
-> resrec
.rdatahash
, ARDisplayString(m
, rr
));
4470 debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m
->rec
.r
.resrec
.rdatahash
, CRDisplayString(m
, &m
->rec
.r
));
4472 // If this record is marked DependentOn another record for conflict detection purposes,
4473 // then *that* record has to be bumped back to probing state to resolve the conflict
4474 while (rr
->DependentOn
) rr
= rr
->DependentOn
;
4476 // If we've just whacked this record's ProbeCount, don't need to do it again
4477 if (rr
->ProbeCount
<= DefaultProbeCountForTypeUnique
)
4479 // If we'd previously verified this record, put it back to probing state and try again
4480 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
)
4482 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4483 rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
4484 rr
->ProbeCount
= DefaultProbeCountForTypeUnique
+ 1;
4485 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(kDNSRecordTypeUnique
);
4486 InitializeLastAPTime(m
, rr
);
4487 RecordProbeFailure(m
, rr
); // Repeated late conflicts also cause us to back off to the slower probing rate
4489 // If we're probing for this record, we just failed
4490 else if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
4492 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4493 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
4495 // We assumed this record must be unique, but we were wrong.
4496 // (e.g. There are two mDNSResponders on the same machine giving
4497 // different answers for the reverse mapping record.)
4498 // This is simply a misconfiguration, and we don't try to recover from it.
4499 else if (rr
->resrec
.RecordType
== kDNSRecordTypeKnownUnique
)
4501 debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
4502 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4503 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
4506 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
4507 rr
->resrec
.RecordType
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4510 // Else, matching signature, different type or rdata, but not a considered a conflict.
4511 // If the packet record has the cache-flush bit set, then we check to see if we
4512 // have any record(s) of the same type that we should re-assert to rescue them
4513 // (see note about "multi-homing and bridged networks" at the end of this function).
4514 else if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
)
4515 if ((m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) && m
->timenow
- rr
->LastMCTime
> mDNSPlatformOneSecond
/2)
4516 { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
4521 // 2. See if we want to add this packet resource record to our cache
4522 // We only try to cache answers if we have a cache to put them in
4523 // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query
4524 if (m
->rrcache_size
&& AcceptableResponse
)
4526 const mDNSu32 slot
= HashSlot(m
->rec
.r
.resrec
.name
);
4527 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &m
->rec
.r
.resrec
);
4529 // 2a. Check if this packet resource record is already in our cache
4530 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4532 // If we found this exact resource record, refresh its TTL
4533 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalSameNameRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
4535 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
4536 verbosedebugf("Found record size %5d interface %p already in cache: %s",
4537 m
->rec
.r
.resrec
.rdlength
, InterfaceID
, CRDisplayString(m
, &m
->rec
.r
));
4538 rr
->TimeRcvd
= m
->timenow
;
4540 if (m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
4542 // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
4543 if (rr
->NextInCFList
== mDNSNULL
&& cfp
!= &rr
->NextInCFList
&& LLQType
!= uDNS_LLQ_Events
)
4544 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
4546 // If this packet record is marked unique, and our previous cached copy was not, then fix it
4547 if (!(rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))
4550 for (q
= m
->Questions
; q
; q
=q
->next
) if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) q
->UniqueAnswers
++;
4551 rr
->resrec
.RecordType
= m
->rec
.r
.resrec
.RecordType
;
4555 if (!mDNSPlatformMemSame(m
->rec
.r
.resrec
.rdata
->u
.data
, rr
->resrec
.rdata
->u
.data
, m
->rec
.r
.resrec
.rdlength
))
4557 // If the rdata of the packet record differs in name capitalization from the record in our cache
4558 // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
4559 // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
4560 rr
->resrec
.rroriginalttl
= 0;
4561 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4562 SetNextCacheCheckTime(m
, rr
);
4563 // DO NOT break out here -- we want to continue as if we never found it
4565 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0)
4567 //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr));
4568 RefreshCacheRecord(m
, rr
, m
->rec
.r
.resrec
.rroriginalttl
);
4573 // If the packet TTL is zero, that means we're deleting this record.
4574 // To give other hosts on the network a chance to protest, we push the deletion
4575 // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
4576 // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
4577 // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
4578 rr
->resrec
.rroriginalttl
= 1;
4579 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4580 SetNextCacheCheckTime(m
, rr
);
4586 // If packet resource record not in our cache, add it now
4587 // (unless it is just a deletion of a record we never had, in which case we don't care)
4588 if (!rr
&& m
->rec
.r
.resrec
.rroriginalttl
> 0)
4590 rr
= CreateNewCacheEntry(m
, slot
, cg
);
4591 if (rr
&& (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) && LLQType
!= uDNS_LLQ_Events
)
4592 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
4595 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4599 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4601 // If we've just received one or more records with their cache flush bits set,
4602 // then scan that cache slot to see if there are any old stale records we need to flush
4603 while (CacheFlushRecords
!= (CacheRecord
*)1)
4605 CacheRecord
*r1
= CacheFlushRecords
, *r2
;
4606 const mDNSu32 slot
= HashSlot(r1
->resrec
.name
);
4607 const CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &r1
->resrec
);
4608 CacheFlushRecords
= CacheFlushRecords
->NextInCFList
;
4609 r1
->NextInCFList
= mDNSNULL
;
4611 // Look for records in the cache with the same signature as this new one with the cache flush
4612 // bit set, and either (a) if they're fresh, just make sure the whole RRSet has the same TTL
4613 // (as required by DNS semantics) or (b) if they're old, mark them for deletion in one second.
4614 // We make these TTL adjustments *only* for records that still have *more* than one second
4615 // remaining to live. Otherwise, a record that we tagged for deletion half a second ago
4616 // (and now has half a second remaining) could inadvertently get its life extended, by either
4617 // (a) if we got an explicit goodbye packet half a second ago, the record would be considered
4618 // "fresh" and would be incorrectly resurrected back to the same TTL as the rest of the RRSet,
4619 // or (b) otherwise, the record would not be fully resurrected, but would be reset to expire
4620 // in one second, thereby inadvertently delaying its actual expiration, instead of hastening it.
4621 // If this were to happen repeatedly, the record's expiration could be deferred indefinitely.
4622 // To avoid this, we need to ensure that the cache flushing operation will only act to
4623 // *decrease* a record's remaining lifetime, never *increase* it. If a record has less than
4624 // one second to go, we simply leave it alone, and leave it to expire at its assigned time.
4625 for (r2
= cg
? cg
->members
: mDNSNULL
; r2
; r2
=r2
->next
)
4626 if (r1
->resrec
.InterfaceID
== r2
->resrec
.InterfaceID
&&
4627 r1
->resrec
.rrtype
== r2
->resrec
.rrtype
&&
4628 r1
->resrec
.rrclass
== r2
->resrec
.rrclass
)
4629 if (RRExpireTime(r2
) - m
->timenow
> mDNSPlatformOneSecond
)
4631 // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
4632 // else, if record is old, mark it to be flushed
4633 if (m
->timenow
- r2
->TimeRcvd
< mDNSPlatformOneSecond
)
4635 // If we find mismatched TTLs in an RRSet, correct them.
4636 // We only do this for records with a TTL of 2 or higher. It's possible to have a
4637 // goodbye announcement with the cache flush bit set (or a case change on record rdata,
4638 // which we treat as a goodbye followed by an addition) and in that case it would be
4639 // inappropriate to synchronize all the other records to a TTL of 0 (or 1).
4640 // We suppress the message for the specific case of correcting from 240 to 60 for type TXT,
4641 // because certain early Bonjour devices are known to have this specific mismatch, and
4642 // there's no point filling syslog with messages about something we already know about.
4643 // We also don't log this for uDNS responses, since a caching name server is obliged
4644 // to give us an aged TTL to correct for how long it has held the record,
4645 // so our received TTLs are expected to vary in that case
4646 if (r2
->resrec
.rroriginalttl
!= r1
->resrec
.rroriginalttl
&& r1
->resrec
.rroriginalttl
> 1)
4648 if (r2
->resrec
.rroriginalttl
!= 240 && r1
->resrec
.rroriginalttl
!= 60 && r2
->resrec
.rrtype
!= kDNSType_TXT
&&
4649 mDNSOpaque16IsZero(response
->h
.id
))
4650 LogMsg("Correcting TTL from %4d to %4d for %s",
4651 r2
->resrec
.rroriginalttl
, r1
->resrec
.rroriginalttl
, CRDisplayString(m
, r2
));
4652 r2
->resrec
.rroriginalttl
= r1
->resrec
.rroriginalttl
;
4655 else // else, if record is old, mark it to be flushed
4657 verbosedebugf("Cache flush %p X %p %s", r1
, r2
, CRDisplayString(m
, r2
));
4658 // We set stale records to expire in one second.
4659 // This gives the owner a chance to rescue it if necessary.
4660 // This is important in the case of multi-homing and bridged networks:
4661 // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
4662 // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
4663 // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
4664 // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
4665 // By delaying the deletion by one second, we give X a change to notice that this bridging has
4666 // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
4667 // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
4668 // final expiration queries for this record.
4669 r2
->resrec
.rroriginalttl
= 1;
4670 r2
->UnansweredQueries
= MaxUnansweredQueries
;
4672 r2
->TimeRcvd
= m
->timenow
;
4673 SetNextCacheCheckTime(m
, r2
);
4675 if (r1
->DelayDelivery
) // If we were planning to delay delivery of this record, see if we still need to
4677 // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
4678 r1
->DelayDelivery
= CheckForSoonToExpireRecords(m
, r1
->resrec
.name
, r1
->resrec
.namehash
, slot
);
4679 if (!r1
->DelayDelivery
) CacheRecordDeferredAdd(m
, r1
);
4683 // See if we need to generate negative cache entries for unanswered unicast questions
4684 ptr
= response
->data
;
4685 for (i
= 0; i
< response
->h
.numQuestions
&& ptr
&& ptr
< end
; i
++)
4688 ptr
= getQuestion(response
, ptr
, end
, InterfaceID
, &q
);
4689 if (ptr
&& ExpectingUnicastResponseForQuestion(m
, response
->h
.id
, &q
))
4692 mDNSu32 slot
= HashSlot(&q
.qname
);
4693 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
.qnamehash
, &q
.qname
);
4694 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4695 if (SameNameRecordAnswersQuestion(&rr
->resrec
, &q
))
4697 // 1. If we got a fresh answer to this query, then don't need to generate a negative entry
4698 // 2. If we already had a negative entry which we were about to discard, then we should resurrect it
4699 if (rr
->resrec
.rroriginalttl
) break;
4700 if (rr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
) break;
4703 if (!rr
|| rr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
)
4705 // We start off assuming a negative caching TTL of 60 seconds
4706 // but then look to see if we can find an SOA authority record to tell us a better value we should be using
4707 mDNSu32 negttl
= 60;
4709 const domainname
*name
= &q
.qname
;
4710 mDNSu32 hash
= q
.qnamehash
;
4712 // If we're going to make (or update) a negative entry, then look for the appropriate TTL from the SOA record
4713 if (response
->h
.numAuthorities
&& (ptr
= LocateAuthorities(response
, end
)) != mDNSNULL
)
4715 ptr
= GetLargeResourceRecord(m
, response
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAuth
, &m
->rec
);
4716 if (ptr
&& m
->rec
.r
.resrec
.rrtype
== kDNSType_SOA
)
4718 mDNSu32 ttl_s
= m
->rec
.r
.resrec
.rroriginalttl
< m
->rec
.r
.resrec
.rdata
->u
.soa
.min
?
4719 m
->rec
.r
.resrec
.rroriginalttl
: m
->rec
.r
.resrec
.rdata
->u
.soa
.min
;
4720 if (negttl
< ttl_s
) negttl
= ttl_s
;
4722 // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer,
4723 // with an Authority Section SOA record for d.com, then this is a hint that the authority
4724 // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either.
4725 // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us
4726 if (q
.qtype
== kDNSType_SOA
)
4728 int qcount
= CountLabels(&q
.qname
);
4729 int scount
= CountLabels(m
->rec
.r
.resrec
.name
);
4730 if (qcount
- 1 > scount
)
4731 if (SameDomainName(SkipLeadingLabels(&q
.qname
, qcount
- scount
), m
->rec
.r
.resrec
.name
))
4732 repeat
= qcount
- 1 - scount
;
4735 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4738 // If we already had a negative entry in the cache, then we double our existing negative TTL. This is to avoid
4739 // the case where the record doesn't exist (e.g. particularly for things like our lb._dns-sd._udp.<domain> query),
4740 // and the server returns no SOA record (or an SOA record with a small MIN TTL) so we assume a TTL
4741 // of 60 seconds, and we end up polling the server every minute for a record that doesn't exist.
4742 // With this fix in place, when this happens, we double the effective TTL each time (up to one hour),
4743 // so that we back off our polling rate and don't keep hitting the server continually.
4746 if (negttl
< rr
->resrec
.rroriginalttl
* 2)
4747 negttl
= rr
->resrec
.rroriginalttl
* 2;
4752 negttl
= GetEffectiveTTL(LLQType
, negttl
); // Add 25% grace period if necessary
4754 if (rr
) LogOperation("Renewing negative TTL from %d to %d %s", rr
->resrec
.rroriginalttl
, negttl
, CRDisplayString(m
, rr
));
4756 // If we already had a negative cache entry just update it, else make one or more new negative cache entries
4758 RefreshCacheRecord(m
, rr
, negttl
);
4761 LogOperation("Making negative cache entry TTL %d for %##s (%s)", negttl
, name
->c
, DNSTypeName(q
.qtype
));
4762 MakeNegativeCacheRecord(m
, name
, hash
, q
.qtype
, q
.qclass
, negttl
);
4763 CreateNewCacheEntry(m
, slot
, cg
);
4764 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4767 name
= (const domainname
*)(name
->c
+ 1 + name
->c
[0]);
4768 hash
= DomainNameHashValue(name
);
4769 slot
= HashSlot(name
);
4770 cg
= CacheGroupForName(m
, slot
, hash
, name
);
4777 mDNSexport
void MakeNegativeCacheRecord(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const mDNSu16 rrtype
, const mDNSu16 rrclass
, mDNSu32 ttl_seconds
)
4779 // Create empty resource record
4780 m
->rec
.r
.resrec
.RecordType
= kDNSRecordTypePacketNegative
;
4781 m
->rec
.r
.resrec
.InterfaceID
= mDNSInterface_Any
;
4782 m
->rec
.r
.resrec
.name
= name
; // Will be updated to point to cg->name when we call CreateNewCacheEntry
4783 m
->rec
.r
.resrec
.rrtype
= rrtype
;
4784 m
->rec
.r
.resrec
.rrclass
= rrclass
;
4785 m
->rec
.r
.resrec
.rroriginalttl
= ttl_seconds
;
4786 m
->rec
.r
.resrec
.rdlength
= 0;
4787 m
->rec
.r
.resrec
.rdestimate
= 0;
4788 m
->rec
.r
.resrec
.namehash
= namehash
;
4789 m
->rec
.r
.resrec
.rdatahash
= 0;
4790 m
->rec
.r
.resrec
.rdata
= (RData
*)&m
->rec
.r
.rdatastorage
;
4791 m
->rec
.r
.resrec
.rdata
->MaxRDLength
= m
->rec
.r
.resrec
.rdlength
;
4793 m
->rec
.r
.NextInKAList
= mDNSNULL
;
4794 m
->rec
.r
.TimeRcvd
= m
->timenow
;
4795 m
->rec
.r
.DelayDelivery
= 0;
4796 m
->rec
.r
.NextRequiredQuery
= m
->timenow
;
4797 m
->rec
.r
.LastUsed
= m
->timenow
;
4798 m
->rec
.r
.CRActiveQuestion
= mDNSNULL
;
4799 m
->rec
.r
.UnansweredQueries
= 0;
4800 m
->rec
.r
.LastUnansweredTime
= 0;
4801 m
->rec
.r
.MPUnansweredQ
= 0;
4802 m
->rec
.r
.MPLastUnansweredQT
= 0;
4803 m
->rec
.r
.MPUnansweredKA
= 0;
4804 m
->rec
.r
.MPExpectingKA
= mDNSfalse
;
4805 m
->rec
.r
.NextInCFList
= mDNSNULL
;
4808 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, void *const pkt
, const mDNSu8
*const end
,
4809 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSIPPort dstport
,
4810 const mDNSInterfaceID InterfaceID
)
4812 mDNSInterfaceID ifid
= InterfaceID
;
4813 DNSMessage
*msg
= (DNSMessage
*)pkt
;
4814 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
4815 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
4816 const mDNSu8 UpdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
4818 mDNSu8
*ptr
= mDNSNULL
;
4819 mDNSBool TLS
= (dstaddr
== (mDNSAddr
*)1); // For debug logs: dstaddr = 0 means TCP; dstaddr = 1 means TLS
4820 if (TLS
) dstaddr
= mDNSNULL
;
4822 #ifndef UNICAST_DISABLED
4823 if (mDNSSameAddress(srcaddr
, &m
->Router
))
4825 if (mDNSSameIPPort(srcport
, NATPMPPort
))
4828 uDNS_ReceiveNATPMPPacket(m
, InterfaceID
, pkt
, (mDNSu16
)(end
- (mDNSu8
*)pkt
));
4832 #ifdef _LEGACY_NAT_TRAVERSAL_
4833 if (mDNSSameIPPort(srcport
, SSDPPort
))
4836 LNT_ConfigureRouterInfo(m
, InterfaceID
, pkt
, (mDNSu16
)(end
- (mDNSu8
*)pkt
));
4843 if ((unsigned)(end
- (mDNSu8
*)pkt
) < sizeof(DNSMessageHeader
)) { LogMsg("DNS Message too short"); return; }
4844 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
4845 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
4846 ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
4847 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
4848 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
4849 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
4850 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
4852 if (!m
) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
4854 // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
4855 // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
4856 if (srcaddr
&& !mDNSAddressIsValid(srcaddr
)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr
); return; }
4860 #ifndef UNICAST_DISABLED
4861 if (!dstaddr
|| (!mDNSAddressIsAllDNSLinkGroup(dstaddr
) && (QR_OP
== StdR
|| QR_OP
== UpdR
)))
4863 if (!mDNSOpaque16IsZero(msg
->h
.id
)) ifid
= mDNSInterface_Any
;
4864 if (mDNS_LogLevel
>= MDNS_LOG_VERBOSE_DEBUG
)
4865 DumpPacket(m
, mDNSfalse
, TLS
? "TLS" : !dstaddr
? "TCP" : "UDP", srcaddr
, srcport
, msg
, end
);
4866 uDNS_ReceiveMsg(m
, msg
, end
, srcaddr
, srcport
);
4867 // Note: mDNSCore also needs to get access to received unicast responses
4870 if (QR_OP
== StdQ
) mDNSCoreReceiveQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, ifid
);
4871 else if (QR_OP
== StdR
) mDNSCoreReceiveResponse(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, ifid
);
4872 else if (QR_OP
!= UpdR
)
4874 LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
4875 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1], srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
);
4877 // Packet reception often causes a change to the task list:
4878 // 1. Inbound queries can cause us to need to send responses
4879 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
4880 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
4881 // 4. Response packets that answer questions may cause our client to issue new questions
4885 // ***************************************************************************
4886 #if COMPILER_LIKES_PRAGMA_MARK
4888 #pragma mark - Searcher Functions
4891 // Targets are considered the same if both queries are untargeted, or
4892 // if both are targeted to the same address+port
4893 // (If Target address is zero, TargetPort is undefined)
4894 #define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \
4895 (mDNSSameAddress(&(A)->Target, &(B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort)))
4897 mDNSlocal DNSQuestion
*FindDuplicateQuestion(const mDNS
*const m
, const DNSQuestion
*const question
)
4900 // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
4901 // This prevents circular references, where two questions are each marked as a duplicate of the other.
4902 // Accordingly, we break out of the loop when we get to 'question', because there's no point searching
4903 // further in the list.
4904 for (q
= m
->Questions
; q
&& q
!= question
; q
=q
->next
) // Scan our list for another question
4905 if (q
->InterfaceID
== question
->InterfaceID
&& // with the same InterfaceID,
4906 SameQTarget(q
, question
) && // and same unicast/multicast target settings
4907 q
->qtype
== question
->qtype
&& // type,
4908 q
->qclass
== question
->qclass
&& // class,
4909 q
->AuthInfo
== question
->AuthInfo
&& // and privacy status matches
4910 q
->LongLived
== question
->LongLived
&& // and long-lived status matches
4911 q
->qnamehash
== question
->qnamehash
&&
4912 SameDomainName(&q
->qname
, &question
->qname
)) // and name
4917 // This is called after a question is deleted, in case other identical questions were being
4918 // suppressed as duplicates
4919 mDNSlocal
void UpdateQuestionDuplicates(mDNS
*const m
, DNSQuestion
*const question
)
4922 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
4923 if (q
->DuplicateOf
== question
) // To see if any questions were referencing this as their duplicate
4924 if ((q
->DuplicateOf
= FindDuplicateQuestion(m
, q
)) == mDNSNULL
)
4926 // If q used to be a duplicate, but now is not,
4927 // then inherit the state from the question that's going away
4928 q
->LastQTime
= question
->LastQTime
;
4929 q
->ThisQInterval
= question
->ThisQInterval
;
4930 q
->ExpectUnicastResp
= question
->ExpectUnicastResp
;
4931 q
->LastAnswerPktNum
= question
->LastAnswerPktNum
;
4932 q
->RecentAnswerPkts
= question
->RecentAnswerPkts
;
4933 q
->RequestUnicast
= question
->RequestUnicast
;
4934 q
->LastQTxTime
= question
->LastQTxTime
;
4935 q
->CNAMEReferrals
= question
->CNAMEReferrals
;
4936 q
->nta
= question
->nta
;
4937 q
->servAddr
= question
->servAddr
;
4938 q
->servPort
= question
->servPort
;
4940 q
->state
= question
->state
;
4941 // q->NATInfoUDP = question->NATInfoUDP;
4942 q
->eventPort
= question
->eventPort
;
4943 // q->tcp = question->tcp;
4944 q
->origLease
= question
->origLease
;
4945 q
->expire
= question
->expire
;
4946 q
->ntries
= question
->ntries
;
4947 q
->id
= question
->id
;
4949 question
->nta
= mDNSNULL
; // If we've got a GetZoneData in progress, transfer it to the newly active question
4950 // question->NATInfoUDP = mDNSNULL;
4951 // question->tcp = mDNSNULL;
4954 LogOperation("UpdateQuestionDuplicates transferred nta pointer for %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4955 q
->nta
->ZoneDataContext
= q
;
4958 // Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash
4959 if (question
->tcp
) LogOperation("UpdateQuestionDuplicates did not transfer tcp pointer");
4961 SetNextQueryTime(m
,q
);
4965 // lookup a DNS Server, matching by name in split-dns configurations. Result stored in addr parameter if successful
4966 mDNSlocal DNSServer
*GetServerForName(mDNS
*m
, const domainname
*name
)
4968 DNSServer
*curmatch
= mDNSNULL
, *p
;
4969 int curmatchlen
= -1, ncount
= name
? CountLabels(name
) : 0;
4971 for (p
= m
->DNSServers
; p
; p
= p
->next
)
4973 int scount
= CountLabels(&p
->domain
);
4974 if (!p
->del
&& ncount
>= scount
&& scount
> curmatchlen
)
4975 if (SameDomainName(SkipLeadingLabels(name
, ncount
- scount
), &p
->domain
))
4976 { curmatch
= p
; curmatchlen
= scount
; }
4981 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
4982 (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort)))
4984 mDNSlocal
void ActivateUnicastQuery(mDNS
*const m
, DNSQuestion
*const question
)
4986 // For now this AutoTunnel stuff is specific to Mac OS X.
4987 // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
4988 #if APPLE_OSX_mDNSResponder
4989 if (question
->qtype
== kDNSType_AAAA
&& question
->AuthInfo
&& question
->AuthInfo
->AutoTunnel
&& question
->QuestionCallback
!= AutoTunnelCallback
)
4991 question
->NoAnswer
= NoAnswer_Suspended
;
4992 AddNewClientTunnel(m
, question
);
4995 #endif // APPLE_OSX_mDNSResponder
4997 if (!question
->DuplicateOf
)
4999 if (question
->LongLived
)
5001 question
->ThisQInterval
= 0; // Question is suspended, waiting for GetZoneData to complete
5002 question
->LastQTime
= m
->timenow
;
5003 LogOperation("uDNS_InitLongLivedQuery: %##s %s %s %d",
5004 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->AuthInfo
? "(Private)" : "", question
->ThisQInterval
);
5005 if (question
->nta
) CancelGetZoneData(m
, question
->nta
);
5006 question
->state
= LLQ_GetZoneInfo
; // Necessary to stop "bad state" error in startLLQHandshakeCallback
5007 question
->nta
= StartGetZoneData(m
, &question
->qname
, ZoneServiceLLQ
, startLLQHandshakeCallback
, question
);
5008 if (!question
->nta
) LogMsg("ERROR: startLLQ - StartGetZoneData failed");
5012 question
->ThisQInterval
= InitialQuestionInterval
;
5013 question
->LastQTime
= m
->timenow
- question
->ThisQInterval
;
5018 mDNSexport mStatus
mDNS_StartQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5020 if (question
->Target
.type
&& !ValidQuestionTarget(question
))
5022 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5023 question
->Target
.type
, mDNSVal16(question
->TargetPort
));
5024 question
->Target
.type
= mDNSAddrType_None
;
5027 if (!question
->Target
.type
) // No question->Target specified, so clear TargetPort and TargetQID
5029 question
->TargetPort
= zeroIPPort
;
5030 question
->TargetQID
= zeroID
;
5033 #ifndef UNICAST_DISABLED
5034 // If the client has specified 'kDNSServiceFlagsForceMulticast'
5035 // then we do a multicast query on that interface, even for unicast domains.
5036 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5037 question
->TargetQID
= zeroID
;
5039 question
->TargetQID
= mDNS_NewMessageID(m
);
5041 question
->TargetQID
= zeroID
;
5042 #endif // UNICAST_DISABLED
5044 debugf("mDNS_StartQuery: %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5046 if (m
->rrcache_size
== 0) // Can't do queries if we have no cache space allocated
5047 return(mStatus_NoCache
);
5053 if (!ValidateDomainName(&question
->qname
))
5055 LogMsg("Attempt to start query with invalid qname %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5056 return(mStatus_Invalid
);
5059 // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
5061 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5062 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5066 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5067 question
->qname
.c
, DNSTypeName(question
->qtype
));
5068 return(mStatus_AlreadyRegistered
);
5073 // If this question is referencing a specific interface, verify it exists
5074 if (question
->InterfaceID
&& question
->InterfaceID
!= mDNSInterface_LocalOnly
)
5076 NetworkInterfaceInfo
*intf
;
5077 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5078 if (intf
->InterfaceID
== question
->InterfaceID
) break;
5080 LogMsg("Note: InterfaceID %p for question %##s (%s) not currently found in active interface list",
5081 question
->InterfaceID
, question
->qname
.c
, DNSTypeName(question
->qtype
));
5084 // Note: In the case where we already have the answer to this question in our cache, that may be all the client
5085 // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
5086 // be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval).
5087 // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate
5088 // that to go out immediately.
5089 question
->next
= mDNSNULL
;
5090 question
->qnamehash
= DomainNameHashValue(&question
->qname
); // MUST do this before FindDuplicateQuestion()
5091 question
->DelayAnswering
= CheckForSoonToExpireRecords(m
, &question
->qname
, question
->qnamehash
, HashSlot(&question
->qname
));
5092 question
->LastQTime
= m
->timenow
;
5093 question
->ThisQInterval
= InitialQuestionInterval
; // MUST be > zero for an active question
5094 question
->ExpectUnicastResp
= 0;
5095 question
->LastAnswerPktNum
= m
->PktNum
;
5096 question
->RecentAnswerPkts
= 0;
5097 question
->CurrentAnswers
= 0;
5098 question
->LargeAnswers
= 0;
5099 question
->UniqueAnswers
= 0;
5100 question
->FlappingInterface1
= mDNSNULL
;
5101 question
->FlappingInterface2
= mDNSNULL
;
5102 // GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
5103 // because that would result in an infinite loop (i.e. to do a private query we first need to get
5104 // the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
5105 // we'd need to already know the _dns-query-tls SRV record.
5106 // Also: Make sure we set AuthInfo before calling FindDuplicateQuestion()
5107 question
->AuthInfo
= (question
->QuestionCallback
== GetZoneData_QuestionCallback
) ? mDNSNULL
5108 : GetAuthInfoForName_internal(m
, &question
->qname
);
5109 question
->DuplicateOf
= FindDuplicateQuestion(m
, question
);
5110 question
->NextInDQList
= mDNSNULL
;
5111 question
->SendQNow
= mDNSNULL
;
5112 question
->SendOnAll
= mDNSfalse
;
5113 question
->RequestUnicast
= 0;
5114 question
->LastQTxTime
= m
->timenow
;
5115 question
->CNAMEReferrals
= 0;
5117 question
->qDNSServer
= mDNSNULL
;
5118 question
->nta
= mDNSNULL
;
5119 question
->servAddr
= zeroAddr
;
5120 question
->servPort
= zeroIPPort
;
5121 question
->tcp
= mDNSNULL
;
5122 question
->NoAnswer
= NoAnswer_Normal
;
5124 question
->state
= LLQ_GetZoneInfo
;
5125 mDNSPlatformMemZero(&question
->NATInfoUDP
, sizeof(question
->NATInfoUDP
));
5126 question
->eventPort
= zeroIPPort
;
5127 question
->origLease
= 0;
5128 question
->expire
= 0;
5129 question
->ntries
= 0;
5130 question
->id
= zeroOpaque64
;
5132 for (i
=0; i
<DupSuppressInfoSize
; i
++)
5133 question
->DupSuppress
[i
].InterfaceID
= mDNSNULL
;
5135 if (!question
->DuplicateOf
)
5136 debugf("mDNS_StartQuery: Question %##s (%s) %p %d (%p) started",
5137 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
,
5138 question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
);
5140 debugf("mDNS_StartQuery: Question %##s (%s) %p %d (%p) duplicate of (%p)",
5141 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
,
5142 question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
, question
->DuplicateOf
);
5144 if (question
->InterfaceID
== mDNSInterface_LocalOnly
)
5146 if (!m
->NewLocalOnlyQuestions
) m
->NewLocalOnlyQuestions
= question
;
5150 if (!m
->NewQuestions
) m
->NewQuestions
= question
;
5152 // If the question's id is non-zero, then it's Wide Area
5153 // MUST NOT do this Wide Area setup until near the end of
5154 // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA,
5155 // NS, etc.) and if we haven't finished setting up our own question and setting
5156 // m->NewQuestions if necessary then we could end up recursively re-entering
5157 // this routine with the question list data structures in an inconsistent state.
5158 if (!mDNSOpaque16IsZero(question
->TargetQID
))
5160 question
->qDNSServer
= GetServerForName(m
, &question
->qname
);
5161 ActivateUnicastQuery(m
, question
);
5163 SetNextQueryTime(m
,question
);
5166 return(mStatus_NoError
);
5170 // CancelGetZoneData is an internal routine (i.e. must be called with the lock already held)
5171 mDNSexport
void CancelGetZoneData(mDNS
*const m
, ZoneData
*nta
)
5173 LogOperation("CancelGetZoneData %##s (%s)", nta
->question
.qname
.c
, DNSTypeName(nta
->question
.qtype
));
5174 mDNS_StopQuery_internal(m
, &nta
->question
);
5175 mDNSPlatformMemFree(nta
);
5178 mDNSexport mStatus
mDNS_StopQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5180 const mDNSu32 slot
= HashSlot(&question
->qname
);
5181 CacheGroup
*cg
= CacheGroupForName(m
, slot
, question
->qnamehash
, &question
->qname
);
5183 DNSQuestion
**q
= &m
->Questions
;
5185 //LogOperation("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5187 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5188 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5189 if (*q
) *q
= (*q
)->next
;
5192 if (question
->ThisQInterval
>= 0) // Only log error message if the query was supposed to be active
5193 LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
5194 question
->qname
.c
, DNSTypeName(question
->qtype
));
5198 return(mStatus_BadReferenceErr
);
5201 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
5202 UpdateQuestionDuplicates(m
, question
);
5203 // But don't trash ThisQInterval until afterwards.
5204 question
->ThisQInterval
= -1;
5206 // If there are any cache records referencing this as their active question, then see if there is any
5207 // other question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
5208 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5210 if (rr
->CRActiveQuestion
== question
)
5213 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5214 if (ActiveQuestion(q
) && ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
5216 debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s", q
, CRDisplayString(m
,rr
));
5217 rr
->CRActiveQuestion
= q
; // Question used to be active; new value may or may not be null
5218 if (!q
) m
->rrcache_active
--; // If no longer active, decrement rrcache_active count
5222 // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv() is about to look at,
5223 // bump its pointer forward one question.
5224 if (m
->CurrentQuestion
== question
)
5226 debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
5227 question
->qname
.c
, DNSTypeName(question
->qtype
));
5228 m
->CurrentQuestion
= question
->next
;
5231 if (m
->NewQuestions
== question
)
5233 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
5234 question
->qname
.c
, DNSTypeName(question
->qtype
));
5235 m
->NewQuestions
= question
->next
;
5238 if (m
->NewLocalOnlyQuestions
== question
) m
->NewLocalOnlyQuestions
= question
->next
;
5240 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5241 question
->next
= mDNSNULL
;
5243 // LogMsg("mDNS_StopQuery_internal: Question %##s (%s) removed", question->qname.c, DNSTypeName(question->qtype));
5245 // And finally, cancel any associated GetZoneData operation that's still running.
5246 // Must not do this until last, because there's a good chance the GetZoneData question is the next in the list,
5247 // so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already
5248 // invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary
5249 // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query.
5250 if (question
->nta
) CancelGetZoneData(m
, question
->nta
);
5251 if (question
->tcp
) { DisposeTCPConn(question
->tcp
); question
->tcp
= mDNSNULL
; }
5252 if (!mDNSOpaque16IsZero(question
->TargetQID
) && question
->LongLived
) uDNS_StopLongLivedQuery(m
, question
);
5254 return(mStatus_NoError
);
5257 mDNSexport mStatus
mDNS_StartQuery(mDNS
*const m
, DNSQuestion
*const question
)
5261 status
= mDNS_StartQuery_internal(m
, question
);
5266 mDNSexport mStatus
mDNS_StopQuery(mDNS
*const m
, DNSQuestion
*const question
)
5270 status
= mDNS_StopQuery_internal(m
, question
);
5275 // Note that mDNS_StopQueryWithRemoves() does not currently implement the full generality of the other APIs
5276 // Specifically, question callbacks invoked as a result of this call cannot themselves make API calls.
5277 // We invoke the callback without using mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback
5278 // specifically to catch and report if the client callback does try to make API calls
5279 mDNSexport mStatus
mDNS_StopQueryWithRemoves(mDNS
*const m
, DNSQuestion
*const question
)
5285 // Check if question is new -- don't want to give remove events for a question we haven't even answered yet
5286 for (qq
= m
->NewQuestions
; qq
; qq
=qq
->next
) if (qq
== question
) break;
5288 status
= mDNS_StopQuery_internal(m
, question
);
5289 if (status
== mStatus_NoError
&& !qq
)
5292 const mDNSu32 slot
= HashSlot(&question
->qname
);
5293 CacheGroup
*const cg
= CacheGroupForName(m
, slot
, question
->qnamehash
, &question
->qname
);
5294 LogOperation("Generating terminal removes for %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5295 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5296 if (rr
->resrec
.RecordType
!= kDNSRecordTypePacketNegative
&& SameNameRecordAnswersQuestion(&rr
->resrec
, question
))
5298 // Don't use mDNS_DropLockBeforeCallback() here, since we don't allow API calls
5299 if (question
->QuestionCallback
)
5300 question
->QuestionCallback(m
, question
, &rr
->resrec
, mDNSfalse
);
5307 mDNSexport mStatus
mDNS_Reconfirm(mDNS
*const m
, CacheRecord
*const cr
)
5311 status
= mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
5312 if (status
== mStatus_NoError
) ReconfirmAntecedents(m
, cr
->resrec
.name
, cr
->resrec
.namehash
, 0);
5317 mDNSexport mStatus
mDNS_ReconfirmByValue(mDNS
*const m
, ResourceRecord
*const rr
)
5319 mStatus status
= mStatus_BadReferenceErr
;
5322 cr
= FindIdenticalRecordInCache(m
, rr
);
5323 debugf("mDNS_ReconfirmByValue: %p %s", cr
, RRDisplayString(m
, rr
));
5324 if (cr
) status
= mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
5325 if (status
== mStatus_NoError
) ReconfirmAntecedents(m
, cr
->resrec
.name
, cr
->resrec
.namehash
, 0);
5330 mDNSexport mStatus
mDNS_StartBrowse(mDNS
*const m
, DNSQuestion
*const question
,
5331 const domainname
*const srv
, const domainname
*const domain
,
5332 const mDNSInterfaceID InterfaceID
, mDNSBool ForceMCast
, mDNSQuestionCallback
*Callback
, void *Context
)
5334 question
->InterfaceID
= InterfaceID
;
5335 question
->Target
= zeroAddr
;
5336 question
->qtype
= kDNSType_PTR
;
5337 question
->qclass
= kDNSClass_IN
;
5338 question
->LongLived
= mDNSfalse
;
5339 question
->ExpectUnique
= mDNSfalse
;
5340 question
->ForceMCast
= ForceMCast
;
5341 question
->ReturnIntermed
= mDNSfalse
;
5342 question
->QuestionCallback
= Callback
;
5343 question
->QuestionContext
= Context
;
5344 if (!ConstructServiceName(&question
->qname
, mDNSNULL
, srv
, domain
)) return(mStatus_BadParamErr
);
5346 #ifndef UNICAST_DISABLED
5347 if (question
->InterfaceID
!= mDNSInterface_LocalOnly
&& !question
->ForceMCast
&& !IsLocalDomain(&question
->qname
))
5349 question
->LongLived
= mDNStrue
;
5350 question
->ThisQInterval
= InitialQuestionInterval
;
5351 question
->LastQTime
= m
->timenow
- question
->ThisQInterval
;
5353 #endif // UNICAST_DISABLED
5354 return(mDNS_StartQuery(m
, question
));
5357 mDNSlocal mDNSBool
MachineHasActiveIPv6(mDNS
*const m
)
5359 NetworkInterfaceInfo
*intf
;
5360 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5361 if (intf
->ip
.type
== mDNSAddrType_IPv6
) return(mDNStrue
);
5365 mDNSlocal
void FoundServiceInfoSRV(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
5367 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5368 mDNSBool PortChanged
= !mDNSSameIPPort(query
->info
->port
, answer
->rdata
->u
.srv
.port
);
5369 if (!AddRecord
) return;
5370 if (answer
->rrtype
!= kDNSType_SRV
) return;
5372 query
->info
->port
= answer
->rdata
->u
.srv
.port
;
5374 // If this is our first answer, then set the GotSRV flag and start the address query
5377 query
->GotSRV
= mDNStrue
;
5378 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5379 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5380 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5381 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5382 mDNS_StartQuery(m
, &query
->qAv4
);
5383 // Only do the AAAA query if this machine actually has IPv6 active
5384 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5386 // If this is not our first answer, only re-issue the address query if the target host name has changed
5387 else if ((query
->qAv4
.InterfaceID
!= query
->qSRV
.InterfaceID
&& query
->qAv4
.InterfaceID
!= answer
->InterfaceID
) ||
5388 !SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
))
5390 mDNS_StopQuery(m
, &query
->qAv4
);
5391 if (query
->qAv6
.ThisQInterval
>= 0) mDNS_StopQuery(m
, &query
->qAv6
);
5392 if (SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
) && !PortChanged
)
5394 // If we get here, it means:
5395 // 1. This is not our first SRV answer
5396 // 2. The interface ID is different, but the target host and port are the same
5397 // This implies that we're seeing the exact same SRV record on more than one interface, so we should
5398 // make our address queries at least as broad as the original SRV query so that we catch all the answers.
5399 query
->qAv4
.InterfaceID
= query
->qSRV
.InterfaceID
; // Will be mDNSInterface_Any, or a specific interface
5400 query
->qAv6
.InterfaceID
= query
->qSRV
.InterfaceID
;
5404 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5405 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5406 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5407 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5409 debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query
->qAv4
.qname
.c
, DNSTypeName(query
->qAv4
.qtype
));
5410 mDNS_StartQuery(m
, &query
->qAv4
);
5411 // Only do the AAAA query if this machine actually has IPv6 active
5412 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5414 else if (query
->ServiceInfoQueryCallback
&& query
->GotADD
&& query
->GotTXT
&& PortChanged
)
5416 if (++query
->Answers
>= 100)
5417 debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
5418 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.srv
.target
.c
,
5419 mDNSVal16(answer
->rdata
->u
.srv
.port
));
5420 query
->ServiceInfoQueryCallback(m
, query
);
5422 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5423 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5426 mDNSlocal
void FoundServiceInfoTXT(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
5428 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5429 if (!AddRecord
) return;
5430 if (answer
->rrtype
!= kDNSType_TXT
) return;
5431 if (answer
->rdlength
> sizeof(query
->info
->TXTinfo
)) return;
5433 query
->GotTXT
= mDNStrue
;
5434 query
->info
->TXTlen
= answer
->rdlength
;
5435 query
->info
->TXTinfo
[0] = 0; // In case answer->rdlength is zero
5436 mDNSPlatformMemCopy(query
->info
->TXTinfo
, answer
->rdata
->u
.txt
.c
, answer
->rdlength
);
5438 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query
->info
->name
.c
, query
->GotADD
);
5440 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5441 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5442 if (query
->ServiceInfoQueryCallback
&& query
->GotADD
)
5444 if (++query
->Answers
>= 100)
5445 debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
5446 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.txt
.c
);
5447 query
->ServiceInfoQueryCallback(m
, query
);
5451 mDNSlocal
void FoundServiceInfo(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
5453 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5454 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
5455 if (!AddRecord
) return;
5457 if (answer
->rrtype
== kDNSType_A
)
5459 query
->info
->ip
.type
= mDNSAddrType_IPv4
;
5460 query
->info
->ip
.ip
.v4
= answer
->rdata
->u
.ipv4
;
5462 else if (answer
->rrtype
== kDNSType_AAAA
)
5464 query
->info
->ip
.type
= mDNSAddrType_IPv6
;
5465 query
->info
->ip
.ip
.v6
= answer
->rdata
->u
.ipv6
;
5469 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer
->name
->c
, answer
->rrtype
, DNSTypeName(answer
->rrtype
));
5473 query
->GotADD
= mDNStrue
;
5474 query
->info
->InterfaceID
= answer
->InterfaceID
;
5476 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query
->info
->ip
.type
, query
->info
->name
.c
, query
->GotTXT
);
5478 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5479 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5480 if (query
->ServiceInfoQueryCallback
&& query
->GotTXT
)
5482 if (++query
->Answers
>= 100)
5483 debugf(answer
->rrtype
== kDNSType_A
?
5484 "**** WARNING **** have given %lu answers for %##s (A) %.4a" :
5485 "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a",
5486 query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.data
);
5487 query
->ServiceInfoQueryCallback(m
, query
);
5491 // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
5492 // If the query is not interface-specific, then InterfaceID may be zero
5493 // Each time the Callback is invoked, the remainder of the fields will have been filled in
5494 // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
5495 mDNSexport mStatus
mDNS_StartResolveService(mDNS
*const m
,
5496 ServiceInfoQuery
*query
, ServiceInfo
*info
, mDNSServiceInfoQueryCallback
*Callback
, void *Context
)
5501 query
->qSRV
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5502 query
->qSRV
.InterfaceID
= info
->InterfaceID
;
5503 query
->qSRV
.Target
= zeroAddr
;
5504 AssignDomainName(&query
->qSRV
.qname
, &info
->name
);
5505 query
->qSRV
.qtype
= kDNSType_SRV
;
5506 query
->qSRV
.qclass
= kDNSClass_IN
;
5507 query
->qSRV
.LongLived
= mDNSfalse
;
5508 query
->qSRV
.ExpectUnique
= mDNStrue
;
5509 query
->qSRV
.ForceMCast
= mDNSfalse
;
5510 query
->qSRV
.ReturnIntermed
= mDNSfalse
;
5511 query
->qSRV
.QuestionCallback
= FoundServiceInfoSRV
;
5512 query
->qSRV
.QuestionContext
= query
;
5514 query
->qTXT
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5515 query
->qTXT
.InterfaceID
= info
->InterfaceID
;
5516 query
->qTXT
.Target
= zeroAddr
;
5517 AssignDomainName(&query
->qTXT
.qname
, &info
->name
);
5518 query
->qTXT
.qtype
= kDNSType_TXT
;
5519 query
->qTXT
.qclass
= kDNSClass_IN
;
5520 query
->qTXT
.LongLived
= mDNSfalse
;
5521 query
->qTXT
.ExpectUnique
= mDNStrue
;
5522 query
->qTXT
.ForceMCast
= mDNSfalse
;
5523 query
->qTXT
.ReturnIntermed
= mDNSfalse
;
5524 query
->qTXT
.QuestionCallback
= FoundServiceInfoTXT
;
5525 query
->qTXT
.QuestionContext
= query
;
5527 query
->qAv4
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5528 query
->qAv4
.InterfaceID
= info
->InterfaceID
;
5529 query
->qAv4
.Target
= zeroAddr
;
5530 query
->qAv4
.qname
.c
[0] = 0;
5531 query
->qAv4
.qtype
= kDNSType_A
;
5532 query
->qAv4
.qclass
= kDNSClass_IN
;
5533 query
->qAv4
.LongLived
= mDNSfalse
;
5534 query
->qAv4
.ExpectUnique
= mDNStrue
;
5535 query
->qAv4
.ForceMCast
= mDNSfalse
;
5536 query
->qAv4
.ReturnIntermed
= mDNSfalse
;
5537 query
->qAv4
.QuestionCallback
= FoundServiceInfo
;
5538 query
->qAv4
.QuestionContext
= query
;
5540 query
->qAv6
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5541 query
->qAv6
.InterfaceID
= info
->InterfaceID
;
5542 query
->qAv6
.Target
= zeroAddr
;
5543 query
->qAv6
.qname
.c
[0] = 0;
5544 query
->qAv6
.qtype
= kDNSType_AAAA
;
5545 query
->qAv6
.qclass
= kDNSClass_IN
;
5546 query
->qAv6
.LongLived
= mDNSfalse
;
5547 query
->qAv6
.ExpectUnique
= mDNStrue
;
5548 query
->qAv6
.ForceMCast
= mDNSfalse
;
5549 query
->qAv6
.ReturnIntermed
= mDNSfalse
;
5550 query
->qAv6
.QuestionCallback
= FoundServiceInfo
;
5551 query
->qAv6
.QuestionContext
= query
;
5553 query
->GotSRV
= mDNSfalse
;
5554 query
->GotTXT
= mDNSfalse
;
5555 query
->GotADD
= mDNSfalse
;
5559 query
->ServiceInfoQueryCallback
= Callback
;
5560 query
->ServiceInfoQueryContext
= Context
;
5562 // info->name = Must already be set up by client
5563 // info->interface = Must already be set up by client
5564 info
->ip
= zeroAddr
;
5565 info
->port
= zeroIPPort
;
5568 // We use mDNS_StartQuery_internal here because we're already holding the lock
5569 status
= mDNS_StartQuery_internal(m
, &query
->qSRV
);
5570 if (status
== mStatus_NoError
) status
= mDNS_StartQuery_internal(m
, &query
->qTXT
);
5571 if (status
!= mStatus_NoError
) mDNS_StopResolveService(m
, query
);
5577 mDNSexport
void mDNS_StopResolveService (mDNS
*const m
, ServiceInfoQuery
*q
)
5580 // We use mDNS_StopQuery_internal here because we're already holding the lock
5581 if (q
->qSRV
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qSRV
);
5582 if (q
->qTXT
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qTXT
);
5583 if (q
->qAv4
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qAv4
);
5584 if (q
->qAv6
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qAv6
);
5588 mDNSexport mStatus
mDNS_GetDomains(mDNS
*const m
, DNSQuestion
*const question
, mDNS_DomainType DomainType
, const domainname
*dom
,
5589 const mDNSInterfaceID InterfaceID
, mDNSQuestionCallback
*Callback
, void *Context
)
5591 question
->InterfaceID
= InterfaceID
;
5592 question
->Target
= zeroAddr
;
5593 question
->qtype
= kDNSType_PTR
;
5594 question
->qclass
= kDNSClass_IN
;
5595 question
->LongLived
= mDNSfalse
;
5596 question
->ExpectUnique
= mDNSfalse
;
5597 question
->ForceMCast
= mDNSfalse
;
5598 question
->ReturnIntermed
= mDNSfalse
;
5599 question
->QuestionCallback
= Callback
;
5600 question
->QuestionContext
= Context
;
5601 if (DomainType
> mDNS_DomainTypeMax
) return(mStatus_BadParamErr
);
5602 if (!MakeDomainNameFromDNSNameString(&question
->qname
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
5603 if (!dom
) dom
= &localdomain
;
5604 if (!AppendDomainName(&question
->qname
, dom
)) return(mStatus_BadParamErr
);
5605 return(mDNS_StartQuery(m
, question
));
5608 // ***************************************************************************
5609 #if COMPILER_LIKES_PRAGMA_MARK
5611 #pragma mark - Responder Functions
5614 mDNSexport mStatus
mDNS_Register(mDNS
*const m
, AuthRecord
*const rr
)
5618 status
= mDNS_Register_internal(m
, rr
);
5623 mDNSexport mStatus
mDNS_Update(mDNS
*const m
, AuthRecord
*const rr
, mDNSu32 newttl
,
5624 const mDNSu16 newrdlength
, RData
*const newrdata
, mDNSRecordUpdateCallback
*Callback
)
5626 #ifndef UNICAST_DISABLED
5627 mDNSBool unicast
= !(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(rr
->resrec
.name
));
5629 mDNSBool unicast
= mDNSfalse
;
5632 if (!ValidateRData(rr
->resrec
.rrtype
, newrdlength
, newrdata
))
5634 LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr
->resrec
, &newrdata
->u
, m
->MsgBuffer
));
5635 return(mStatus_Invalid
);
5640 // If TTL is unspecified, leave TTL unchanged
5641 if (newttl
== 0) newttl
= rr
->resrec
.rroriginalttl
;
5643 // If we already have an update queued up which has not gone through yet,
5644 // give the client a chance to free that memory
5645 if (!unicast
&& rr
->NewRData
)
5647 RData
*n
= rr
->NewRData
;
5648 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
5649 if (rr
->UpdateCallback
)
5650 rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
5653 rr
->NewRData
= newrdata
;
5654 rr
->newrdlength
= newrdlength
;
5655 rr
->UpdateCallback
= Callback
;
5657 if (unicast
) { mStatus status
= uDNS_UpdateRecord(m
, rr
); mDNS_Unlock(m
); return(status
); }
5659 if (rr
->resrec
.rroriginalttl
== newttl
&&
5660 rr
->resrec
.rdlength
== newrdlength
&& mDNSPlatformMemSame(rr
->resrec
.rdata
->u
.data
, newrdata
->u
.data
, newrdlength
))
5661 CompleteRDataUpdate(m
, rr
);
5665 domainname type
, domain
;
5666 DeconstructServiceName(rr
->resrec
.name
, &name
, &type
, &domain
);
5667 rr
->AnnounceCount
= InitialAnnounceCount
;
5668 // iChat often does suprious record updates where no data has changed. For the _presence service type, using
5669 // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
5670 // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
5671 // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
5672 // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
5673 if (SameDomainLabel(type
.c
, (mDNSu8
*)"\x6_ichat")) rr
->AnnounceCount
= 1;
5674 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
5675 InitializeLastAPTime(m
, rr
);
5676 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
5677 if (!rr
->UpdateBlocked
&& rr
->UpdateCredits
) rr
->UpdateCredits
--;
5678 if (!rr
->NextUpdateCredit
) rr
->NextUpdateCredit
= NonZeroTime(m
->timenow
+ kUpdateCreditRefreshInterval
);
5679 if (rr
->AnnounceCount
> rr
->UpdateCredits
+ 1) rr
->AnnounceCount
= (mDNSu8
)(rr
->UpdateCredits
+ 1);
5680 if (rr
->UpdateCredits
<= 5)
5682 mDNSu32 delay
= 6 - rr
->UpdateCredits
; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
5683 if (!rr
->UpdateBlocked
) rr
->UpdateBlocked
= NonZeroTime(m
->timenow
+ (mDNSs32
)delay
* mDNSPlatformOneSecond
);
5684 rr
->ThisAPInterval
*= 4;
5685 rr
->LastAPTime
= rr
->UpdateBlocked
- rr
->ThisAPInterval
;
5686 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s",
5687 rr
->resrec
.name
->c
, delay
, delay
> 1 ? "s" : "");
5689 rr
->resrec
.rroriginalttl
= newttl
;
5693 return(mStatus_NoError
);
5696 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
5697 // the record list and/or question list.
5698 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
5699 mDNSexport mStatus
mDNS_Deregister(mDNS
*const m
, AuthRecord
*const rr
)
5703 status
= mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
5708 // Circular reference: AdvertiseInterface references mDNS_HostNameCallback, which calls mDNS_SetFQDN, which call AdvertiseInterface
5709 mDNSlocal
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
);
5711 mDNSlocal NetworkInterfaceInfo
*FindFirstAdvertisedInterface(mDNS
*const m
)
5713 NetworkInterfaceInfo
*intf
;
5714 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5715 if (intf
->Advertise
) break;
5719 mDNSlocal
void AdvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
5721 char buffer
[MAX_REVERSE_MAPPING_NAME
];
5722 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
5723 if (!primary
) primary
= set
; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
5725 // Send dynamic update for non-linklocal IPv4 Addresses
5726 mDNS_SetupResourceRecord(&set
->RR_A
, mDNSNULL
, set
->InterfaceID
, kDNSType_A
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNS_HostNameCallback
, set
);
5727 mDNS_SetupResourceRecord(&set
->RR_PTR
, mDNSNULL
, set
->InterfaceID
, kDNSType_PTR
, kHostNameTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
5728 mDNS_SetupResourceRecord(&set
->RR_HINFO
, mDNSNULL
, set
->InterfaceID
, kDNSType_HINFO
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
5730 #if ANSWER_REMOTE_HOSTNAME_QUERIES
5731 set
->RR_A
.AllowRemoteQuery
= mDNStrue
;
5732 set
->RR_PTR
.AllowRemoteQuery
= mDNStrue
;
5733 set
->RR_HINFO
.AllowRemoteQuery
= mDNStrue
;
5735 // 1. Set up Address record to map from host name ("foo.local.") to IP address
5736 // 2. Set up reverse-lookup PTR record to map from our address back to our host name
5737 AssignDomainName(&set
->RR_A
.namestorage
, &m
->MulticastHostname
);
5738 if (set
->ip
.type
== mDNSAddrType_IPv4
)
5740 set
->RR_A
.resrec
.rrtype
= kDNSType_A
;
5741 set
->RR_A
.resrec
.rdata
->u
.ipv4
= set
->ip
.ip
.v4
;
5742 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
5743 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.",
5744 set
->ip
.ip
.v4
.b
[3], set
->ip
.ip
.v4
.b
[2], set
->ip
.ip
.v4
.b
[1], set
->ip
.ip
.v4
.b
[0]);
5746 else if (set
->ip
.type
== mDNSAddrType_IPv6
)
5749 set
->RR_A
.resrec
.rrtype
= kDNSType_AAAA
;
5750 set
->RR_A
.resrec
.rdata
->u
.ipv6
= set
->ip
.ip
.v6
;
5751 for (i
= 0; i
< 16; i
++)
5753 static const char hexValues
[] = "0123456789ABCDEF";
5754 buffer
[i
* 4 ] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] & 0x0F];
5755 buffer
[i
* 4 + 1] = '.';
5756 buffer
[i
* 4 + 2] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] >> 4];
5757 buffer
[i
* 4 + 3] = '.';
5759 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
5762 MakeDomainNameFromDNSNameString(&set
->RR_PTR
.namestorage
, buffer
);
5763 set
->RR_PTR
.AutoTarget
= Target_AutoHost
; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
5764 set
->RR_PTR
.ForceMCast
= mDNStrue
; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server
5766 set
->RR_A
.RRSet
= &primary
->RR_A
; // May refer to self
5768 mDNS_Register_internal(m
, &set
->RR_A
);
5769 mDNS_Register_internal(m
, &set
->RR_PTR
);
5771 if (!NO_HINFO
&& m
->HIHardware
.c
[0] > 0 && m
->HISoftware
.c
[0] > 0 && m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0] <= 254)
5773 mDNSu8
*p
= set
->RR_HINFO
.resrec
.rdata
->u
.data
;
5774 AssignDomainName(&set
->RR_HINFO
.namestorage
, &m
->MulticastHostname
);
5775 set
->RR_HINFO
.DependentOn
= &set
->RR_A
;
5776 mDNSPlatformMemCopy(p
, &m
->HIHardware
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
5778 mDNSPlatformMemCopy(p
, &m
->HISoftware
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
5779 mDNS_Register_internal(m
, &set
->RR_HINFO
);
5783 debugf("Not creating HINFO record: platform support layer provided no information");
5784 set
->RR_HINFO
.resrec
.RecordType
= kDNSRecordTypeUnregistered
;
5788 mDNSlocal
void DeadvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
5790 NetworkInterfaceInfo
*intf
;
5792 // If we still have address records referring to this one, update them
5793 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
5794 AuthRecord
*A
= primary
? &primary
->RR_A
: mDNSNULL
;
5795 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5796 if (intf
->RR_A
.RRSet
== &set
->RR_A
)
5797 intf
->RR_A
.RRSet
= A
;
5799 // Unregister these records.
5800 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
5801 // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
5802 // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
5803 // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
5804 if (set
->RR_A
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_A
, mDNS_Dereg_normal
);
5805 if (set
->RR_PTR
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_PTR
, mDNS_Dereg_normal
);
5806 if (set
->RR_HINFO
.resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_HINFO
, mDNS_Dereg_normal
);
5809 mDNSexport
void mDNS_SetFQDN(mDNS
*const m
)
5811 domainname newmname
;
5812 NetworkInterfaceInfo
*intf
;
5816 if (!AppendDomainLabel(&newmname
, &m
->hostlabel
)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
5817 if (!AppendLiteralLabelString(&newmname
, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
5818 if (SameDomainNameCS(&m
->MulticastHostname
, &newmname
)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
5822 AssignDomainName(&m
->MulticastHostname
, &newmname
);
5823 // 1. Stop advertising our address records on all interfaces
5824 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5825 if (intf
->Advertise
) DeadvertiseInterface(m
, intf
);
5827 // 2. Start advertising our address records using the new name
5828 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5829 if (intf
->Advertise
) AdvertiseInterface(m
, intf
);
5831 // 3. Make sure that any SRV records (and the like) that reference our
5832 // host name in their rdata get updated to reference this new host name
5833 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) if (rr
->AutoTarget
) SetTargetToHostName(m
, rr
);
5834 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
) if (rr
->AutoTarget
) SetTargetToHostName(m
, rr
);
5839 mDNSlocal
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
5841 (void)rr
; // Unused parameter
5845 char *msg
= "Unknown result";
5846 if (result
== mStatus_NoError
) msg
= "Name registered";
5847 else if (result
== mStatus_NameConflict
) msg
= "Name conflict";
5848 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
5852 if (result
== mStatus_NoError
)
5854 // Notify the client that the host name is successfully registered
5855 if (m
->MainCallback
)
5856 m
->MainCallback(m
, mStatus_NoError
);
5858 else if (result
== mStatus_NameConflict
)
5860 domainlabel oldlabel
= m
->hostlabel
;
5862 // 1. First give the client callback a chance to pick a new name
5863 if (m
->MainCallback
)
5864 m
->MainCallback(m
, mStatus_NameConflict
);
5866 // 2. If the client callback didn't do it, add (or increment) an index ourselves
5867 // This needs to be case-insensitive compare, because we need to know that the name has been changed so as to
5868 // remedy the conflict, and a name that differs only in capitalization will just suffer the exact same conflict again.
5869 if (SameDomainLabelCS(m
->hostlabel
.c
, oldlabel
.c
))
5870 IncrementLabelSuffix(&m
->hostlabel
, mDNSfalse
);
5872 // 3. Generate the FQDNs from the hostlabel,
5873 // and make sure all SRV records, etc., are updated to reference our new hostname
5875 LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel
.c
, m
->hostlabel
.c
);
5877 else if (result
== mStatus_MemFree
)
5879 // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
5880 // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
5881 debugf("mDNS_HostNameCallback: MemFree (ignored)");
5884 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result
, rr
->resrec
.name
->c
);
5887 mDNSlocal
void UpdateInterfaceProtocols(mDNS
*const m
, NetworkInterfaceInfo
*active
)
5889 NetworkInterfaceInfo
*intf
;
5890 active
->IPv4Available
= mDNSfalse
;
5891 active
->IPv6Available
= mDNSfalse
;
5892 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5893 if (intf
->InterfaceID
== active
->InterfaceID
)
5895 if (intf
->ip
.type
== mDNSAddrType_IPv4
&& intf
->McastTxRx
) active
->IPv4Available
= mDNStrue
;
5896 if (intf
->ip
.type
== mDNSAddrType_IPv6
&& intf
->McastTxRx
) active
->IPv6Available
= mDNStrue
;
5900 mDNSexport mStatus
mDNS_RegisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, mDNSBool flapping
)
5902 mDNSBool FirstOfType
= mDNStrue
;
5903 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
5905 if (!set
->InterfaceID
)
5906 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set
->ip
); return(mStatus_Invalid
); }
5908 if (!mDNSAddressIsValidNonZero(&set
->mask
))
5909 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set
->ip
, &set
->mask
); return(mStatus_Invalid
); }
5913 // Assume this interface will be active now, unless we find a duplicate already in the list
5914 set
->InterfaceActive
= mDNStrue
;
5915 set
->IPv4Available
= (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
);
5916 set
->IPv6Available
= (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
);
5918 // Scan list to see if this InterfaceID is already represented
5923 LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
5925 return(mStatus_AlreadyRegistered
);
5928 if ((*p
)->InterfaceID
== set
->InterfaceID
)
5930 // This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now
5931 set
->InterfaceActive
= mDNSfalse
;
5932 if (set
->ip
.type
== (*p
)->ip
.type
) FirstOfType
= mDNSfalse
;
5933 if (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
) (*p
)->IPv4Available
= mDNStrue
;
5934 if (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
) (*p
)->IPv6Available
= mDNStrue
;
5940 set
->next
= mDNSNULL
;
5944 AdvertiseInterface(m
, set
);
5946 LogOperation("mDNS_RegisterInterface: InterfaceID %p %s (%#a) %s", set
->InterfaceID
, set
->ifname
, &set
->ip
,
5947 set
->InterfaceActive
?
5948 "not represented in list; marking active and retriggering queries" :
5949 "already represented in list; marking inactive for now");
5951 // In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
5952 // giving the false impression that there's an active representative of this interface when there really isn't.
5953 // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
5954 // even if we believe that we previously had an active representative of this interface.
5955 if (set
->McastTxRx
&& ((m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) || FirstOfType
|| set
->InterfaceActive
))
5959 // If flapping, delay between first and second queries is eight seconds instead of one
5960 mDNSs32 delay
= flapping
? mDNSPlatformOneSecond
* 5 : 0;
5961 mDNSu8 announce
= flapping
? (mDNSu8
)1 : InitialAnnounceCount
;
5963 // Use a small amount of randomness:
5964 // In the case of a network administrator turning on an Ethernet hub so that all the
5965 // connected machines establish link at exactly the same time, we don't want them all
5966 // to go and hit the network with identical queries at exactly the same moment.
5967 if (!m
->SuppressSending
) m
->SuppressSending
= m
->timenow
+ (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
5971 LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", set
->ifname
, &set
->ip
);
5972 if (!m
->SuppressProbes
||
5973 m
->SuppressProbes
- (m
->timenow
+ delay
) < 0)
5974 m
->SuppressProbes
= (m
->timenow
+ delay
);
5977 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5978 if (mDNSOpaque16IsZero(q
->TargetQID
))
5979 if (!q
->InterfaceID
|| q
->InterfaceID
== set
->InterfaceID
) // If non-specific Q, or Q on this specific interface,
5980 { // then reactivate this question
5981 mDNSBool dodelay
= flapping
&& (q
->FlappingInterface1
== set
->InterfaceID
|| q
->FlappingInterface2
== set
->InterfaceID
);
5982 mDNSs32 initial
= dodelay
? InitialQuestionInterval
* QuestionIntervalStep2
: InitialQuestionInterval
;
5983 mDNSs32 qdelay
= dodelay
? mDNSPlatformOneSecond
* 5 : 0;
5984 if (dodelay
) LogOperation("No cache records for expired %##s (%s); okay to delay questions a little", q
->qname
.c
, DNSTypeName(q
->qtype
));
5986 if (!q
->ThisQInterval
|| q
->ThisQInterval
> initial
)
5988 q
->ThisQInterval
= initial
;
5989 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
5991 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
+ qdelay
;
5992 q
->RecentAnswerPkts
= 0;
5993 SetNextQueryTime(m
,q
);
5996 // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
5997 // we now need them to re-probe if necessary, and then re-announce.
5998 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
5999 if (!rr
->resrec
.InterfaceID
|| rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6001 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
6002 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
6003 if (rr
->AnnounceCount
< announce
) rr
->AnnounceCount
= announce
;
6004 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6005 InitializeLastAPTime(m
, rr
);
6010 return(mStatus_NoError
);
6013 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
6014 // the record list and/or question list.
6015 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6016 mDNSexport
void mDNS_DeregisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, mDNSBool flapping
)
6018 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6020 mDNSBool revalidate
= mDNSfalse
;
6021 // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
6022 // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
6023 // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
6024 if (m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) revalidate
= mDNStrue
;
6028 // Find this record in our list
6029 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
6030 if (!*p
) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m
); return; }
6032 // Unlink this record from our list
6034 set
->next
= mDNSNULL
;
6036 if (!set
->InterfaceActive
)
6038 // If this interface not the active member of its set, update the v4/v6Available flags for the active member
6039 NetworkInterfaceInfo
*intf
;
6040 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6041 if (intf
->InterfaceActive
&& intf
->InterfaceID
== set
->InterfaceID
)
6042 UpdateInterfaceProtocols(m
, intf
);
6046 NetworkInterfaceInfo
*intf
;
6047 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6048 if (intf
->InterfaceID
== set
->InterfaceID
)
6052 LogOperation("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;"
6053 " making it active", set
->InterfaceID
, set
->ifname
, &set
->ip
);
6054 intf
->InterfaceActive
= mDNStrue
;
6055 UpdateInterfaceProtocols(m
, intf
);
6057 // See if another representative *of the same type* exists. If not, we mave have gone from
6058 // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
6059 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6060 if (intf
->InterfaceID
== set
->InterfaceID
&& intf
->ip
.type
== set
->ip
.type
)
6062 if (!intf
) revalidate
= mDNStrue
;
6072 LogOperation("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;"
6073 " marking questions etc. dormant", set
->InterfaceID
, set
->ifname
, &set
->ip
);
6076 LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect",
6077 set
->ifname
, &set
->ip
);
6079 // 1. Deactivate any questions specific to this interface, and tag appropriate questions
6080 // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them
6081 for (q
= m
->Questions
; q
; q
=q
->next
)
6083 if (q
->InterfaceID
== set
->InterfaceID
) q
->ThisQInterval
= 0;
6084 if (!q
->InterfaceID
|| q
->InterfaceID
== set
->InterfaceID
)
6086 q
->FlappingInterface2
= q
->FlappingInterface1
;
6087 q
->FlappingInterface1
= set
->InterfaceID
; // Keep history of the last two interfaces to go away
6091 // 2. Flush any cache records received on this interface
6092 revalidate
= mDNSfalse
; // Don't revalidate if we're flushing the records
6093 FORALL_CACHERECORDS(slot
, cg
, rr
)
6094 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6096 // If this interface is deemed flapping,
6097 // postpone deleting the cache records in case the interface comes back again
6098 if (!flapping
) mDNS_PurgeCacheResourceRecord(m
, rr
);
6101 // We want these record to go away in 30 seconds
6102 // We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them --
6103 // if the interface does come back, any relevant questions will be reactivated anyway
6104 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForFlappingInterface
);
6105 rr
->UnansweredQueries
= MaxUnansweredQueries
;
6109 // 3. Any DNS servers specific to this interface are now unusable
6110 for (s
= m
->DNSServers
; s
; s
= s
->next
)
6111 if (s
->interface
== set
->InterfaceID
)
6113 s
->interface
= mDNSInterface_Any
;
6114 s
->teststate
= DNSServer_Disabled
;
6119 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6120 if (set
->Advertise
) DeadvertiseInterface(m
, set
);
6122 // If we have any cache records received on this interface that went away, then re-verify them.
6123 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6124 // giving the false impression that there's an active representative of this interface when there really isn't.
6125 // Don't need to do this when shutting down, because *all* interfaces are about to go away
6126 if (revalidate
&& !m
->mDNS_shutdown
)
6131 m
->NextCacheCheck
= m
->timenow
;
6132 FORALL_CACHERECORDS(slot
, cg
, rr
)
6133 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6134 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForFlappingInterface
);
6140 mDNSlocal
void ServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6142 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6143 (void)m
; // Unused parameter
6147 char *msg
= "Unknown result";
6148 if (result
== mStatus_NoError
) msg
= "Name Registered";
6149 else if (result
== mStatus_NameConflict
) msg
= "Name Conflict";
6150 else if (result
== mStatus_MemFree
) msg
= "Memory Free";
6151 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6155 // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
6156 if (result
== mStatus_NoError
&& rr
!= &sr
->RR_SRV
) return;
6158 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6159 if (result
== mStatus_NameConflict
)
6161 sr
->Conflict
= mDNStrue
; // Record that this service set had a conflict
6162 mDNS_DeregisterService(m
, sr
); // Unlink the records from our list
6166 if (result
== mStatus_MemFree
)
6168 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
6169 // don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
6171 if (sr
->RR_PTR
.resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6172 for (i
=0; i
<sr
->NumSubTypes
; i
++) if (sr
->SubTypes
[i
].resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6174 // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
6175 // then we can now report the NameConflict to the client
6176 if (sr
->Conflict
) result
= mStatus_NameConflict
;
6179 // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
6180 // function is allowed to do anything, including deregistering this service and freeing its memory.
6181 if (sr
->ServiceCallback
)
6182 sr
->ServiceCallback(m
, sr
, result
);
6185 mDNSlocal
void NSSCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6187 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6188 if (sr
->ServiceCallback
)
6189 sr
->ServiceCallback(m
, sr
, result
);
6192 mDNSlocal mStatus
uDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*srs
)
6195 ServiceRecordSet
**p
= &m
->ServiceRegistrations
;
6196 while (*p
&& *p
!= srs
) p
=&(*p
)->uDNS_next
;
6197 if (*p
) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs
, srs
->RR_SRV
.resrec
.name
->c
); return(mStatus_AlreadyRegistered
); }
6199 srs
->uDNS_next
= mDNSNULL
;
6202 srs
->RR_SRV
.resrec
.rroriginalttl
= kHostNameTTL
;
6203 srs
->RR_TXT
.resrec
.rroriginalttl
= kStandardTTL
;
6204 srs
->RR_PTR
.resrec
.rroriginalttl
= kStandardTTL
;
6205 for (i
= 0; i
< srs
->NumSubTypes
;i
++) srs
->SubTypes
[i
].resrec
.rroriginalttl
= kStandardTTL
;
6207 srs
->srs_uselease
= mDNStrue
;
6209 if (srs
->RR_SRV
.AutoTarget
)
6211 // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other
6212 // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate,
6213 // with the port number in our advertised SRV record automatically tracking the external mapped port.
6214 DomainAuthInfo
*AuthInfo
= GetAuthInfoForName_internal(m
, srs
->RR_SRV
.resrec
.name
);
6215 if (!AuthInfo
|| !AuthInfo
->AutoTunnel
) srs
->RR_SRV
.AutoTarget
= Target_AutoHostAndNATMAP
;
6218 if (!GetServiceTarget(m
, srs
))
6220 // defer registration until we've got a target
6221 debugf("uDNS_RegisterService - no target for %##s", srs
->RR_SRV
.resrec
.name
->c
);
6222 srs
->state
= regState_NoTarget
;
6223 srs
->nta
= mDNSNULL
;
6224 return mStatus_NoError
;
6227 srs
->state
= regState_FetchingZoneData
;
6228 srs
->nta
= StartGetZoneData(m
, srs
->RR_SRV
.resrec
.name
, ZoneServiceUpdate
, ServiceRegistrationZoneDataComplete
, srs
);
6229 return srs
->nta
? mStatus_NoError
: mStatus_NoMemoryErr
;
6233 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
6234 // Type is service type (e.g. "_ipp._tcp.")
6235 // Domain is fully qualified domain name (i.e. ending with a null label)
6236 // We always register a TXT, even if it is empty (so that clients are not
6237 // left waiting forever looking for a nonexistent record.)
6238 // If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6239 // then the default host name (m->MulticastHostname) is automatically used
6240 // If the optional target host parameter is set, then the storage it points to must remain valid for the lifetime of the service registration
6241 mDNSexport mStatus
mDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*sr
,
6242 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6243 const domainname
*const host
, mDNSIPPort port
, const mDNSu8 txtinfo
[], mDNSu16 txtlen
,
6244 AuthRecord
*SubTypes
, mDNSu32 NumSubTypes
,
6245 const mDNSInterfaceID InterfaceID
, mDNSServiceCallback Callback
, void *Context
)
6250 sr
->state
= regState_Zero
;
6251 sr
->srs_uselease
= 0;
6253 sr
->TestForSelfConflict
= 0;
6258 sr
->SRSUpdatePort
= zeroIPPort
;
6259 mDNSPlatformMemZero(&sr
->NATinfo
, sizeof(sr
->NATinfo
));
6260 sr
->ClientCallbackDeferred
= 0;
6261 sr
->DeferredStatus
= 0;
6262 sr
->SRVUpdateDeferred
= 0;
6266 sr
->ServiceCallback
= Callback
;
6267 sr
->ServiceContext
= Context
;
6268 sr
->Conflict
= mDNSfalse
;
6270 sr
->Extras
= mDNSNULL
;
6271 sr
->NumSubTypes
= NumSubTypes
;
6272 sr
->SubTypes
= SubTypes
;
6274 // Initialize the AuthRecord objects to sane values
6275 // Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out
6276 mDNS_SetupResourceRecord(&sr
->RR_ADV
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeAdvisory
, ServiceCallback
, sr
);
6277 mDNS_SetupResourceRecord(&sr
->RR_PTR
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6278 mDNS_SetupResourceRecord(&sr
->RR_SRV
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6279 mDNS_SetupResourceRecord(&sr
->RR_TXT
, mDNSNULL
, InterfaceID
, kDNSType_TXT
, kStandardTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6281 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6282 if (mDNSIPPortIsZero(port
))
6283 return(mDNS_RegisterNoSuchService(m
, &sr
->RR_SRV
, name
, type
, domain
, mDNSNULL
, mDNSInterface_Any
, NSSCallback
, sr
));
6285 // If the client is registering an oversized TXT record,
6286 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
6287 if (sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
< txtlen
)
6288 sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
= txtlen
;
6290 // Set up the record names
6291 // For now we only create an advisory record for the main type, not for subtypes
6292 // We need to gain some operational experience before we decide if there's a need to create them for subtypes too
6293 if (ConstructServiceName(&sr
->RR_ADV
.namestorage
, (const domainlabel
*)"\x09_services", (const domainname
*)"\x07_dns-sd\x04_udp", domain
) == mDNSNULL
)
6294 return(mStatus_BadParamErr
);
6295 if (ConstructServiceName(&sr
->RR_PTR
.namestorage
, mDNSNULL
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6296 if (ConstructServiceName(&sr
->RR_SRV
.namestorage
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6297 AssignDomainName(&sr
->RR_TXT
.namestorage
, sr
->RR_SRV
.resrec
.name
);
6299 // 1. Set up the ADV record rdata to advertise our service type
6300 AssignDomainName(&sr
->RR_ADV
.resrec
.rdata
->u
.name
, sr
->RR_PTR
.resrec
.name
);
6302 // 2. Set up the PTR record rdata to point to our service name
6303 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6304 AssignDomainName(&sr
->RR_PTR
.resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6305 sr
->RR_PTR
.Additional1
= &sr
->RR_SRV
;
6306 sr
->RR_PTR
.Additional2
= &sr
->RR_TXT
;
6308 // 2a. Set up any subtype PTRs to point to our service name
6309 // If the client is using subtypes, it is the client's responsibility to have
6310 // already set the first label of the record name to the subtype being registered
6311 for (i
=0; i
<NumSubTypes
; i
++)
6314 AssignDomainName(&st
, sr
->SubTypes
[i
].resrec
.name
);
6315 st
.c
[1+st
.c
[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
6316 AppendDomainName(&st
, type
);
6317 mDNS_SetupResourceRecord(&sr
->SubTypes
[i
], mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6318 if (ConstructServiceName(&sr
->SubTypes
[i
].namestorage
, mDNSNULL
, &st
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6319 AssignDomainName(&sr
->SubTypes
[i
].resrec
.rdata
->u
.name
, &sr
->RR_SRV
.namestorage
);
6320 sr
->SubTypes
[i
].Additional1
= &sr
->RR_SRV
;
6321 sr
->SubTypes
[i
].Additional2
= &sr
->RR_TXT
;
6324 // 3. Set up the SRV record rdata.
6325 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.priority
= 0;
6326 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.weight
= 0;
6327 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
= port
;
6329 // Setting AutoTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6330 if (host
&& host
->c
[0]) AssignDomainName(&sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
, host
);
6331 else { sr
->RR_SRV
.AutoTarget
= Target_AutoHost
; sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
.c
[0] = '\0'; }
6333 // 4. Set up the TXT record rdata,
6334 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6335 if (txtinfo
== mDNSNULL
) sr
->RR_TXT
.resrec
.rdlength
= 0;
6336 else if (txtinfo
!= sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
)
6338 sr
->RR_TXT
.resrec
.rdlength
= txtlen
;
6339 if (sr
->RR_TXT
.resrec
.rdlength
> sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
) return(mStatus_BadParamErr
);
6340 mDNSPlatformMemCopy(sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, txtinfo
, txtlen
);
6342 sr
->RR_TXT
.DependentOn
= &sr
->RR_SRV
;
6344 #ifndef UNICAST_DISABLED
6345 // If the client has specified an explicit InterfaceID,
6346 // then we do a multicast registration on that interface, even for unicast domains.
6347 if (!(InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(&sr
->RR_SRV
.namestorage
)))
6351 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6352 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6353 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6354 // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
6355 if (!sr
->RR_TXT
.resrec
.rdlength
) { sr
->RR_TXT
.resrec
.rdlength
= 1; sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6357 status
= uDNS_RegisterService(m
, sr
);
6363 err
= mDNS_Register_internal(m
, &sr
->RR_SRV
);
6364 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_TXT
);
6365 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
6366 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
6367 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
6368 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
6369 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
6370 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_ADV
);
6371 for (i
=0; i
<NumSubTypes
; i
++) if (!err
) err
= mDNS_Register_internal(m
, &sr
->SubTypes
[i
]);
6372 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_PTR
);
6376 if (err
) mDNS_DeregisterService(m
, sr
);
6380 mDNSlocal
void DummyCallback(mDNS
*const m
, AuthRecord
*rr
, mStatus result
)
6384 (void)result
; // Unused
6385 LogOperation("DummyCallback %d %s", result
, ARDisplayString(m
, rr
));
6388 mDNSexport mStatus
mDNS_AddRecordToService(mDNS
*const m
, ServiceRecordSet
*sr
,
6389 ExtraResourceRecord
*extra
, RData
*rdata
, mDNSu32 ttl
)
6391 ExtraResourceRecord
**e
;
6394 extra
->next
= mDNSNULL
;
6395 mDNS_SetupResourceRecord(&extra
->r
, rdata
, sr
->RR_PTR
.resrec
.InterfaceID
,
6396 extra
->r
.resrec
.rrtype
, ttl
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6397 AssignDomainName(&extra
->r
.namestorage
, sr
->RR_SRV
.resrec
.name
);
6401 while (*e
) e
= &(*e
)->next
;
6403 if (ttl
== 0) ttl
= kStandardTTL
;
6405 extra
->r
.DependentOn
= &sr
->RR_SRV
;
6407 debugf("mDNS_AddRecordToService adding record to %##s %s %d",
6408 extra
->r
.resrec
.name
->c
, DNSTypeName(extra
->r
.resrec
.rrtype
), extra
->r
.resrec
.rdlength
);
6410 status
= mDNS_Register_internal(m
, &extra
->r
);
6411 if (status
== mStatus_NoError
)
6414 #ifndef UNICAST_DISABLED
6415 if (sr
->RR_SRV
.resrec
.InterfaceID
!= mDNSInterface_LocalOnly
&& !IsLocalDomain(sr
->RR_SRV
.resrec
.name
))
6417 extra
->r
.resrec
.RecordType
= kDNSRecordTypeShared
; // don't want it to conflict with the service name (???)
6418 extra
->r
.RecordCallback
= DummyCallback
; // don't generate callbacks for extra RRs for unicast services (WHY NOT????)
6419 if (sr
->state
!= regState_Registered
&& sr
->state
!= regState_Refresh
) extra
->r
.state
= regState_ExtraQueued
;
6428 mDNSexport mStatus
mDNS_RemoveRecordFromService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
,
6429 mDNSRecordCallback MemFreeCallback
, void *Context
)
6431 ExtraResourceRecord
**e
;
6436 while (*e
&& *e
!= extra
) e
= &(*e
)->next
;
6439 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra
->r
.resrec
.name
->c
);
6440 status
= mStatus_BadReferenceErr
;
6444 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra
->r
.resrec
.name
->c
);
6445 extra
->r
.RecordCallback
= MemFreeCallback
;
6446 extra
->r
.RecordContext
= Context
;
6448 status
= mDNS_Deregister_internal(m
, &extra
->r
, mDNS_Dereg_normal
);
6454 mDNSexport mStatus
mDNS_RenameAndReregisterService(mDNS
*const m
, ServiceRecordSet
*const sr
, const domainlabel
*newname
)
6456 // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
6457 // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
6458 domainlabel name1
, name2
;
6459 domainname type
, domain
;
6460 const domainname
*host
= sr
->RR_SRV
.AutoTarget
? mDNSNULL
: &sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
;
6461 ExtraResourceRecord
*extras
= sr
->Extras
;
6464 DeconstructServiceName(sr
->RR_SRV
.resrec
.name
, &name1
, &type
, &domain
);
6468 IncrementLabelSuffix(&name2
, mDNStrue
);
6472 if (SameDomainName(&domain
, &localdomain
))
6473 LogMsg("%##s service renamed from \"%#s\" to \"%#s\"", type
.c
, name1
.c
, newname
->c
);
6474 else LogMsg("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type
.c
, domain
.c
, name1
.c
, newname
->c
);
6476 err
= mDNS_RegisterService(m
, sr
, newname
, &type
, &domain
,
6477 host
, sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, sr
->RR_TXT
.resrec
.rdlength
,
6478 sr
->SubTypes
, sr
->NumSubTypes
,
6479 sr
->RR_PTR
.resrec
.InterfaceID
, sr
->ServiceCallback
, sr
->ServiceContext
);
6481 // mDNS_RegisterService() just reset sr->Extras to NULL.
6482 // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
6483 // through the old list of extra records, and re-add them to our freshly created service registration
6484 while (!err
&& extras
)
6486 ExtraResourceRecord
*e
= extras
;
6487 extras
= extras
->next
;
6488 err
= mDNS_AddRecordToService(m
, sr
, e
, e
->r
.resrec
.rdata
, e
->r
.resrec
.rroriginalttl
);
6494 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
6495 // which may change the record list and/or question list.
6496 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6497 mDNSexport mStatus
mDNS_DeregisterService(mDNS
*const m
, ServiceRecordSet
*sr
)
6499 // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
6500 if (mDNSIPPortIsZero(sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
)) return(mDNS_DeregisterNoSuchService(m
, &sr
->RR_SRV
));
6502 #ifndef UNICAST_DISABLED
6503 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6507 status
= uDNS_DeregisterService(m
, sr
);
6512 if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeUnregistered
)
6514 debugf("Service set for %##s already deregistered", sr
->RR_SRV
.resrec
.name
->c
);
6515 return(mStatus_BadReferenceErr
);
6517 else if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeDeregistering
)
6519 debugf("Service set for %##s already in the process of deregistering", sr
->RR_SRV
.resrec
.name
->c
);
6520 // Avoid race condition:
6521 // If a service gets a conflict, then we set the Conflict flag to tell us to generate
6522 // an mStatus_NameConflict message when we get the mStatus_MemFree for our PTR record.
6523 // If the client happens to deregister the service in the middle of that process, then
6524 // we clear the flag back to the normal state, so that we deliver a plain mStatus_MemFree
6525 // instead of incorrectly promoting it to mStatus_NameConflict.
6526 // This race condition is exposed particularly when the conformance test generates
6527 // a whole batch of simultaneous conflicts across a range of services all advertised
6528 // using the same system default name, and if we don't take this precaution then
6529 // we end up incrementing m->nicelabel multiple times instead of just once.
6530 // <rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision
6531 sr
->Conflict
= mDNSfalse
;
6532 return(mStatus_NoError
);
6538 ExtraResourceRecord
*e
;
6542 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
6543 // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
6544 mDNS_Deregister_internal(m
, &sr
->RR_SRV
, mDNS_Dereg_repeat
);
6545 mDNS_Deregister_internal(m
, &sr
->RR_TXT
, mDNS_Dereg_repeat
);
6547 mDNS_Deregister_internal(m
, &sr
->RR_ADV
, mDNS_Dereg_normal
);
6549 // We deregister all of the extra records, but we leave the sr->Extras list intact
6550 // in case the client wants to do a RenameAndReregister and reinstate the registration
6553 mDNS_Deregister_internal(m
, &e
->r
, mDNS_Dereg_repeat
);
6557 for (i
=0; i
<sr
->NumSubTypes
; i
++)
6558 mDNS_Deregister_internal(m
, &sr
->SubTypes
[i
], mDNS_Dereg_normal
);
6560 // Be sure to deregister the PTR last!
6561 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
6562 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
6563 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
6564 // we've deregistered all our records and done any other necessary cleanup before that happens.
6565 status
= mDNS_Deregister_internal(m
, &sr
->RR_PTR
, mDNS_Dereg_normal
);
6571 // Create a registration that asserts that no such service exists with this name.
6572 // This can be useful where there is a given function is available through several protocols.
6573 // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
6574 // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
6575 // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
6576 // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
6577 mDNSexport mStatus
mDNS_RegisterNoSuchService(mDNS
*const m
, AuthRecord
*const rr
,
6578 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6579 const domainname
*const host
,
6580 const mDNSInterfaceID InterfaceID
, mDNSRecordCallback Callback
, void *Context
)
6582 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, Callback
, Context
);
6583 if (ConstructServiceName(&rr
->namestorage
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6584 rr
->resrec
.rdata
->u
.srv
.priority
= 0;
6585 rr
->resrec
.rdata
->u
.srv
.weight
= 0;
6586 rr
->resrec
.rdata
->u
.srv
.port
= zeroIPPort
;
6587 if (host
&& host
->c
[0]) AssignDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, host
);
6588 else rr
->AutoTarget
= Target_AutoHost
;
6589 return(mDNS_Register(m
, rr
));
6592 mDNSexport mStatus
mDNS_AdvertiseDomains(mDNS
*const m
, AuthRecord
*rr
,
6593 mDNS_DomainType DomainType
, const mDNSInterfaceID InterfaceID
, char *domname
)
6595 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, mDNSNULL
, mDNSNULL
);
6596 if (!MakeDomainNameFromDNSNameString(&rr
->namestorage
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
6597 if (!MakeDomainNameFromDNSNameString(&rr
->resrec
.rdata
->u
.name
, domname
)) return(mStatus_BadParamErr
);
6598 return(mDNS_Register(m
, rr
));
6601 mDNSOpaque16
mDNS_NewMessageID(mDNS
* const m
)
6603 static mDNSBool randomized
= mDNSfalse
;
6605 if (!randomized
) { m
->NextMessageID
= (mDNSu16
)mDNSRandom(0xFFFF); randomized
= mDNStrue
; }
6606 if (m
->NextMessageID
== 0) m
->NextMessageID
++;
6607 return mDNSOpaque16fromIntVal(m
->NextMessageID
++);
6610 // ***************************************************************************
6611 #if COMPILER_LIKES_PRAGMA_MARK
6613 #pragma mark - Startup and Shutdown
6616 mDNSlocal
void mDNS_GrowCache_internal(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
6618 if (storage
&& numrecords
)
6621 debugf("Adding cache storage for %d more records (%d bytes)", numrecords
, numrecords
*sizeof(CacheEntity
));
6622 for (i
=0; i
<numrecords
; i
++) storage
[i
].next
= &storage
[i
+1];
6623 storage
[numrecords
-1].next
= m
->rrcache_free
;
6624 m
->rrcache_free
= storage
;
6625 m
->rrcache_size
+= numrecords
;
6629 mDNSexport
void mDNS_GrowCache(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
6632 mDNS_GrowCache_internal(m
, storage
, numrecords
);
6636 mDNSexport mStatus
mDNS_Init(mDNS
*const m
, mDNS_PlatformSupport
*const p
,
6637 CacheEntity
*rrcachestorage
, mDNSu32 rrcachesize
,
6638 mDNSBool AdvertiseLocalAddresses
, mDNSCallback
*Callback
, void *Context
)
6644 if (!rrcachestorage
) rrcachesize
= 0;
6648 m
->CanReceiveUnicastOn5353
= mDNSfalse
; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
6649 m
->AdvertiseLocalAddresses
= AdvertiseLocalAddresses
;
6650 m
->mDNSPlatformStatus
= mStatus_Waiting
;
6651 m
->UnicastPort4
= zeroIPPort
;
6652 m
->UnicastPort6
= zeroIPPort
;
6653 m
->MainCallback
= Callback
;
6654 m
->MainContext
= Context
;
6655 m
->rec
.r
.resrec
.RecordType
= 0;
6657 // For debugging: To catch and report locking failures
6659 m
->mDNS_reentrancy
= 0;
6660 m
->mDNS_shutdown
= mDNSfalse
;
6661 m
->lock_rrcache
= 0;
6662 m
->lock_Questions
= 0;
6663 m
->lock_Records
= 0;
6665 // Task Scheduling variables
6666 result
= mDNSPlatformTimeInit();
6667 if (result
!= mStatus_NoError
) return(result
);
6668 m
->timenow_adjust
= (mDNSs32
)mDNSRandom(0xFFFFFFFF);
6669 timenow
= mDNS_TimeNow_NoLock(m
);
6671 m
->timenow
= 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
6672 m
->timenow_last
= timenow
;
6673 m
->NextScheduledEvent
= timenow
;
6674 m
->SuppressSending
= timenow
;
6675 m
->NextCacheCheck
= timenow
+ 0x78000000;
6676 m
->NextScheduledQuery
= timenow
+ 0x78000000;
6677 m
->NextScheduledProbe
= timenow
+ 0x78000000;
6678 m
->NextScheduledResponse
= timenow
+ 0x78000000;
6679 m
->NextScheduledNATOp
= timenow
+ 0x78000000;
6680 m
->RandomQueryDelay
= 0;
6681 m
->RandomReconfirmDelay
= 0;
6683 m
->SendDeregistrations
= mDNSfalse
;
6684 m
->SendImmediateAnswers
= mDNSfalse
;
6685 m
->SleepState
= mDNSfalse
;
6687 // These fields only required for mDNS Searcher...
6688 m
->Questions
= mDNSNULL
;
6689 m
->NewQuestions
= mDNSNULL
;
6690 m
->CurrentQuestion
= mDNSNULL
;
6691 m
->LocalOnlyQuestions
= mDNSNULL
;
6692 m
->NewLocalOnlyQuestions
= mDNSNULL
;
6693 m
->rrcache_size
= 0;
6694 m
->rrcache_totalused
= 0;
6695 m
->rrcache_active
= 0;
6696 m
->rrcache_report
= 10;
6697 m
->rrcache_free
= mDNSNULL
;
6699 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++) m
->rrcache_hash
[slot
] = mDNSNULL
;
6701 mDNS_GrowCache_internal(m
, rrcachestorage
, rrcachesize
);
6703 // Fields below only required for mDNS Responder...
6704 m
->hostlabel
.c
[0] = 0;
6705 m
->nicelabel
.c
[0] = 0;
6706 m
->MulticastHostname
.c
[0] = 0;
6707 m
->HIHardware
.c
[0] = 0;
6708 m
->HISoftware
.c
[0] = 0;
6709 m
->ResourceRecords
= mDNSNULL
;
6710 m
->DuplicateRecords
= mDNSNULL
;
6711 m
->NewLocalRecords
= mDNSNULL
;
6712 m
->CurrentRecord
= mDNSNULL
;
6713 m
->HostInterfaces
= mDNSNULL
;
6714 m
->ProbeFailTime
= 0;
6715 m
->NumFailedProbes
= 0;
6716 m
->SuppressProbes
= 0;
6718 #ifndef UNICAST_DISABLED
6719 m
->NextuDNSEvent
= timenow
+ 0x78000000;
6720 m
->NextSRVUpdate
= timenow
+ 0x78000000;
6721 m
->SuppressStdPort53Queries
= 0;
6723 m
->ServiceRegistrations
= mDNSNULL
;
6724 m
->NextMessageID
= 0;
6725 m
->DNSServers
= mDNSNULL
;
6727 m
->Router
= zeroAddr
;
6728 m
->AdvertisedV4
= zeroAddr
;
6729 m
->AdvertisedV6
= zeroAddr
;
6731 m
->AuthInfoList
= mDNSNULL
;
6733 m
->ReverseMap
.ThisQInterval
= -1;
6734 m
->StaticHostname
.c
[0] = 0;
6736 m
->Hostnames
= mDNSNULL
;
6737 m
->AutoTunnelHostAddr
.b
[0] = 0;
6738 m
->AutoTunnelHostAddrActive
= mDNSfalse
;
6739 m
->AutoTunnelLabel
.c
[0] = 0;
6741 m
->RegisterSearchDomains
= mDNSfalse
;
6743 // NAT traversal fields
6744 m
->NATTraversals
= mDNSNULL
;
6745 m
->CurrentNATTraversal
= mDNSNULL
;
6746 m
->retryIntervalGetAddr
= 0; // delta between time sent and retry
6747 m
->retryGetAddr
= timenow
+ 0x78000000; // absolute time when we retry
6748 m
->ExternalAddress
= zerov4Addr
;
6750 m
->NATMcastRecvskt
= mDNSNULL
;
6751 m
->NATMcastRecvsk2
= mDNSNULL
;
6752 m
->LastNATupseconds
= 0;
6753 m
->LastNATReplyLocalTime
= timenow
;
6755 m
->UPnPInterfaceID
= 0;
6756 m
->UPnPRouterPort
= zeroIPPort
;
6757 m
->UPnPSOAPPort
= zeroIPPort
;
6758 m
->UPnPRouterURL
= mDNSNULL
;
6759 m
->UPnPSOAPURL
= mDNSNULL
;
6760 m
->UPnPRouterAddressString
= mDNSNULL
;
6761 m
->UPnPSOAPAddressString
= mDNSNULL
;
6764 #if APPLE_OSX_mDNSResponder
6765 m
->TunnelClients
= mDNSNULL
;
6768 result
= mDNSPlatformInit(m
);
6770 #ifndef UNICAST_DISABLED
6771 // It's better to do this *after* the platform layer has set up the
6772 // interface list and security credentials
6773 uDNS_SetupDNSConfig(m
); // Get initial DNS configuration
6779 mDNSlocal
void DynDNSHostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6782 debugf("NameStatusCallback: result %d for registration of name %##s", result
, rr
->resrec
.name
->c
);
6783 mDNSPlatformDynDNSHostNameStatusChanged(rr
->resrec
.name
, result
);
6786 mDNSexport mStatus
uDNS_SetupDNSConfig(mDNS
*const m
)
6794 DNSServer
*ptr
, **p
= &m
->DNSServers
;
6797 if (m
->RegisterSearchDomains
) uDNS_RegisterSearchDomains(m
);
6801 // Let the platform layer get the current DNS information
6802 // The m->RegisterSearchDomains boolean is so that we lazily get the search domain list only on-demand
6803 // (no need to hit the network with domain enumeration queries until we actually need that information).
6804 for (ptr
= m
->DNSServers
; ptr
; ptr
= ptr
->next
) ptr
->del
= mDNStrue
;
6806 mDNSPlatformSetDNSConfig(m
, mDNStrue
, mDNSfalse
, &fqdn
, mDNSNULL
, mDNSNULL
);
6808 // Update our qDNSServer pointers before we go and free the DNSServer object memory
6809 for (q
= m
->Questions
; q
; q
=q
->next
)
6810 if (!mDNSOpaque16IsZero(q
->TargetQID
))
6812 DNSServer
*s
= GetServerForName(m
, &q
->qname
);
6813 if (q
->qDNSServer
!= s
)
6815 // If DNS Server for this question has changed, reactivate it
6816 LogOperation("Updating DNS Server from %#a:%d to %#a:%d for %##s (%s)",
6817 q
->qDNSServer
? &q
->qDNSServer
->addr
: mDNSNULL
, mDNSVal16(q
->qDNSServer
? q
->qDNSServer
->port
: zeroIPPort
),
6818 s
? &s
->addr
: mDNSNULL
, mDNSVal16(s
? s
->port
: zeroIPPort
),
6819 q
->qname
.c
, DNSTypeName(q
->qtype
));
6821 ActivateUnicastQuery(m
, q
);
6829 // Scan our cache, looking for uDNS records that we would have queried this server for.
6830 // We reconfirm any records that match, because in this world of split DNS, firewalls, etc.
6831 // different DNS servers can give different answers to the same question.
6833 ptr
->del
= mDNSfalse
; // Clear del so GetServerForName will (temporarily) find this server again before it's finally deleted
6834 FORALL_CACHERECORDS(slot
, cg
, cr
)
6835 if (!cr
->resrec
.InterfaceID
&& GetServerForName(m
, cr
->resrec
.name
) == ptr
)
6836 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
6838 mDNSPlatformMemFree(ptr
);
6844 // If we have no DNS servers at all, then immediately purge all unicast cache records (including for LLQs)
6845 // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless
6846 // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour
6847 if (!m
->DNSServers
) FORALL_CACHERECORDS(slot
, cg
, cr
) if (!cr
->resrec
.InterfaceID
) mDNS_PurgeCacheResourceRecord(m
, cr
);
6849 // Did our FQDN change?
6850 if (!SameDomainName(&fqdn
, &m
->FQDN
))
6852 if (m
->FQDN
.c
[0]) mDNS_RemoveDynDNSHostName(m
, &m
->FQDN
);
6854 AssignDomainName(&m
->FQDN
, &fqdn
);
6858 mDNSPlatformDynDNSHostNameStatusChanged(&m
->FQDN
, 1);
6859 mDNS_AddDynDNSHostName(m
, &m
->FQDN
, DynDNSHostNameCallback
, mDNSNULL
);
6865 // handle router and primary interface changes
6866 v4
= v6
= r
= zeroAddr
;
6867 v4
.type
= r
.type
= mDNSAddrType_IPv4
;
6869 if (mDNSPlatformGetPrimaryInterface(m
, &v4
, &v6
, &r
) == mStatus_NoError
&& !mDNSv4AddressIsLinkLocal(&v4
.ip
.v4
))
6871 mDNS_SetPrimaryInterfaceInfo(m
,
6872 !mDNSIPv4AddressIsZero(v4
.ip
.v4
) ? &v4
: mDNSNULL
,
6873 !mDNSIPv6AddressIsZero(v6
.ip
.v6
) ? &v6
: mDNSNULL
,
6874 !mDNSIPv4AddressIsZero(r
.ip
.v4
) ? &r
: mDNSNULL
);
6878 mDNS_SetPrimaryInterfaceInfo(m
, mDNSNULL
, mDNSNULL
, mDNSNULL
);
6879 if (m
->FQDN
.c
[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m
->FQDN
, 1); // Set status to 1 to indicate temporary failure
6882 return mStatus_NoError
;
6885 mDNSexport
void mDNSCoreInitComplete(mDNS
*const m
, mStatus result
)
6887 m
->mDNSPlatformStatus
= result
;
6888 if (m
->MainCallback
)
6891 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
6892 m
->MainCallback(m
, mStatus_NoError
);
6893 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
6898 mDNSexport
void mDNS_Close(mDNS
*const m
)
6900 mDNSu32 rrcache_active
= 0;
6901 mDNSu32 rrcache_totalused
= 0;
6903 NetworkInterfaceInfo
*intf
;
6907 m
->mDNS_shutdown
= mDNStrue
;
6909 #ifndef UNICAST_DISABLED
6911 while (m
->Hostnames
) mDNS_RemoveDynDNSHostName(m
, &m
->Hostnames
->fqdn
);
6914 rrcache_totalused
= m
->rrcache_totalused
;
6915 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
6917 while (m
->rrcache_hash
[slot
])
6919 CacheGroup
*cg
= m
->rrcache_hash
[slot
];
6922 CacheRecord
*cr
= cg
->members
;
6923 cg
->members
= cg
->members
->next
;
6924 if (cr
->CRActiveQuestion
) rrcache_active
++;
6925 ReleaseCacheRecord(m
, cr
);
6927 cg
->rrcache_tail
= &cg
->members
;
6928 ReleaseCacheGroup(m
, &m
->rrcache_hash
[slot
]);
6931 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused
, rrcache_active
);
6932 if (rrcache_active
!= m
->rrcache_active
)
6933 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active
, m
->rrcache_active
);
6935 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6936 if (intf
->Advertise
)
6937 DeadvertiseInterface(m
, intf
);
6939 // Shut down all our active NAT Traversals
6940 while (m
->NATTraversals
)
6942 NATTraversalInfo
*t
= m
->NATTraversals
;
6943 mDNS_StopNATOperation_internal(m
, t
); // This will cut 't' from the list, thereby advancing m->NATTraversals in the process
6945 // After stopping the NAT Traversal, we zero out the fields.
6946 // This has particularly important implications for our AutoTunnel records --
6947 // when we deregister our AutoTunnel records below, we don't want their mStatus_MemFree
6948 // handlers to just turn around and attempt to re-register those same records.
6949 // Clearing t->ExternalPort will cause the mStatus_MemFree callback handlers to not do this.
6950 t
->ExternalAddress
= zerov4Addr
;
6951 t
->ExternalPort
= zeroIPPort
;
6953 t
->Result
= mStatus_NoError
;
6956 // Make sure there are nothing but deregistering records remaining in the list
6957 if (m
->CurrentRecord
)
6958 LogMsg("mDNS_Close ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
6960 // First we deregister any non-shared records. In particular, we want to make sure we deregister
6961 // any extra records added to a Service Record Set first, before we deregister its PTR record.
6962 m
->CurrentRecord
= m
->ResourceRecords
;
6963 while (m
->CurrentRecord
)
6965 rr
= m
->CurrentRecord
;
6966 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
6967 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
6969 m
->CurrentRecord
= rr
->next
;
6972 // Now deregister any remaining records we didn't get the first time through
6973 while (m
->CurrentRecord
)
6975 rr
= m
->CurrentRecord
;
6976 if (rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
6977 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
6979 m
->CurrentRecord
= rr
->next
;
6982 if (m
->ResourceRecords
) debugf("mDNS_Close: Sending final packets for deregistering records");
6983 else debugf("mDNS_Close: No deregistering records remain");
6985 // If any deregistering records remain, send their deregistration announcements before we exit
6986 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) DiscardDeregistrations(m
);
6987 else if (m
->ResourceRecords
) SendResponses(m
);
6989 for (rr
= m
->ResourceRecords
; rr
; rr
= rr
->next
)
6990 LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m
, rr
));
6993 debugf("mDNS_Close: mDNSPlatformClose");
6994 mDNSPlatformClose(m
);
6995 debugf("mDNS_Close: done");