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.751 2007/10/30 23:49:41 cheshire
42 <rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
43 LLQ state was not being transferred properly between duplicate questions
45 Revision 1.750 2007/10/29 23:58:52 cheshire
46 <rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
47 Use standard "if (mDNSIPv4AddressIsOnes(....ExternalAddress))" mechanism to determine whether callback has been invoked yet
49 Revision 1.749 2007/10/29 21:28:36 cheshire
50 Change "Correcting TTL" log message to LogOperation to suppress it in customer build
52 Revision 1.748 2007/10/29 20:02:50 cheshire
53 <rdar://problem/5526813> BTMM: Wide-area records being announced via multicast
55 Revision 1.747 2007/10/26 22:53:50 cheshire
56 Made mDNS_Register_internal and mDNS_Deregister_internal use AuthRecord_uDNS macro
57 instead of replicating the logic in both places
59 Revision 1.746 2007/10/25 22:48:50 cheshire
60 Added LogOperation message saying when we restart GetZoneData for record and service registrations
62 Revision 1.745 2007/10/25 20:48:47 cheshire
63 For naming consistency (with AuthRecord's UpdateServer) renamed 'ns' to 'SRSUpdateServer'
65 Revision 1.744 2007/10/25 20:06:14 cheshire
66 Don't try to do SOA queries using private DNS (TLS over TCP) queries
68 Revision 1.743 2007/10/25 00:12:46 cheshire
69 <rdar://problem/5496734> BTMM: Need to retry registrations after failures
70 Retrigger service registrations whenever a new network interface is added
72 Revision 1.742 2007/10/24 22:40:06 cheshire
73 Renamed: RecordRegistrationCallback -> RecordRegistrationGotZoneData
74 Renamed: ServiceRegistrationZoneDataComplete -> ServiceRegistrationGotZoneData
76 Revision 1.741 2007/10/24 00:50:29 cheshire
77 <rdar://problem/5496734> BTMM: Need to retry registrations after failures
78 Retrigger record registrations whenever a new network interface is added
80 Revision 1.740 2007/10/23 00:38:03 cheshire
81 When sending uDNS cache expiration query, need to increment rr->UnansweredQueries
82 or code will spin sending the same cache expiration query repeatedly
84 Revision 1.739 2007/10/22 23:46:41 cheshire
85 <rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
86 Need to clear question->nta pointer after calling CancelGetZoneData()
88 Revision 1.738 2007/10/19 22:08:49 cheshire
89 <rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
90 Additional fixes and refinements
92 Revision 1.737 2007/10/18 23:06:42 cheshire
93 <rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
94 Additional fixes and refinements
96 Revision 1.736 2007/10/18 20:23:17 cheshire
97 Moved SuspendLLQs into mDNS.c, since it's only called from one place
99 Revision 1.735 2007/10/18 00:12:34 cheshire
100 Fixed "unused variable" compiler warning
102 Revision 1.734 2007/10/17 22:49:54 cheshire
103 <rdar://problem/5519458> BTMM: Machines don't appear in the sidebar on wake from sleep
105 Revision 1.733 2007/10/17 22:37:23 cheshire
106 <rdar://problem/5536979> BTMM: Need to create NAT port mapping for receiving LLQ events
108 Revision 1.732 2007/10/17 21:53:51 cheshire
109 Improved debugging messages; renamed startLLQHandshakeCallback to LLQGotZoneData
111 Revision 1.731 2007/10/17 18:37:50 cheshire
112 <rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
113 Further refinement: pre-increment m->CurrentRecord before calling mDNS_Deregister_internal()
115 Revision 1.730 2007/10/16 21:16:07 cheshire
116 <rdar://problem/5539930> Goodbye packets not being sent for services on shutdown
118 Revision 1.729 2007/10/05 17:56:10 cheshire
119 Move CountLabels and SkipLeadingLabels to DNSCommon.c so they're callable from other files
121 Revision 1.728 2007/10/04 23:18:14 cheshire
122 <rdar://problem/5523706> mDNSResponder flooding DNS servers with unreasonable query level
124 Revision 1.727 2007/10/04 22:51:57 cheshire
125 Added debugging LogOperation message to show when we're sending cache expiration queries
127 Revision 1.726 2007/10/03 00:14:24 cheshire
128 Removed write to null to generate stack trace for SetNextQueryTime locking failure
130 Revision 1.725 2007/10/02 21:11:08 cheshire
131 <rdar://problem/5518270> LLQ refreshes don't work, which breaks BTMM browsing
133 Revision 1.724 2007/10/02 20:10:23 cheshire
134 Additional debugging checks on shutdown -- list all records we didn't send a goodbye for, not just the first one
136 Revision 1.723 2007/10/02 19:56:54 cheshire
137 <rdar://problem/5518310> Double-dispose causes crash changing Dynamic DNS hostname
139 Revision 1.722 2007/10/01 22:59:46 cheshire
140 <rdar://problem/5516303> mDNSResponder did not shut down after 20 seconds
141 Need to shut down NATTraversals on exit
143 Revision 1.721 2007/10/01 18:42:07 cheshire
144 To make packet logs appear in a more intuitive order, dump received packets *before* handling them, not after
146 Revision 1.720 2007/09/29 20:40:19 cheshire
147 <rdar://problem/5513378> Crash in ReissueBlockedQuestions
149 Revision 1.719 2007/09/27 22:23:56 cheshire
150 <rdar://problem/4947392> uDNS: Use SOA to determine TTL for negative answers
151 Need to clear m->rec.r.resrec.RecordType after we've finished using m->rec
153 Revision 1.718 2007/09/27 22:02:33 cheshire
154 <rdar://problem/5464941> BTMM: Registered records in BTMM don't get removed from server after calling RemoveRecord
156 Revision 1.717 2007/09/27 21:21:39 cheshire
157 Export CompleteDeregistration so it's callable from other files
159 Revision 1.716 2007/09/27 02:12:21 cheshire
160 Updated GrantCacheExtensions degugging message to show new record lifetime
162 Revision 1.715 2007/09/27 01:20:06 cheshire
163 <rdar://problem/5500077> BTMM: Need to refresh LLQs based on lease life and not TTL of response
165 Revision 1.714 2007/09/27 00:37:01 cheshire
166 <rdar://problem/4947392> BTMM: Use SOA to determine TTL for negative answers
168 Revision 1.713 2007/09/27 00:25:39 cheshire
169 Added ttl_seconds parameter to MakeNegativeCacheRecord in preparation for:
170 <rdar://problem/4947392> uDNS: Use SOA to determine TTL for negative answers
172 Revision 1.712 2007/09/26 23:16:58 cheshire
173 <rdar://problem/5496399> BTMM: Leopard sending excessive LLQ registration requests to .Mac
175 Revision 1.711 2007/09/26 22:06:02 cheshire
176 <rdar://problem/5507399> BTMM: No immediate failure notifications for BTMM names
178 Revision 1.710 2007/09/26 00:49:46 cheshire
179 Improve packet logging to show sent and received packets,
180 transport protocol (UDP/TCP/TLS) and source/destination address:port
182 Revision 1.709 2007/09/21 21:12:36 cheshire
183 <rdar://problem/5498009> BTMM: Need to log updates and query packet contents
185 Revision 1.708 2007/09/20 23:13:37 cheshire
186 <rdar://problem/4038277> BTMM: Not getting LLQ remove events when logging out of VPN or disconnecting from network
187 Additional fix: If we have no DNS servers at all, then immediately purge all unicast cache records (including for LLQs)
189 Revision 1.707 2007/09/20 02:29:37 cheshire
190 <rdar://problem/4038277> BTMM: Not getting LLQ remove events when logging out of VPN or disconnecting from network
192 Revision 1.706 2007/09/20 01:13:19 cheshire
193 Export CacheGroupForName so it's callable from other files
195 Revision 1.705 2007/09/20 01:12:06 cheshire
196 Moved HashSlot(X) from mDNS.c to DNSCommon.h so it's usable in other files
198 Revision 1.704 2007/09/19 22:47:25 cheshire
199 <rdar://problem/5490182> Memory corruption freeing a "no such service" service record
201 Revision 1.703 2007/09/14 01:46:59 cheshire
202 Fix Posix build (#ifdef _LEGACY_NAT_TRAVERSAL_ section included a closing curly brace it should not have)
204 Revision 1.702 2007/09/13 22:06:46 cheshire
205 <rdar://problem/5480643> Tully's Free WiFi: DNS fails
206 Need to accept DNS responses where the query ID field matches, even if the source address does not
208 Revision 1.701 2007/09/12 23:22:32 cheshire
209 <rdar://problem/5476979> Only accept NAT Port Mapping packets from our default gateway
211 Revision 1.700 2007/09/12 23:03:08 cheshire
212 <rdar://problem/5476978> DNSServiceNATPortMappingCreate callback not giving correct interface index
214 Revision 1.699 2007/09/12 22:19:28 cheshire
215 <rdar://problem/5476977> Need to listen for port 5350 NAT-PMP announcements
217 Revision 1.698 2007/09/12 22:13:27 cheshire
218 Remove DynDNSHostNames cleanly on shutdown
220 Revision 1.697 2007/09/12 01:44:47 cheshire
221 <rdar://problem/5475938> Eliminate "Correcting TTL" syslog messages for unicast DNS records
223 Revision 1.696 2007/09/12 01:26:08 cheshire
224 Initialize LastNATReplyLocalTime to timenow, so that gateway uptime checks work more reliably
226 Revision 1.695 2007/09/11 19:19:16 cheshire
227 Correct capitalization of "uPNP" to "UPnP"
229 Revision 1.694 2007/09/10 22:06:51 cheshire
230 Rename uptime => upseconds and LastNATUptime => LastNATupseconds to make it clear these time values are in seconds
232 Revision 1.693 2007/09/07 22:24:36 vazquez
233 <rdar://problem/5466301> Need to stop spewing mDNSResponderHelper logs
235 Revision 1.692 2007/09/07 00:12:09 cheshire
236 <rdar://problem/5466010> Unicast DNS changes broke efficiency fix 3928456
238 Revision 1.691 2007/09/05 22:25:01 vazquez
239 <rdar://problem/5400521> update_record mDNSResponder leak
241 Revision 1.690 2007/09/05 21:48:01 cheshire
242 <rdar://problem/5385864> BTMM: mDNSResponder flushes wide-area Bonjour records after an hour for a zone.
243 Now that we're respecting the TTL of uDNS records in the cache, the LLQ maintenance cod needs
244 to update the cache lifetimes of all relevant records every time it successfully renews an LLQ,
245 otherwise those records will expire and vanish from the cache.
247 Revision 1.689 2007/09/05 02:29:06 cheshire
248 <rdar://problem/5457287> mDNSResponder taking up 100% CPU in ReissueBlockedQuestions
249 Additional fixes to code implementing "NoAnswer" logic
251 Revision 1.688 2007/08/31 22:56:39 cheshire
252 <rdar://problem/5407080> BTMM: TTLs incorrect on cached BTMM records
254 Revision 1.687 2007/08/31 19:53:14 cheshire
255 <rdar://problem/5431151> BTMM: IPv6 address lookup should not succeed if autotunnel cannot be setup
256 If AutoTunnel setup fails, the code now generates a fake NXDomain error saying that the requested AAAA record does not exist
258 Revision 1.686 2007/08/30 00:01:56 cheshire
259 Added comment about SetTargetToHostName()
261 Revision 1.685 2007/08/29 01:19:24 cheshire
262 <rdar://problem/5400181> BTMM: Tunneled services do not need NAT port mappings
263 Set AutoTarget to Target_AutoHostAndNATMAP for non-AutoTunnel wide-area services
265 Revision 1.684 2007/08/28 23:58:42 cheshire
266 Rename HostTarget -> AutoTarget
268 Revision 1.683 2007/08/28 23:53:21 cheshire
269 Rename serviceRegistrationCallback -> ServiceRegistrationZoneDataComplete
271 Revision 1.682 2007/08/27 20:28:19 cheshire
272 Improve "suspect uDNS response" log message
274 Revision 1.681 2007/08/24 23:37:23 cheshire
275 Added debugging message to show when ExtraResourceRecord callback gets invoked
277 Revision 1.680 2007/08/24 00:15:19 cheshire
278 Renamed GetAuthInfoForName() to GetAuthInfoForName_internal() to make it clear that it may only be called with the lock held
280 Revision 1.679 2007/08/23 21:47:09 vazquez
281 <rdar://problem/5427316> BTMM: mDNSResponder sends NAT-PMP packets on public network
282 make sure we clean up port mappings on base stations by sending a lease value of 0,
283 and only send NAT-PMP packets on private networks; also save some memory by
284 not using packet structs in NATTraversals.
286 Revision 1.678 2007/08/01 16:09:13 cheshire
287 Removed unused NATTraversalInfo substructure from AuthRecord; reduced structure sizecheck values accordingly
289 Revision 1.677 2007/08/01 01:58:24 cheshire
290 Added RecordType sanity check in mDNS_Register_internal
292 Revision 1.676 2007/08/01 00:04:13 cheshire
293 <rdar://problem/5261696> Crash in tcpKQSocketCallback
294 Half-open TCP connections were not being cancelled properly
296 Revision 1.675 2007/07/31 02:28:35 vazquez
297 <rdar://problem/3734269> NAT-PMP: Detect public IP address changes and base station reboot
299 Revision 1.674 2007/07/31 01:57:23 cheshire
300 Adding code to respect TTL received in uDNS responses turned out to
301 expose other problems; backing out change for now.
303 Revision 1.673 2007/07/30 23:31:26 cheshire
304 Code for respecting TTL received in uDNS responses should exclude LLQ-type responses
306 Revision 1.672 2007/07/28 01:25:56 cheshire
307 <rdar://problem/4780038> BTMM: Add explicit UDP event port to LLQ setup request, to fix LLQs not working behind NAT
309 Revision 1.671 2007/07/27 22:32:54 cheshire
310 When processing TTLs in uDNS responses, we'll currently impose a minimum effective TTL
311 of 2 seconds, or other stuff breaks (e.g. we end up making a negative cache entry).
313 Revision 1.670 2007/07/27 20:54:43 cheshire
314 Fixed code to respect real record TTL received in uDNS responses
316 Revision 1.669 2007/07/27 20:09:32 cheshire
317 Don't need to dump out all received mDNS packets; they're easily viewed using mDNSNetMonitor
319 Revision 1.668 2007/07/27 19:58:47 cheshire
320 Use symbolic names QC_add and QC_rmv instead of mDNStrue/mDNSfalse
322 Revision 1.667 2007/07/27 19:52:10 cheshire
323 Don't increment m->rrcache_active for no-cache add events
325 Revision 1.666 2007/07/27 19:30:39 cheshire
326 Changed mDNSQuestionCallback parameter from mDNSBool to QC_result,
327 to properly reflect tri-state nature of the possible responses
329 Revision 1.665 2007/07/27 18:44:01 cheshire
330 Rename "AnswerQuestionWithResourceRecord" to more informative "AnswerCurrentQuestionWithResourceRecord"
332 Revision 1.664 2007/07/27 18:38:56 cheshire
333 Rename "uDNS_CheckQuery" to more informative "uDNS_CheckCurrentQuestion"
335 Revision 1.663 2007/07/25 03:05:02 vazquez
337 <rdar://problem/5338913> LegacyNATTraversal: UPnP heap overflow
338 <rdar://problem/5338933> LegacyNATTraversal: UPnP stack buffer overflow
339 and a myriad of other security problems
341 Revision 1.662 2007/07/24 20:22:46 cheshire
342 Make sure all fields of main mDNS object are initialized correctly
344 Revision 1.661 2007/07/21 00:54:45 cheshire
345 <rdar://problem/5344576> Delay IPv6 address callback until AutoTunnel route and policy is configured
347 Revision 1.660 2007/07/20 20:00:45 cheshire
348 "Legacy Browse" is better called "Automatic Browse"
350 Revision 1.659 2007/07/20 00:54:18 cheshire
351 <rdar://problem/4641118> Need separate SCPreferences for per-user .Mac settings
353 Revision 1.658 2007/07/18 02:28:57 cheshire
354 Don't set AutoTunnel settings in uDNS_RegisterService; should be done in GetServiceTarget
356 Revision 1.657 2007/07/18 00:57:10 cheshire
357 <rdar://problem/5303834> Automatically configure IPSec policy when resolving services
358 Only need to call AddNewClientTunnel() for IPv6 addresses
360 Revision 1.656 2007/07/16 23:54:48 cheshire
361 <rdar://problem/5338850> Crash when removing or changing DNS keys
363 Revision 1.655 2007/07/16 20:11:37 vazquez
364 <rdar://problem/3867231> LegacyNATTraversal: Need complete rewrite
365 Init LNT stuff and handle SSDP packets
367 Revision 1.654 2007/07/12 23:30:23 cheshire
368 Changed some 'LogOperation' calls to 'debugf' to reduce verbosity in syslog
370 Revision 1.653 2007/07/12 02:51:27 cheshire
371 <rdar://problem/5303834> Automatically configure IPSec policy when resolving services
373 Revision 1.652 2007/07/11 23:43:42 cheshire
374 Rename PurgeCacheResourceRecord to mDNS_PurgeCacheResourceRecord
376 Revision 1.651 2007/07/11 22:44:40 cheshire
377 <rdar://problem/5328801> SIGHUP should purge the cache
379 Revision 1.650 2007/07/11 21:34:09 cheshire
380 <rdar://problem/5304766> Register IPSec tunnel with IPv4-only hostname and create NAT port mappings
381 Need to hold mDNS_Lock when calling mDNS_AddDynDNSHostName/mDNS_RemoveDynDNSHostName
383 Revision 1.649 2007/07/11 02:52:52 cheshire
384 <rdar://problem/5303807> Register IPv6-only hostname and don't create port mappings for AutoTunnel services
385 In uDNS_RegisterService, set HostTarget for AutoTunnel services
387 Revision 1.648 2007/07/09 23:48:12 cheshire
388 Add parentheses around bitwise operation for clarity
390 Revision 1.647 2007/07/06 21:17:55 cheshire
391 Initialize m->retryGetAddr to timenow + 0x78000000;
393 Revision 1.646 2007/07/06 18:55:49 cheshire
394 Initialize m->NextScheduledNATOp
396 Revision 1.645 2007/06/29 22:55:54 cheshire
397 Move declaration of DNSServer *s; Fixed incomplete comment.
399 Revision 1.644 2007/06/29 00:07:29 vazquez
400 <rdar://problem/5301908> Clean up NAT state machine (necessary for 6 other fixes)
402 Revision 1.643 2007/06/20 01:10:12 cheshire
403 <rdar://problem/5280520> Sync iPhone changes into main mDNSResponder code
405 Revision 1.642 2007/06/15 21:54:50 cheshire
406 <rdar://problem/4883206> Add packet logging to help debugging private browsing over TLS
408 Revision 1.641 2007/05/25 00:30:24 cheshire
409 When checking for duplicate questions, make sure privacy (or not) status, and long-lived (or not)
410 status matches. This is particularly important when doing a private query for an SOA record,
411 which will result in a call StartGetZoneData which does a non-private query for the same SOA record.
412 If the latter is tagged as a duplicate of the former, then we have deadlock, and neither will complete.
414 Revision 1.640 2007/05/25 00:25:44 cheshire
415 <rdar://problem/5227737> Need to enhance putRData to output all current known types
417 Revision 1.639 2007/05/23 00:51:33 cheshire
418 Increase threshold for shedding cache records from 512 to 3000. The 512 figure was calculated when
419 each cache entry took about 700 bytes; now they're only 164 bytes. Also, machines have more RAM these
420 days, and there are more services being advertised using DNS-SD, so it makes sense to cache more.
422 Revision 1.638 2007/05/23 00:43:16 cheshire
423 If uDNS UDP response has TC (truncated) bit set, don't interpret it as being the entire RRSet
425 Revision 1.637 2007/05/14 23:53:00 cheshire
426 Export mDNS_StartQuery_internal and mDNS_StopQuery_internal so they can be called from uDNS.c
428 Revision 1.636 2007/05/10 23:27:15 cheshire
429 Update mDNS_Deregister_internal debugging messages
431 Revision 1.635 2007/05/07 20:43:45 cheshire
432 <rdar://problem/4241419> Reduce the number of queries and announcements
434 Revision 1.634 2007/05/04 22:09:08 cheshire
435 Only do "restarting exponential backoff sequence" for mDNS questions
436 In mDNS_RegisterInterface, only retrigger mDNS questions
437 In uDNS_SetupDNSConfig, use ActivateUnicastQuery() instead of just setting q->ThisQInterval directly
439 Revision 1.633 2007/05/04 21:45:12 cheshire
440 Get rid of unused q->RestartTime; Get rid of uDNS_Close (synonym for uDNS_Sleep)
442 Revision 1.632 2007/05/04 20:20:50 cheshire
443 <rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData
444 Need to set srs->nta = mDNSNULL; when regState_NoTarget
446 Revision 1.631 2007/05/04 00:39:42 cheshire
447 <rdar://problem/4410011> Eliminate looping SOA lookups
448 When creating a cascade of negative SOA cache entries, CacheGroup pointer cg needs to be updated
449 each time round the loop to reference the right CacheGroup for each newly fabricated SOA name
451 Revision 1.630 2007/05/03 22:40:38 cheshire
452 <rdar://problem/4669229> mDNSResponder ignores bogus null target in SRV record
454 Revision 1.629 2007/05/03 00:15:51 cheshire
455 <rdar://problem/4410011> Eliminate looping SOA lookups
457 Revision 1.628 2007/05/02 22:21:33 cheshire
458 <rdar://problem/5167331> RegisterRecord and RegisterService need to cancel StartGetZoneData
460 Revision 1.627 2007/04/30 19:29:13 cheshire
461 Fix display of port number in "Updating DNS Server" message
463 Revision 1.626 2007/04/30 04:21:13 cheshire
464 Can't safely call AnswerLocalQuestions() from within mDNS_Deregister() -- need to defer it until mDNS_Execute time
466 Revision 1.625 2007/04/28 01:34:21 cheshire
467 Fixed crashing bug: We need to update rr->CRActiveQuestion pointers for *all* questions
468 (Code was explicitly ignoring wide-area unicast questions, leading to stale pointers and crashes)
470 Revision 1.624 2007/04/27 21:04:30 cheshire
471 On network configuration change, need to call uDNS_RegisterSearchDomains
473 Revision 1.623 2007/04/27 19:28:01 cheshire
474 Any code that calls StartGetZoneData needs to keep a handle to the structure, so
475 it can cancel it if necessary. (First noticed as a crash in Apple Remote Desktop
476 -- it would start a query and then quickly cancel it, and then when
477 StartGetZoneData completed, it had a dangling pointer and crashed.)
479 Revision 1.622 2007/04/26 16:09:22 cheshire
480 mDNS_StopQueryWithRemoves should ignore kDNSRecordTypePacketNegative records
482 Revision 1.621 2007/04/26 15:43:22 cheshire
483 Make sure DNSServer *s is non-null before using value in LogOperation
485 Revision 1.620 2007/04/26 13:11:05 cheshire
486 Fixed crash when logging out of VPN
488 Revision 1.619 2007/04/26 00:35:15 cheshire
489 <rdar://problem/5140339> uDNS: Domain discovery not working over VPN
490 Fixes to make sure results update correctly when connectivity changes (e.g. a DNS server
491 inside the firewall may give answers where a public one gives none, and vice versa.)
493 Revision 1.618 2007/04/25 19:26:01 cheshire
494 m->NextScheduledQuery was getting set too early in SendQueries()
495 Improved "SendQueries didn't send all its queries" debugging message
497 Revision 1.617 2007/04/25 17:48:22 cheshire
498 Update debugging message
500 Revision 1.616 2007/04/25 16:38:32 cheshire
501 If negative cache entry already exists, reactivate it instead of creating a new one
503 Revision 1.615 2007/04/25 02:14:38 cheshire
504 <rdar://problem/4246187> uDNS: Identical client queries should reference a single shared core query
505 Additional fixes to make LLQs work properly
507 Revision 1.614 2007/04/23 21:52:45 cheshire
508 <rdar://problem/5094009> IPv6 filtering in AirPort base station breaks Wide-Area Bonjour
510 Revision 1.613 2007/04/23 04:58:20 cheshire
511 <rdar://problem/5072548> Crash when setting extremely large TXT records
513 Revision 1.612 2007/04/22 20:39:38 cheshire
514 <rdar://problem/4633194> Add 20 to 120ms random delay to browses
516 Revision 1.611 2007/04/22 18:16:29 cheshire
517 Removed incorrect ActiveQuestion(q) check that was preventing suspended questions from getting reactivated
519 Revision 1.610 2007/04/22 06:02:02 cheshire
520 <rdar://problem/4615977> Query should immediately return failure when no server
522 Revision 1.609 2007/04/20 21:17:24 cheshire
523 For naming consistency, kDNSRecordTypeNegative should be kDNSRecordTypePacketNegative
525 Revision 1.608 2007/04/20 19:45:31 cheshire
526 In LogAllOperations mode, dump out unknown DNS packets in their entirety
528 Revision 1.607 2007/04/19 23:56:25 cheshire
529 Don't do cache-flush processing for LLQ answers
531 Revision 1.606 2007/04/19 22:50:53 cheshire
532 <rdar://problem/4246187> Identical client queries should reference a single shared core query
534 Revision 1.605 2007/04/19 20:06:41 cheshire
535 Rename field 'Private' (sounds like a boolean) to more informative 'AuthInfo' (it's a DomainAuthInfo pointer)
537 Revision 1.604 2007/04/19 18:03:04 cheshire
538 Add "const" declaration
540 Revision 1.603 2007/04/06 21:00:25 cheshire
543 Revision 1.602 2007/04/05 22:55:35 cheshire
544 <rdar://problem/5077076> Records are ending up in Lighthouse without expiry information
546 Revision 1.601 2007/04/04 21:48:52 cheshire
547 <rdar://problem/4720694> Combine unicast authoritative answer list with multicast list
549 Revision 1.600 2007/04/04 01:31:33 cheshire
550 Improve debugging message
552 Revision 1.599 2007/04/04 00:03:26 cheshire
553 <rdar://problem/5089862> DNSServiceQueryRecord is returning kDNSServiceErr_NoSuchRecord for empty rdata
555 Revision 1.598 2007/04/03 19:43:16 cheshire
556 Use mDNSSameIPPort (and similar) instead of accessing internal fields directly
558 Revision 1.597 2007/03/31 00:32:32 cheshire
559 After skipping OPT and TSIG, clear m->rec.r.resrec.RecordType
561 Revision 1.596 2007/03/28 20:59:26 cheshire
562 <rdar://problem/4743285> Remove inappropriate use of IsPrivateV4Addr()
564 Revision 1.595 2007/03/26 23:48:16 cheshire
565 <rdar://problem/4848295> Advertise model information via Bonjour
566 Refinements to reduce unnecessary transmissions of the DeviceInfo TXT record
568 Revision 1.594 2007/03/26 23:05:05 cheshire
569 <rdar://problem/5089257> Don't cache TSIG records
571 Revision 1.593 2007/03/23 17:40:08 cheshire
572 <rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision
574 Revision 1.592 2007/03/22 18:31:48 cheshire
575 Put dst parameter first in mDNSPlatformStrCopy/mDNSPlatformMemCopy, like conventional Posix strcpy/memcpy
577 Revision 1.591 2007/03/22 00:49:19 cheshire
578 <rdar://problem/4848295> Advertise model information via Bonjour
580 Revision 1.590 2007/03/21 23:05:59 cheshire
581 Rename uDNS_HostnameInfo to HostnameInfo; deleted some unused fields
583 Revision 1.589 2007/03/20 15:37:19 cheshire
584 Delete unnecessary log message
586 Revision 1.588 2007/03/20 00:24:44 cheshire
587 <rdar://problem/4175213> Should deliver "name registered" callback slightly *before* announcing PTR record
589 Revision 1.587 2007/03/16 22:10:56 cheshire
590 <rdar://problem/4471307> mDNS: Query for *either* type A or AAAA should return both types
592 Revision 1.586 2007/03/10 03:26:44 cheshire
593 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
595 Revision 1.585 2007/03/10 02:02:58 cheshire
596 <rdar://problem/4961667> uDNS: LLQ refresh response packet causes cached records to be removed from cache
597 Eliminate unnecessary "InternalResponseHndlr responseCallback" function pointer
599 Revision 1.584 2007/02/28 01:51:27 cheshire
600 Added comment about reverse-order IP address
602 Revision 1.583 2007/01/27 03:19:33 cheshire
603 Need to initialize question->sock
605 Revision 1.582 2007/01/25 00:40:16 cheshire
606 Unified CNAME-following functionality into cache management code (which means CNAME-following
607 should now also work for mDNS queries too); deleted defunct pktResponseHndlr() routine.
609 Revision 1.581 2007/01/23 02:56:11 cheshire
610 Store negative results in the cache, instead of generating them out of pktResponseHndlr()
612 Revision 1.580 2007/01/19 21:17:33 cheshire
613 StartLLQPolling needs to call SetNextQueryTime() to cause query to be done in a timely fashion
615 Revision 1.579 2007/01/19 18:39:10 cheshire
616 Fix a bunch of parameters that should have been declared "const"
618 Revision 1.578 2007/01/10 22:51:57 cheshire
619 <rdar://problem/4917539> Add support for one-shot private queries as well as long-lived private queries
621 Revision 1.577 2007/01/10 02:05:21 cheshire
622 Delay uDNS_SetupDNSConfig() until *after* the platform layer
623 has set up the interface list and security credentials
625 Revision 1.576 2007/01/09 02:40:57 cheshire
626 uDNS_SetupDNSConfig() shouldn't be called from mDNSMacOSX.c (platform support layer);
627 moved it to mDNS_Init() in mDNS.c (core code)
629 Revision 1.575 2007/01/09 00:17:25 cheshire
630 Improve "ERROR m->CurrentRecord already set" debugging messages
632 Revision 1.574 2007/01/05 08:30:41 cheshire
633 Trim excessive "$Log" checkin history from before 2006
634 (checkin history still available via "cvs log ..." of course)
636 Revision 1.573 2007/01/05 06:34:03 cheshire
637 Improve "ERROR m->CurrentQuestion already set" debugging messages
639 Revision 1.572 2007/01/04 23:11:11 cheshire
640 <rdar://problem/4720673> uDNS: Need to start caching unicast records
641 When an automatic browsing domain is removed, generate appropriate "remove" events for legacy queries
643 Revision 1.571 2007/01/04 21:45:20 cheshire
644 Added mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback macros,
645 to do additional lock sanity checking around callback invocations
647 Revision 1.570 2007/01/04 20:57:47 cheshire
648 Rename ReturnCNAME to ReturnIntermed (for ReturnIntermediates)
650 Revision 1.569 2007/01/04 20:27:27 cheshire
651 Change a LogMsg() to debugf()
653 Revision 1.568 2007/01/04 02:39:53 cheshire
654 <rdar://problem/4030599> Hostname passed into DNSServiceRegister ignored for Wide-Area service registrations
656 Revision 1.567 2006/12/21 00:01:37 cheshire
657 Tidy up code alignment
659 Revision 1.566 2006/12/20 04:07:34 cheshire
660 Remove uDNS_info substructure from AuthRecord_struct
662 Revision 1.565 2006/12/19 22:49:23 cheshire
663 Remove uDNS_info substructure from ServiceRecordSet_struct
665 Revision 1.564 2006/12/19 02:38:20 cheshire
666 Get rid of unnecessary duplicate query ID field from DNSQuestion_struct
668 Revision 1.563 2006/12/19 02:18:48 cheshire
669 Get rid of unnecessary duplicate "void *context" field from DNSQuestion_struct
671 Revision 1.562 2006/12/16 01:58:31 cheshire
672 <rdar://problem/4720673> uDNS: Need to start caching unicast records
674 Revision 1.561 2006/12/01 07:38:53 herscher
675 Only perform cache workaround fix if query is wide-area
677 Revision 1.560 2006/11/30 23:07:56 herscher
678 <rdar://problem/4765644> uDNS: Sync up with Lighthouse changes for Private DNS
680 Revision 1.559 2006/11/27 08:20:57 cheshire
681 Preliminary support for unifying the uDNS and mDNS code, including caching of uDNS answers
683 Revision 1.558 2006/11/10 07:44:03 herscher
684 <rdar://problem/4825493> Fix Daemon locking failures while toggling BTMM
686 Revision 1.557 2006/11/10 01:12:51 cheshire
687 <rdar://problem/4829718> Incorrect TTL corrections
689 Revision 1.556 2006/11/10 00:54:14 cheshire
690 <rdar://problem/4816598> Changing case of Computer Name doesn't work
692 Revision 1.555 2006/10/30 20:03:37 cheshire
693 <rdar://problem/4456945> After service restarts on different port, for a few seconds DNS-SD may return stale port number
695 Revision 1.554 2006/10/20 05:35:04 herscher
696 <rdar://problem/4720713> uDNS: Merge unicast active question list with multicast list.
698 Revision 1.553 2006/10/05 03:42:43 herscher
699 Remove embedded uDNS_info struct in DNSQuestion_struct
701 Revision 1.552 2006/09/15 21:20:15 cheshire
702 Remove uDNS_info substructure from mDNS_struct
704 Revision 1.551 2006/08/14 23:24:22 cheshire
705 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
707 Revision 1.550 2006/07/27 17:58:34 cheshire
708 Improved text of "SendQueries didn't send all its queries; will try again" debugging message
710 Revision 1.549 2006/07/20 22:07:31 mkrochma
711 <rdar://problem/4633196> Wide-area browsing is currently broken in TOT
712 More fixes for uninitialized variables
714 Revision 1.548 2006/07/20 19:30:19 mkrochma
715 <rdar://problem/4633196> Wide-area browsing sometimes doesn't work in TOT
717 Revision 1.547 2006/07/15 02:31:30 cheshire
718 <rdar://problem/4630812> Suppress log messages for certain old devices with inconsistent TXT RRSet TTLs
720 Revision 1.546 2006/07/07 01:09:09 cheshire
721 <rdar://problem/4472013> Add Private DNS server functionality to dnsextd
722 Only use mallocL/freeL debugging routines when building mDNSResponder, not dnsextd
724 Revision 1.545 2006/07/05 23:10:30 cheshire
725 <rdar://problem/4472014> Add Private DNS client functionality to mDNSResponder
726 Update mDNSSendDNSMessage() to use uDNS_TCPSocket type instead of "int"
728 Revision 1.544 2006/06/29 07:42:14 cheshire
729 <rdar://problem/3922989> Performance: Remove unnecessary SameDomainName() checks
731 Revision 1.543 2006/06/29 01:38:43 cheshire
732 <rdar://problem/4605285> Only request unicast responses on wake from sleep and network connection
734 Revision 1.542 2006/06/27 23:40:29 cheshire
735 Fix typo in comment: mis-spelled "compile"
737 Revision 1.541 2006/06/27 19:46:24 cheshire
738 Updated comments and debugging messages
740 Revision 1.540 2006/06/15 21:35:16 cheshire
741 Move definitions of mDNS_vsnprintf, mDNS_SetupResourceRecord, and some constants
742 from mDNS.c to DNSCommon.c, so they can be accessed from dnsextd code
744 Revision 1.539 2006/06/08 23:45:46 cheshire
745 Change SimultaneousProbe messages from debugf() to LogOperation()
747 Revision 1.538 2006/03/19 17:13:06 cheshire
748 <rdar://problem/4483117> Need faster purging of stale records
749 Shorten kDefaultReconfirmTimeForNoAnswer to five seconds
750 and reconfirm whole chain of antecedents ot once
752 Revision 1.537 2006/03/19 02:00:07 cheshire
753 <rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
755 Revision 1.536 2006/03/08 23:29:53 cheshire
756 <rdar://problem/4468716> Improve "Service Renamed" log message
758 Revision 1.535 2006/03/02 20:41:17 cheshire
759 <rdar://problem/4111464> After record update, old record sometimes remains in cache
760 Minor code tidying and comments to reduce the risk of similar programming errors in future
762 Revision 1.534 2006/03/02 03:25:46 cheshire
763 <rdar://problem/4111464> After record update, old record sometimes remains in cache
764 Code to harmonize RRSet TTLs was inadvertently rescuing expiring records
766 Revision 1.533 2006/02/26 00:54:41 cheshire
767 Fixes to avoid code generation warning/error on FreeBSD 7
771 #include "DNSCommon.h" // Defines general DNS untility routines
772 #include "uDNS.h" // Defines entry points into unicast-specific routines
774 // Disable certain benign warnings with Microsoft compilers
775 #if(defined(_MSC_VER))
776 // Disable "conditional expression is constant" warning for debug macros.
777 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
778 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
779 #pragma warning(disable:4127)
781 // Disable "assignment within conditional expression".
782 // Other compilers understand the convention that if you place the assignment expression within an extra pair
783 // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
784 // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
785 // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
786 #pragma warning(disable:4706)
789 // ***************************************************************************
790 #if COMPILER_LIKES_PRAGMA_MARK
791 #pragma mark - Program Constants
796 mDNSlocal
const mDNSInterfaceID mDNSInterfaceMark
= (mDNSInterfaceID
)~0;
798 // Any records bigger than this are considered 'large' records
799 #define SmallRecordLimit 1024
801 #define kMaxUpdateCredits 10
802 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
804 mDNSexport
const char *const mDNS_DomainTypeNames
[] =
806 "b._dns-sd._udp.", // Browse
807 "db._dns-sd._udp.", // Default Browse
808 "lb._dns-sd._udp.", // Automatic Browse
809 "r._dns-sd._udp.", // Registration
810 "dr._dns-sd._udp." // Default Registration
813 #ifdef UNICAST_DISABLED
814 #define uDNS_IsActiveQuery(q, u) mDNSfalse
817 // ***************************************************************************
818 #if COMPILER_LIKES_PRAGMA_MARK
820 #pragma mark - General Utility Functions
823 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
824 #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
826 mDNSexport
void SetNextQueryTime(mDNS
*const m
, const DNSQuestion
*const q
)
828 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
+1)
829 LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m
->mDNS_busy
, m
->mDNS_reentrancy
);
832 if (m
->mDNS_busy
!= m
->mDNS_reentrancy
+1) *(long*)0 = 0;
835 if (ActiveQuestion(q
))
837 mDNSs32 sendtime
= q
->LastQTime
+ q
->ThisQInterval
;
839 // Don't allow sendtime to be earlier than SuppressStdPort53Queries
840 if (!mDNSOpaque16IsZero(q
->TargetQID
) && !q
->LongLived
&& m
->SuppressStdPort53Queries
&& (sendtime
- m
->SuppressStdPort53Queries
< 0))
841 sendtime
= m
->SuppressStdPort53Queries
;
843 if (m
->NextScheduledQuery
- sendtime
> 0)
844 m
->NextScheduledQuery
= sendtime
;
848 mDNSexport CacheGroup
*CacheGroupForName(const mDNS
*const m
, const mDNSu32 slot
, const mDNSu32 namehash
, const domainname
*const name
)
851 for (cg
= m
->rrcache_hash
[slot
]; cg
; cg
=cg
->next
)
852 if (cg
->namehash
== namehash
&& SameDomainName(cg
->name
, name
))
857 mDNSlocal CacheGroup
*CacheGroupForRecord(const mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
859 return(CacheGroupForName(m
, slot
, rr
->namehash
, rr
->name
));
862 mDNSlocal mDNSBool
AddressIsLocalSubnet(mDNS
*const m
, const mDNSInterfaceID InterfaceID
, const mDNSAddr
*addr
)
864 NetworkInterfaceInfo
*intf
;
866 if (addr
->type
== mDNSAddrType_IPv4
)
868 // Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception
869 if (mDNSv4AddressIsLinkLocal(&addr
->ip
.v4
)) return(mDNStrue
);
870 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
871 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
872 if (((intf
->ip
.ip
.v4
.NotAnInteger
^ addr
->ip
.v4
.NotAnInteger
) & intf
->mask
.ip
.v4
.NotAnInteger
) == 0)
876 if (addr
->type
== mDNSAddrType_IPv6
)
878 if (mDNSv6AddressIsLinkLocal(&addr
->ip
.v4
)) return(mDNStrue
);
879 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
880 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
881 if ((((intf
->ip
.ip
.v6
.l
[0] ^ addr
->ip
.v6
.l
[0]) & intf
->mask
.ip
.v6
.l
[0]) == 0) &&
882 (((intf
->ip
.ip
.v6
.l
[1] ^ addr
->ip
.v6
.l
[1]) & intf
->mask
.ip
.v6
.l
[1]) == 0) &&
883 (((intf
->ip
.ip
.v6
.l
[2] ^ addr
->ip
.v6
.l
[2]) & intf
->mask
.ip
.v6
.l
[2]) == 0) &&
884 (((intf
->ip
.ip
.v6
.l
[3] ^ addr
->ip
.v6
.l
[3]) & intf
->mask
.ip
.v6
.l
[3]) == 0))
891 // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
892 // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
893 mDNSlocal
void AnswerLocalOnlyQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, AuthRecord
*rr
, QC_result AddRecord
)
895 // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
896 if (AddRecord
) rr
->LocalAnswer
= mDNStrue
;
897 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
898 if (q
->QuestionCallback
&& !q
->NoAnswer
)
899 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
900 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
903 // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers
904 // to each, stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
905 // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
906 // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
907 mDNSlocal
void AnswerLocalQuestions(mDNS
*const m
, AuthRecord
*rr
, QC_result AddRecord
)
909 if (m
->CurrentQuestion
)
910 LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
912 m
->CurrentQuestion
= m
->LocalOnlyQuestions
;
913 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewLocalOnlyQuestions
)
915 DNSQuestion
*q
= m
->CurrentQuestion
;
916 m
->CurrentQuestion
= q
->next
;
917 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
918 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
921 // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
922 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
924 m
->CurrentQuestion
= m
->Questions
;
925 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
927 DNSQuestion
*q
= m
->CurrentQuestion
;
928 m
->CurrentQuestion
= q
->next
;
929 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
930 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
934 m
->CurrentQuestion
= mDNSNULL
;
937 // ***************************************************************************
938 #if COMPILER_LIKES_PRAGMA_MARK
940 #pragma mark - Resource Record Utility Functions
943 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
945 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
946 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
947 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
948 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
950 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
951 (ResourceRecordIsValidAnswer(RR) && \
952 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
954 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
955 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
957 #define InitialAnnounceCount ((mDNSu8)8)
959 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
960 // This means that because the announce interval is doubled after sending the first packet, the first
961 // observed on-the-wire inter-packet interval between announcements is actually one second.
962 // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
963 #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
964 #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
965 #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
967 #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
968 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
969 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
971 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
972 #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
973 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
974 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
976 #define MaxUnansweredQueries 4
978 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
979 // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
980 // TTL and rdata may differ.
981 // This is used for cache flush management:
982 // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
983 // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
985 mDNSlocal mDNSBool
SameResourceRecordSignature(const AuthRecord
*const r1
, const AuthRecord
*const r2
)
987 if (!r1
) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse
); }
988 if (!r2
) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse
); }
989 if (r1
->resrec
.InterfaceID
&&
990 r2
->resrec
.InterfaceID
&&
991 r1
->resrec
.InterfaceID
!= r2
->resrec
.InterfaceID
) return(mDNSfalse
);
993 r1
->resrec
.rrtype
== r2
->resrec
.rrtype
&&
994 r1
->resrec
.rrclass
== r2
->resrec
.rrclass
&&
995 r1
->resrec
.namehash
== r2
->resrec
.namehash
&&
996 SameDomainName(r1
->resrec
.name
, r2
->resrec
.name
));
999 // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
1000 // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
1001 // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
1002 // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
1003 // so a response of any type should match, even if it is not actually the type the client plans to use.
1004 mDNSlocal mDNSBool
PacketRRMatchesSignature(const CacheRecord
*const pktrr
, const AuthRecord
*const authrr
)
1006 if (!pktrr
) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse
); }
1007 if (!authrr
) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse
); }
1008 if (pktrr
->resrec
.InterfaceID
&&
1009 authrr
->resrec
.InterfaceID
&&
1010 pktrr
->resrec
.InterfaceID
!= authrr
->resrec
.InterfaceID
) return(mDNSfalse
);
1011 if (!(authrr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) && pktrr
->resrec
.rrtype
!= authrr
->resrec
.rrtype
) return(mDNSfalse
);
1013 pktrr
->resrec
.rrclass
== authrr
->resrec
.rrclass
&&
1014 pktrr
->resrec
.namehash
== authrr
->resrec
.namehash
&&
1015 SameDomainName(pktrr
->resrec
.name
, authrr
->resrec
.name
));
1018 // IdenticalResourceRecord returns true if two resources records have
1019 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
1021 // IdenticalSameNameRecord is the same, except it skips the expensive SameDomainName() check,
1022 // which is at its most expensive and least useful in cases where we know in advance that the names match
1024 mDNSlocal mDNSBool
IdenticalResourceRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
1026 if (!r1
) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse
); }
1027 if (!r2
) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse
); }
1028 if (r1
->rrtype
!= r2
->rrtype
|| r1
->rrclass
!= r2
->rrclass
|| r1
->namehash
!= r2
->namehash
|| !SameDomainName(r1
->name
, r2
->name
))
1030 return(SameRData(r1
, r2
));
1033 mDNSlocal mDNSBool
IdenticalSameNameRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
1035 if (!r1
) { LogMsg("IdenticalSameNameRecord ERROR: r1 is NULL"); return(mDNSfalse
); }
1036 if (!r2
) { LogMsg("IdenticalSameNameRecord ERROR: r2 is NULL"); return(mDNSfalse
); }
1037 if (r1
->rrtype
!= r2
->rrtype
|| r1
->rrclass
!= r2
->rrclass
)
1040 #if VerifySameNameAssumptions
1041 if (r1
->namehash
!= r2
->namehash
|| !SameDomainName(r1
->name
, r2
->name
))
1043 LogMsg("Bogus IdenticalSameNameRecord call: %##s does not match %##s", r1
->name
->c
, r1
->name
->c
);
1048 return(SameRData(r1
, r2
));
1051 // CacheRecord *ks is the CacheRecord from the known answer list in the query.
1052 // This is the information that the requester believes to be correct.
1053 // AuthRecord *rr is the answer we are proposing to give, if not suppressed.
1054 // This is the information that we believe to be correct.
1055 // We've already determined that we plan to give this answer on this interface
1056 // (either the record is non-specific, or it is specific to this interface)
1057 // so now we just need to check the name, type, class, rdata and TTL.
1058 mDNSlocal mDNSBool
ShouldSuppressKnownAnswer(const CacheRecord
*const ka
, const AuthRecord
*const rr
)
1060 // If RR signature is different, or data is different, then don't suppress our answer
1061 if (!IdenticalResourceRecord(&ka
->resrec
, &rr
->resrec
)) return(mDNSfalse
);
1063 // If the requester's indicated TTL is less than half the real TTL,
1064 // we need to give our answer before the requester's copy expires.
1065 // If the requester's indicated TTL is at least half the real TTL,
1066 // then we can suppress our answer this time.
1067 // If the requester's indicated TTL is greater than the TTL we believe,
1068 // then that's okay, and we don't need to do anything about it.
1069 // (If two responders on the network are offering the same information,
1070 // that's okay, and if they are offering the information with different TTLs,
1071 // the one offering the lower TTL should defer to the one offering the higher TTL.)
1072 return(mDNSBool
)(ka
->resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/ 2);
1075 mDNSlocal
void SetNextAnnounceProbeTime(mDNS
*const m
, const AuthRecord
*const rr
)
1077 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
1079 //LogMsg("ProbeCount %d Next %ld %s",
1080 // rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
1081 if (m
->NextScheduledProbe
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
1082 m
->NextScheduledProbe
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
1084 else if (rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
))
1086 if (m
->NextScheduledResponse
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
1087 m
->NextScheduledResponse
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
1091 mDNSlocal
void InitializeLastAPTime(mDNS
*const m
, AuthRecord
*const rr
)
1093 // To allow us to aggregate probes when a group of services are registered together,
1094 // the first probe is delayed 1/4 second. This means the common-case behaviour is:
1095 // 1/4 second wait; probe
1096 // 1/4 second wait; probe
1097 // 1/4 second wait; probe
1098 // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
1100 // If we have no probe suppression time set, or it is in the past, set it now
1101 if (m
->SuppressProbes
== 0 || m
->SuppressProbes
- m
->timenow
< 0)
1103 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ DefaultProbeIntervalForTypeUnique
);
1104 // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
1105 if (m
->SuppressProbes
- m
->NextScheduledProbe
>= 0)
1106 m
->SuppressProbes
= m
->NextScheduledProbe
;
1107 // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
1108 if (m
->SuppressProbes
- m
->NextScheduledQuery
>= 0)
1109 m
->SuppressProbes
= m
->NextScheduledQuery
;
1112 rr
->LastAPTime
= m
->SuppressProbes
- rr
->ThisAPInterval
;
1113 // Set LastMCTime to now, to inhibit multicast responses
1114 // (no need to send additional multicast responses when we're announcing anyway)
1115 rr
->LastMCTime
= m
->timenow
;
1116 rr
->LastMCInterface
= mDNSInterfaceMark
;
1118 // If this is a record type that's not going to probe, then delay its first announcement so that
1119 // it will go out synchronized with the first announcement for the other records that *are* probing.
1120 // This is a minor performance tweak that helps keep groups of related records synchronized together.
1121 // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
1122 // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
1123 // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
1124 // because they will meet the criterion of being at least half-way to their scheduled announcement time.
1125 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
)
1126 rr
->LastAPTime
+= DefaultProbeIntervalForTypeUnique
* DefaultProbeCountForTypeUnique
+ rr
->ThisAPInterval
/ 2;
1128 SetNextAnnounceProbeTime(m
, rr
);
1131 // Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname
1132 // Eventually we should unify this with GetServiceTarget() in uDNS.c
1133 mDNSlocal
void SetTargetToHostName(mDNS
*const m
, AuthRecord
*const rr
)
1135 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
1137 if (!target
) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr
->resrec
.rrtype
);
1139 if (target
&& SameDomainName(target
, &m
->MulticastHostname
))
1140 debugf("SetTargetToHostName: Target of %##s is already %##s", rr
->resrec
.name
->c
, target
->c
);
1142 if (target
&& !SameDomainName(target
, &m
->MulticastHostname
))
1144 AssignDomainName(target
, &m
->MulticastHostname
);
1145 SetNewRData(&rr
->resrec
, mDNSNULL
, 0); // Update rdlength, rdestimate, rdatahash
1147 // If we're in the middle of probing this record, we need to start again,
1148 // because changing its rdata may change the outcome of the tie-breaker.
1149 // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
1150 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
1152 // If we've announced this record, we really should send a goodbye packet for the old rdata before
1153 // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
1154 // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
1155 if (rr
->RequireGoodbye
&& rr
->resrec
.RecordType
== kDNSRecordTypeShared
)
1156 debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating",
1157 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1159 rr
->AnnounceCount
= InitialAnnounceCount
;
1160 rr
->RequireGoodbye
= mDNSfalse
;
1161 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
1162 InitializeLastAPTime(m
,rr
);
1166 mDNSlocal
void AcknowledgeRecord(mDNS
*const m
, AuthRecord
*const rr
)
1168 if (rr
->RecordCallback
)
1170 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
1171 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
1172 rr
->Acknowledged
= mDNStrue
;
1173 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
1174 rr
->RecordCallback(m
, rr
, mStatus_NoError
);
1175 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
1179 // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
1180 #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \
1181 ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
1182 #define RecordIsLocalDuplicate(A,B) \
1183 ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
1185 // Exported so uDNS.c can call this
1186 mDNSexport mStatus
mDNS_Register_internal(mDNS
*const m
, AuthRecord
*const rr
)
1188 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
1190 AuthRecord
**p
= &m
->ResourceRecords
;
1191 AuthRecord
**d
= &m
->DuplicateRecords
;
1193 if ((mDNSs32
)rr
->resrec
.rroriginalttl
<= 0)
1194 { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m
, rr
)); return(mStatus_BadParamErr
); }
1196 if (!rr
->resrec
.RecordType
)
1197 { LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m
, rr
)); return(mStatus_BadParamErr
); }
1199 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
1200 while (*d
&& *d
!= rr
) d
=&(*d
)->next
;
1203 LogMsg("Error! Tried to register AuthRecord %p %##s (%s) that's already in the list",
1204 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1205 return(mStatus_AlreadyRegistered
);
1208 if (rr
->DependentOn
)
1210 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
1211 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
1214 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
1215 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1216 return(mStatus_Invalid
);
1218 if (!(rr
->DependentOn
->resrec
.RecordType
& (kDNSRecordTypeUnique
| kDNSRecordTypeVerified
)))
1220 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
1221 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->DependentOn
->resrec
.RecordType
);
1222 return(mStatus_Invalid
);
1226 // If this resource record is referencing a specific interface, make sure it exists
1227 if (rr
->resrec
.InterfaceID
&& rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
1229 NetworkInterfaceInfo
*intf
;
1230 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
1231 if (intf
->InterfaceID
== rr
->resrec
.InterfaceID
) break;
1234 debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr
->resrec
.InterfaceID
);
1235 return(mStatus_BadReferenceErr
);
1239 rr
->next
= mDNSNULL
;
1241 // Field Group 1: The actual information pertaining to this resource record
1242 // Set up by client prior to call
1244 // Field Group 2: Persistent metadata for Authoritative Records
1245 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1246 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1247 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1248 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
1249 // rr->Callback = already set in mDNS_SetupResourceRecord
1250 // rr->Context = already set in mDNS_SetupResourceRecord
1251 // rr->RecordType = already set in mDNS_SetupResourceRecord
1252 // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
1253 // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
1254 // Make sure target is not uninitialized data, or we may crash writing debugging log messages
1255 if (rr
->AutoTarget
&& target
) target
->c
[0] = 0;
1257 // Field Group 3: Transient state for Authoritative Records
1258 rr
->Acknowledged
= mDNSfalse
;
1259 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
1260 rr
->AnnounceCount
= InitialAnnounceCount
;
1261 rr
->RequireGoodbye
= mDNSfalse
;
1262 rr
->LocalAnswer
= mDNSfalse
;
1263 rr
->IncludeInProbe
= mDNSfalse
;
1264 rr
->ImmedAnswer
= mDNSNULL
;
1265 rr
->ImmedUnicast
= mDNSfalse
;
1266 rr
->ImmedAdditional
= mDNSNULL
;
1267 rr
->SendRNow
= mDNSNULL
;
1268 rr
->v4Requester
= zerov4Addr
;
1269 rr
->v6Requester
= zerov6Addr
;
1270 rr
->NextResponse
= mDNSNULL
;
1271 rr
->NR_AnswerTo
= mDNSNULL
;
1272 rr
->NR_AdditionalTo
= mDNSNULL
;
1273 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
1274 if (!rr
->AutoTarget
) InitializeLastAPTime(m
, rr
);
1275 // rr->LastAPTime = Set for us in InitializeLastAPTime()
1276 // rr->LastMCTime = Set for us in InitializeLastAPTime()
1277 // rr->LastMCInterface = Set for us in InitializeLastAPTime()
1278 rr
->NewRData
= mDNSNULL
;
1279 rr
->newrdlength
= 0;
1280 rr
->UpdateCallback
= mDNSNULL
;
1281 rr
->UpdateCredits
= kMaxUpdateCredits
;
1282 rr
->NextUpdateCredit
= 0;
1283 rr
->UpdateBlocked
= 0;
1285 // Field Group 4: Transient uDNS state for Authoritative Records
1286 rr
->state
= regState_Zero
;
1292 rr
->UpdateServer
= zeroAddr
;
1293 rr
->UpdatePort
= zeroIPPort
;
1298 rr
->InFlightRData
= 0;
1299 rr
->InFlightRDLen
= 0;
1300 rr
->QueuedRData
= 0;
1301 rr
->QueuedRDLen
= 0;
1303 // rr->resrec.interface = already set in mDNS_SetupResourceRecord
1304 // rr->resrec.name->c = MUST be set by client
1305 // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
1306 // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
1307 // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
1308 // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
1311 SetTargetToHostName(m
, rr
); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
1314 rr
->resrec
.rdlength
= GetRDLength(&rr
->resrec
, mDNSfalse
);
1315 rr
->resrec
.rdestimate
= GetRDLength(&rr
->resrec
, mDNStrue
);
1318 if (!ValidateDomainName(rr
->resrec
.name
))
1319 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
1321 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
1322 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
1323 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
1324 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& rr
->resrec
.rdlength
== 0) { rr
->resrec
.rdlength
= 1; rr
->resrec
.rdata
->u
.txt
.c
[0] = 0; }
1326 // Don't do this until *after* we've set rr->resrec.rdlength
1327 if (!ValidateRData(rr
->resrec
.rrtype
, rr
->resrec
.rdlength
, rr
->resrec
.rdata
))
1328 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
1330 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
1331 rr
->resrec
.rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->resrec
.rdlength
, &rr
->resrec
.rdata
->u
);
1333 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
1335 // If this is supposed to be unique, make sure we don't have any name conflicts
1336 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1338 const AuthRecord
*s1
= rr
->RRSet
? rr
->RRSet
: rr
;
1339 for (r
= m
->ResourceRecords
; r
; r
=r
->next
)
1341 const AuthRecord
*s2
= r
->RRSet
? r
->RRSet
: r
;
1342 if (s1
!= s2
&& SameResourceRecordSignature(r
, rr
) && !SameRData(&r
->resrec
, &rr
->resrec
))
1345 if (r
) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
1347 debugf("Name conflict %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1348 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
1349 rr
->resrec
.rroriginalttl
= 0;
1350 rr
->ImmedAnswer
= mDNSInterfaceMark
;
1351 m
->NextScheduledResponse
= m
->timenow
;
1356 // Now that we've finished building our new record, make sure it's not identical to one we already have
1357 for (r
= m
->ResourceRecords
; r
; r
=r
->next
) if (RecordIsLocalDuplicate(r
, rr
)) break;
1361 debugf("Adding to duplicate list %p %s", rr
, ARDisplayString(m
,rr
));
1363 // If the previous copy of this record is already verified unique,
1364 // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
1365 // Setting ProbeCount to zero will cause SendQueries() to advance this record to
1366 // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
1367 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& r
->resrec
.RecordType
== kDNSRecordTypeVerified
)
1372 debugf("Adding to active record list %p %s", rr
, ARDisplayString(m
,rr
));
1373 if (!m
->NewLocalRecords
) m
->NewLocalRecords
= rr
;
1377 if (!AuthRecord_uDNS(rr
))
1379 // For records that are not going to probe, acknowledge them right away
1380 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
&& rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
1381 AcknowledgeRecord(m
, rr
);
1383 #ifndef UNICAST_DISABLED
1386 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
) rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
1388 rr
->AnnounceCount
= 0;
1389 rr
->state
= regState_FetchingZoneData
;
1390 rr
->uselease
= mDNStrue
;
1391 rr
->nta
= StartGetZoneData(m
, rr
->resrec
.name
, ZoneServiceUpdate
, RecordRegistrationGotZoneData
, rr
);
1395 return(mStatus_NoError
);
1398 mDNSlocal
void RecordProbeFailure(mDNS
*const m
, const AuthRecord
*const rr
)
1400 m
->ProbeFailTime
= m
->timenow
;
1401 m
->NumFailedProbes
++;
1402 // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
1403 // If a bunch of hosts have all been configured with the same name, then they'll all
1404 // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
1405 // up to name-10. After that they'll start adding random increments in the range 1-100,
1406 // so they're more likely to branch out in the available namespace and settle on a set of
1407 // unique names quickly. If after five more tries the host is still conflicting, then we
1408 // may have a serious problem, so we start rate-limiting so we don't melt down the network.
1409 if (m
->NumFailedProbes
>= 15)
1411 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
* 5);
1412 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
1413 m
->NumFailedProbes
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1417 mDNSlocal
void CompleteRDataUpdate(mDNS
*const m
, AuthRecord
*const rr
)
1419 RData
*OldRData
= rr
->resrec
.rdata
;
1420 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
); // Update our rdata
1421 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
1422 if (rr
->UpdateCallback
)
1423 rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
1426 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
1427 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1428 // Exported so uDNS.c can call this
1429 mDNSexport mStatus
mDNS_Deregister_internal(mDNS
*const m
, AuthRecord
*const rr
, mDNS_Dereg_type drt
)
1432 mDNSu8 RecordType
= rr
->resrec
.RecordType
;
1433 AuthRecord
**p
= &m
->ResourceRecords
; // Find this record in our list of active records
1435 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
1439 // We found our record on the main list. See if there are any duplicates that need special handling.
1440 if (drt
== mDNS_Dereg_conflict
) // If this was a conflict, see that all duplicates get the same treatment
1442 // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
1443 // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
1444 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
) if (RecordIsLocalDuplicate(r2
, rr
)) r2
->ProbeCount
= 0xFF;
1448 // Before we delete the record (and potentially send a goodbye packet)
1449 // first see if we have a record on the duplicate list ready to take over from it.
1450 AuthRecord
**d
= &m
->DuplicateRecords
;
1451 while (*d
&& !RecordIsLocalDuplicate(*d
, rr
)) d
=&(*d
)->next
;
1454 AuthRecord
*dup
= *d
;
1455 debugf("Duplicate record %p taking over from %p %##s (%s)",
1456 dup
, rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1457 *d
= dup
->next
; // Cut replacement record from DuplicateRecords list
1458 dup
->next
= rr
->next
; // And then...
1459 rr
->next
= dup
; // ... splice it in right after the record we're about to delete
1460 dup
->resrec
.RecordType
= rr
->resrec
.RecordType
;
1461 dup
->ProbeCount
= rr
->ProbeCount
;
1462 dup
->AnnounceCount
= rr
->AnnounceCount
;
1463 dup
->RequireGoodbye
= rr
->RequireGoodbye
;
1464 dup
->ImmedAnswer
= rr
->ImmedAnswer
;
1465 dup
->ImmedUnicast
= rr
->ImmedUnicast
;
1466 dup
->ImmedAdditional
= rr
->ImmedAdditional
;
1467 dup
->v4Requester
= rr
->v4Requester
;
1468 dup
->v6Requester
= rr
->v6Requester
;
1469 dup
->ThisAPInterval
= rr
->ThisAPInterval
;
1470 dup
->LastAPTime
= rr
->LastAPTime
;
1471 dup
->LastMCTime
= rr
->LastMCTime
;
1472 dup
->LastMCInterface
= rr
->LastMCInterface
;
1473 rr
->RequireGoodbye
= mDNSfalse
;
1479 // We didn't find our record on the main list; try the DuplicateRecords list instead.
1480 p
= &m
->DuplicateRecords
;
1481 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
1482 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
1483 if (*p
) rr
->RequireGoodbye
= mDNSfalse
;
1484 if (*p
) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
1485 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1490 // No need to log an error message if we already know this is a potentially repeated deregistration
1491 if (drt
!= mDNS_Dereg_repeat
)
1492 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list",
1493 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1494 return(mStatus_BadReferenceErr
);
1497 // If this is a shared record and we've announced it at least once,
1498 // we need to retract that announcement before we delete the record
1500 // If this is a record (including mDNSInterface_LocalOnly records) for which we've given local answers then
1501 // it's tempting to just do "AnswerLocalQuestions(m, rr, mDNSfalse)" here, but that would not not be safe.
1502 // The AnswerLocalQuestions routine walks the question list invoking client callbacks, using the "m->CurrentQuestion"
1503 // mechanism to cope with the client callback modifying the question list while that's happening.
1504 // However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain)
1505 // which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice.
1506 // More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other
1507 // records, thereby invoking yet more callbacks, without limit.
1508 // The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending
1509 // actual goodbye packets.
1511 #ifndef UNICAST_DISABLED
1512 if (AuthRecord_uDNS(rr
) && rr
->RequireGoodbye
)
1514 if (rr
->tcp
) { DisposeTCPConn(rr
->tcp
); rr
->tcp
= mDNSNULL
; }
1515 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
1516 uDNS_DeregisterRecord(m
, rr
);
1517 // At this point unconditionally we bail out
1518 // Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
1519 // which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
1520 // process and will complete asynchronously. Either way we don't need to do anything more here.
1521 return(mStatus_NoError
);
1523 #endif UNICAST_DISABLED
1525 if (RecordType
== kDNSRecordTypeShared
&& (rr
->RequireGoodbye
|| rr
->LocalAnswer
))
1527 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %s", ARDisplayString(m
, rr
));
1528 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
1529 rr
->resrec
.rroriginalttl
= 0;
1530 rr
->ImmedAnswer
= mDNSInterfaceMark
;
1531 if (m
->NextScheduledResponse
- (m
->timenow
+ mDNSPlatformOneSecond
/10) >= 0)
1532 m
->NextScheduledResponse
= (m
->timenow
+ mDNSPlatformOneSecond
/10);
1536 *p
= rr
->next
; // Cut this record from the list
1537 // If someone is about to look at this, bump the pointer forward
1538 if (m
->CurrentRecord
== rr
) m
->CurrentRecord
= rr
->next
;
1539 if (m
->NewLocalRecords
== rr
) m
->NewLocalRecords
= rr
->next
;
1540 rr
->next
= mDNSNULL
;
1542 if (RecordType
== kDNSRecordTypeUnregistered
)
1543 LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m
, rr
));
1544 else if (RecordType
== kDNSRecordTypeDeregistering
)
1545 LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m
, rr
));
1548 verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m
, rr
));
1549 rr
->resrec
.RecordType
= kDNSRecordTypeUnregistered
;
1552 if ((drt
== mDNS_Dereg_conflict
|| drt
== mDNS_Dereg_repeat
) && RecordType
== kDNSRecordTypeShared
)
1553 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
1554 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1556 // If we have an update queued up which never executed, give the client a chance to free that memory
1557 if (rr
->NewRData
) CompleteRDataUpdate(m
, rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
1559 if (rr
->nta
) { CancelGetZoneData(m
, rr
->nta
); rr
->nta
= mDNSNULL
; }
1560 if (rr
->tcp
) { DisposeTCPConn(rr
->tcp
); rr
->tcp
= mDNSNULL
; }
1562 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
1563 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
1564 // In this case the likely client action to the mStatus_MemFree message is to free the memory,
1565 // so any attempt to touch rr after this is likely to lead to a crash.
1566 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
1567 if (drt
!= mDNS_Dereg_conflict
)
1569 if (rr
->RecordCallback
)
1570 rr
->RecordCallback(m
, rr
, mStatus_MemFree
); // MUST NOT touch rr after this
1574 RecordProbeFailure(m
, rr
);
1575 if (rr
->RecordCallback
)
1576 rr
->RecordCallback(m
, rr
, mStatus_NameConflict
); // MUST NOT touch rr after this
1577 // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
1578 // Note that with all the client callbacks going on, by the time we get here all the
1579 // records we marked may have been explicitly deregistered by the client anyway.
1580 r2
= m
->DuplicateRecords
;
1583 if (r2
->ProbeCount
!= 0xFF) r2
= r2
->next
;
1584 else { mDNS_Deregister_internal(m
, r2
, mDNS_Dereg_conflict
); r2
= m
->DuplicateRecords
; }
1587 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
1589 return(mStatus_NoError
);
1592 // ***************************************************************************
1593 #if COMPILER_LIKES_PRAGMA_MARK
1595 #pragma mark - Packet Sending Functions
1598 mDNSlocal
void AddRecordToResponseList(AuthRecord
***nrpp
, AuthRecord
*rr
, AuthRecord
*add
)
1600 if (rr
->NextResponse
== mDNSNULL
&& *nrpp
!= &rr
->NextResponse
)
1603 // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
1604 // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
1605 // The referenced record will definitely be acceptable (by recursive application of this rule)
1606 if (add
&& add
->NR_AdditionalTo
) add
= add
->NR_AdditionalTo
;
1607 rr
->NR_AdditionalTo
= add
;
1608 *nrpp
= &rr
->NextResponse
;
1610 debugf("AddRecordToResponseList: %##s (%s) already in list", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
1613 mDNSlocal
void AddAdditionalsToResponseList(mDNS
*const m
, AuthRecord
*ResponseRecords
, AuthRecord
***nrpp
, const mDNSInterfaceID InterfaceID
)
1615 AuthRecord
*rr
, *rr2
;
1616 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // For each record we plan to put
1618 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
1619 // later in the "for" loop, and we will follow further "additional" links then.)
1620 if (rr
->Additional1
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional1
, InterfaceID
))
1621 AddRecordToResponseList(nrpp
, rr
->Additional1
, rr
);
1623 if (rr
->Additional2
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional2
, InterfaceID
))
1624 AddRecordToResponseList(nrpp
, rr
->Additional2
, rr
);
1626 // For SRV records, automatically add the Address record(s) for the target host
1627 if (rr
->resrec
.rrtype
== kDNSType_SRV
)
1629 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
1630 if (RRTypeIsAddressType(rr2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
1631 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceID
) && // ... which are valid for answer ...
1632 rr
->resrec
.rdatahash
== rr2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
1633 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, rr2
->resrec
.name
))
1634 AddRecordToResponseList(nrpp
, rr2
, rr
);
1636 else if (RRTypeIsAddressType(rr
->resrec
.rrtype
)) // For A or AAAA, put counterpart as additional
1638 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
1639 if (RRTypeIsAddressType(rr2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
1640 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceID
) && // ... which are valid for answer ...
1641 rr
->resrec
.namehash
== rr2
->resrec
.namehash
&& // ... and have the same name
1642 SameDomainName(rr
->resrec
.name
, rr2
->resrec
.name
))
1643 AddRecordToResponseList(nrpp
, rr2
, rr
);
1645 else if (rr
->resrec
.rrtype
== kDNSType_PTR
) // For service PTR, see if we want to add DeviceInfo record
1647 if (ResourceRecordIsValidInterfaceAnswer(&m
->DeviceInfo
, InterfaceID
) &&
1648 SameDomainLabel(rr
->resrec
.rdata
->u
.name
.c
, m
->DeviceInfo
.resrec
.name
->c
))
1649 AddRecordToResponseList(nrpp
, &m
->DeviceInfo
, rr
);
1654 mDNSlocal
void SendDelayedUnicastResponse(mDNS
*const m
, const mDNSAddr
*const dest
, const mDNSInterfaceID InterfaceID
)
1657 AuthRecord
*ResponseRecords
= mDNSNULL
;
1658 AuthRecord
**nrp
= &ResponseRecords
;
1660 // Make a list of all our records that need to be unicast to this destination
1661 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1663 // If we find we can no longer unicast this answer, clear ImmedUnicast
1664 if (rr
->ImmedAnswer
== mDNSInterfaceMark
||
1665 mDNSSameIPv4Address(rr
->v4Requester
, onesIPv4Addr
) ||
1666 mDNSSameIPv6Address(rr
->v6Requester
, onesIPv6Addr
) )
1667 rr
->ImmedUnicast
= mDNSfalse
;
1669 if (rr
->ImmedUnicast
&& rr
->ImmedAnswer
== InterfaceID
)
1670 if ((dest
->type
== mDNSAddrType_IPv4
&& mDNSSameIPv4Address(rr
->v4Requester
, dest
->ip
.v4
)) ||
1671 (dest
->type
== mDNSAddrType_IPv6
&& mDNSSameIPv6Address(rr
->v6Requester
, dest
->ip
.v6
)))
1673 rr
->ImmedAnswer
= mDNSNULL
; // Clear the state fields
1674 rr
->ImmedUnicast
= mDNSfalse
;
1675 rr
->v4Requester
= zerov4Addr
;
1676 rr
->v6Requester
= zerov6Addr
;
1677 if (rr
->NextResponse
== mDNSNULL
&& nrp
!= &rr
->NextResponse
) // rr->NR_AnswerTo
1678 { rr
->NR_AnswerTo
= (mDNSu8
*)~0; *nrp
= rr
; nrp
= &rr
->NextResponse
; }
1682 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
1684 while (ResponseRecords
)
1686 mDNSu8
*responseptr
= m
->omsg
.data
;
1688 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
1690 // Put answers in the packet
1691 while (ResponseRecords
&& ResponseRecords
->NR_AnswerTo
)
1693 rr
= ResponseRecords
;
1694 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1695 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1696 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
1697 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1698 if (!newptr
&& m
->omsg
.h
.numAnswers
) break; // If packet full, send it now
1699 if (newptr
) responseptr
= newptr
;
1700 ResponseRecords
= rr
->NextResponse
;
1701 rr
->NextResponse
= mDNSNULL
;
1702 rr
->NR_AnswerTo
= mDNSNULL
;
1703 rr
->NR_AdditionalTo
= mDNSNULL
;
1704 rr
->RequireGoodbye
= mDNStrue
;
1707 // Add additionals, if there's space
1708 while (ResponseRecords
&& !ResponseRecords
->NR_AnswerTo
)
1710 rr
= ResponseRecords
;
1711 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1712 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1713 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
1714 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1716 if (newptr
) responseptr
= newptr
;
1717 if (newptr
&& m
->omsg
.h
.numAnswers
) rr
->RequireGoodbye
= mDNStrue
;
1718 else if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) rr
->ImmedAnswer
= mDNSInterfaceMark
;
1719 ResponseRecords
= rr
->NextResponse
;
1720 rr
->NextResponse
= mDNSNULL
;
1721 rr
->NR_AnswerTo
= mDNSNULL
;
1722 rr
->NR_AdditionalTo
= mDNSNULL
;
1725 if (m
->omsg
.h
.numAnswers
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, mDNSInterface_Any
, dest
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
1729 mDNSexport
void CompleteDeregistration(mDNS
*const m
, AuthRecord
*rr
)
1731 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() that
1732 // it should go ahead and immediately dispose of this registration
1733 rr
->resrec
.RecordType
= kDNSRecordTypeShared
;
1734 rr
->RequireGoodbye
= mDNSfalse
;
1735 if (rr
->LocalAnswer
) { AnswerLocalQuestions(m
, rr
, mDNSfalse
); rr
->LocalAnswer
= mDNSfalse
; }
1736 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
); // Don't touch rr after this
1739 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
1740 // the record list and/or question list.
1741 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1742 mDNSlocal
void DiscardDeregistrations(mDNS
*const m
)
1744 if (m
->CurrentRecord
)
1745 LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
1746 m
->CurrentRecord
= m
->ResourceRecords
;
1748 while (m
->CurrentRecord
)
1750 AuthRecord
*rr
= m
->CurrentRecord
;
1751 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
1752 CompleteDeregistration(m
, rr
); // Don't touch rr after this
1754 m
->CurrentRecord
= rr
->next
;
1758 mDNSlocal
void GrantUpdateCredit(AuthRecord
*rr
)
1760 if (++rr
->UpdateCredits
>= kMaxUpdateCredits
) rr
->NextUpdateCredit
= 0;
1761 else rr
->NextUpdateCredit
= NonZeroTime(rr
->NextUpdateCredit
+ kUpdateCreditRefreshInterval
);
1764 // Note about acceleration of announcements to facilitate automatic coalescing of
1765 // multiple independent threads of announcements into a single synchronized thread:
1766 // The announcements in the packet may be at different stages of maturity;
1767 // One-second interval, two-second interval, four-second interval, and so on.
1768 // After we've put in all the announcements that are due, we then consider
1769 // whether there are other nearly-due announcements that are worth accelerating.
1770 // To be eligible for acceleration, a record MUST NOT be older (further along
1771 // its timeline) than the most mature record we've already put in the packet.
1772 // In other words, younger records can have their timelines accelerated to catch up
1773 // with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
1774 // Older records cannot have their timelines accelerated; this would just widen
1775 // the gap between them and their younger bretheren and get them even more out of sync.
1777 // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
1778 // the record list and/or question list.
1779 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
1780 mDNSlocal
void SendResponses(mDNS
*const m
)
1783 AuthRecord
*rr
, *r2
;
1784 mDNSs32 maxExistingAnnounceInterval
= 0;
1785 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
1787 m
->NextScheduledResponse
= m
->timenow
+ 0x78000000;
1789 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1790 if (rr
->ImmedUnicast
)
1792 mDNSAddr v4
= { mDNSAddrType_IPv4
, {{{0}}} };
1793 mDNSAddr v6
= { mDNSAddrType_IPv6
, {{{0}}} };
1794 v4
.ip
.v4
= rr
->v4Requester
;
1795 v6
.ip
.v6
= rr
->v6Requester
;
1796 if (!mDNSIPv4AddressIsZero(rr
->v4Requester
)) SendDelayedUnicastResponse(m
, &v4
, rr
->ImmedAnswer
);
1797 if (!mDNSIPv6AddressIsZero(rr
->v6Requester
)) SendDelayedUnicastResponse(m
, &v6
, rr
->ImmedAnswer
);
1798 if (rr
->ImmedUnicast
)
1800 LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m
, rr
));
1801 rr
->ImmedUnicast
= mDNSfalse
;
1806 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
1809 // Run through our list of records, and decide which ones we're going to announce on all interfaces
1810 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1812 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
1813 if (TimeToAnnounceThisRecord(rr
, m
->timenow
) && ResourceRecordIsValidAnswer(rr
))
1815 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
1816 if (maxExistingAnnounceInterval
< rr
->ThisAPInterval
)
1817 maxExistingAnnounceInterval
= rr
->ThisAPInterval
;
1818 if (rr
->UpdateBlocked
) rr
->UpdateBlocked
= 0;
1822 // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
1823 // Eligible records that are more than half-way to their announcement time are accelerated
1824 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1825 if ((rr
->resrec
.InterfaceID
&& rr
->ImmedAnswer
) ||
1826 (rr
->ThisAPInterval
<= maxExistingAnnounceInterval
&&
1827 TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2) &&
1828 ResourceRecordIsValidAnswer(rr
)))
1829 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
1831 // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
1832 // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
1833 // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
1834 // then all that means is that it won't get sent -- which would not be the end of the world.
1835 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1837 if (rr
->ImmedAnswer
&& rr
->resrec
.rrtype
== kDNSType_SRV
)
1838 for (r2
=m
->ResourceRecords
; r2
; r2
=r2
->next
) // Scan list of resource records
1839 if (RRTypeIsAddressType(r2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
1840 ResourceRecordIsValidAnswer(r2
) && // ... which are valid for answer ...
1841 rr
->LastMCTime
- r2
->LastMCTime
>= 0 && // ... which we have not sent recently ...
1842 rr
->resrec
.rdatahash
== r2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
1843 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, r2
->resrec
.name
) &&
1844 (rr
->ImmedAnswer
== mDNSInterfaceMark
|| rr
->ImmedAnswer
== r2
->resrec
.InterfaceID
))
1845 r2
->ImmedAdditional
= r2
->resrec
.InterfaceID
; // ... then mark this address record for sending too
1846 // We also make sure we send the DeviceInfo TXT record too, if necessary
1847 // We check for RecordType == kDNSRecordTypeShared because we don't want to tag the
1848 // DeviceInfo TXT record onto a goodbye packet (RecordType == kDNSRecordTypeDeregistering).
1849 if (rr
->ImmedAnswer
&& rr
->resrec
.RecordType
== kDNSRecordTypeShared
&& rr
->resrec
.rrtype
== kDNSType_PTR
)
1850 if (ResourceRecordIsValidAnswer(&m
->DeviceInfo
) && SameDomainLabel(rr
->resrec
.rdata
->u
.name
.c
, m
->DeviceInfo
.resrec
.name
->c
))
1852 if (!m
->DeviceInfo
.ImmedAnswer
) m
->DeviceInfo
.ImmedAnswer
= rr
->ImmedAnswer
;
1853 else m
->DeviceInfo
.ImmedAnswer
= mDNSInterfaceMark
;
1857 // If there's a record which is supposed to be unique that we're going to send, then make sure that we give
1858 // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
1859 // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
1860 // record, then other RRSet members that have not been sent recently will get flushed out of client caches.
1861 // -- 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
1862 // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
1863 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1864 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1866 if (rr
->ImmedAnswer
) // If we're sending this as answer, see that its whole RRSet is similarly marked
1868 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
1869 if (ResourceRecordIsValidAnswer(r2
))
1870 if (r2
->ImmedAnswer
!= mDNSInterfaceMark
&&
1871 r2
->ImmedAnswer
!= rr
->ImmedAnswer
&& SameResourceRecordSignature(r2
, rr
))
1872 r2
->ImmedAnswer
= rr
->ImmedAnswer
;
1874 else if (rr
->ImmedAdditional
) // If we're sending this as additional, see that its whole RRSet is similarly marked
1876 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
1877 if (ResourceRecordIsValidAnswer(r2
))
1878 if (r2
->ImmedAdditional
!= rr
->ImmedAdditional
&& SameResourceRecordSignature(r2
, rr
))
1879 r2
->ImmedAdditional
= rr
->ImmedAdditional
;
1883 // Now set SendRNow state appropriately
1884 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1886 if (rr
->ImmedAnswer
== mDNSInterfaceMark
) // Sending this record on all appropriate interfaces
1888 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
1889 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional if sending as answer
1890 rr
->LastMCTime
= m
->timenow
;
1891 rr
->LastMCInterface
= rr
->ImmedAnswer
;
1892 // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
1893 if (TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2))
1895 rr
->AnnounceCount
--;
1896 rr
->ThisAPInterval
*= 2;
1897 rr
->LastAPTime
= m
->timenow
;
1898 debugf("Announcing %##s (%s) %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->AnnounceCount
);
1901 else if (rr
->ImmedAnswer
) // Else, just respond to a single query on single interface:
1903 rr
->SendRNow
= rr
->ImmedAnswer
; // Just respond on that interface
1904 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional too
1905 rr
->LastMCTime
= m
->timenow
;
1906 rr
->LastMCInterface
= rr
->ImmedAnswer
;
1908 SetNextAnnounceProbeTime(m
, rr
);
1909 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
1913 // *** 2. Loop through interface list, sending records as appropriate
1919 int numAnnounce
= 0;
1921 mDNSu8
*responseptr
= m
->omsg
.data
;
1923 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
1925 // First Pass. Look for:
1926 // 1. Deregistering records that need to send their goodbye packet
1927 // 2. Updated records that need to retract their old data
1928 // 3. Answers and announcements we need to send
1929 // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
1930 // send this packet and then try again.
1931 // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
1932 // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
1933 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1934 if (rr
->SendRNow
== intf
->InterfaceID
)
1936 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
1938 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
1939 if (newptr
) { responseptr
= newptr
; numDereg
++; }
1940 else if (m
->omsg
.h
.numAnswers
) break;
1942 else if (rr
->NewRData
&& !m
->SleepState
) // If we have new data for this record
1944 RData
*OldRData
= rr
->resrec
.rdata
;
1945 mDNSu16 oldrdlength
= rr
->resrec
.rdlength
;
1946 // See if we should send a courtesy "goodbye" for the old data before we replace it.
1947 if (ResourceRecordIsValidAnswer(rr
) && rr
->RequireGoodbye
)
1949 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
1950 if (newptr
) { responseptr
= newptr
; numDereg
++; rr
->RequireGoodbye
= mDNSfalse
; }
1951 else if (m
->omsg
.h
.numAnswers
) break;
1953 // Now try to see if we can fit the update in the same packet (not fatal if we can't)
1954 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
);
1955 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1956 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1957 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
1958 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1959 if (newptr
) { responseptr
= newptr
; rr
->RequireGoodbye
= mDNStrue
; }
1960 SetNewRData(&rr
->resrec
, OldRData
, oldrdlength
);
1964 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
1965 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
1966 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, m
->SleepState
? 0 : rr
->resrec
.rroriginalttl
);
1967 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
1970 responseptr
= newptr
;
1971 rr
->RequireGoodbye
= (mDNSu8
) (!m
->SleepState
);
1972 if (rr
->LastAPTime
== m
->timenow
) numAnnounce
++; else numAnswer
++;
1974 else if (m
->omsg
.h
.numAnswers
) break;
1976 // If sending on all interfaces, go to next interface; else we're finished now
1977 if (rr
->ImmedAnswer
== mDNSInterfaceMark
&& rr
->resrec
.InterfaceID
== mDNSInterface_Any
)
1978 rr
->SendRNow
= GetNextActiveInterfaceID(intf
);
1980 rr
->SendRNow
= mDNSNULL
;
1983 // Second Pass. Add additional records, if there's space.
1984 newptr
= responseptr
;
1985 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
1986 if (rr
->ImmedAdditional
== intf
->InterfaceID
)
1987 if (ResourceRecordIsValidAnswer(rr
))
1989 // If we have at least one answer already in the packet, then plan to add additionals too
1990 mDNSBool SendAdditional
= (m
->omsg
.h
.numAnswers
> 0);
1992 // If we're not planning to send any additionals, but this record is a unique one, then
1993 // make sure we haven't already sent any other members of its RRSet -- if we have, then they
1994 // will have had the cache flush bit set, so now we need to finish the job and send the rest.
1995 if (!SendAdditional
&& (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
))
1997 const AuthRecord
*a
;
1998 for (a
= m
->ResourceRecords
; a
; a
=a
->next
)
1999 if (a
->LastMCTime
== m
->timenow
&&
2000 a
->LastMCInterface
== intf
->InterfaceID
&&
2001 SameResourceRecordSignature(a
, rr
)) { SendAdditional
= mDNStrue
; break; }
2003 if (!SendAdditional
) // If we don't want to send this after all,
2004 rr
->ImmedAdditional
= mDNSNULL
; // then cancel its ImmedAdditional field
2005 else if (newptr
) // Else, try to add it if we can
2007 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2008 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
2009 newptr
= PutResourceRecord(&m
->omsg
, newptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
2010 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
2013 responseptr
= newptr
;
2014 rr
->ImmedAdditional
= mDNSNULL
;
2015 rr
->RequireGoodbye
= mDNStrue
;
2016 // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
2017 // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
2018 // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
2019 // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
2020 rr
->LastMCTime
= m
->timenow
;
2021 rr
->LastMCInterface
= intf
->InterfaceID
;
2026 if (m
->omsg
.h
.numAnswers
> 0 || m
->omsg
.h
.numAdditionals
)
2028 debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
2029 numDereg
, numDereg
== 1 ? "" : "s",
2030 numAnnounce
, numAnnounce
== 1 ? "" : "s",
2031 numAnswer
, numAnswer
== 1 ? "" : "s",
2032 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s", intf
->InterfaceID
);
2033 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
2034 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
2035 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
2036 if (++pktcount
>= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount
); break; }
2037 // There might be more things to send on this interface, so go around one more time and try again.
2039 else // Nothing more to send on this interface; go to next
2041 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
2042 #if MDNS_DEBUGMSGS && 0
2043 const char *const msg
= next
? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
2044 debugf(msg
, intf
, next
);
2051 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
2054 if (m
->CurrentRecord
)
2055 LogMsg("SendResponses ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
2056 m
->CurrentRecord
= m
->ResourceRecords
;
2057 while (m
->CurrentRecord
)
2059 rr
= m
->CurrentRecord
;
2060 m
->CurrentRecord
= rr
->next
;
2064 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
2065 LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m
, rr
));
2066 rr
->SendRNow
= mDNSNULL
;
2069 if (rr
->ImmedAnswer
)
2071 if (rr
->NewRData
) CompleteRDataUpdate(m
,rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
2073 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
2074 CompleteDeregistration(m
, rr
); // Don't touch rr after this
2077 rr
->ImmedAnswer
= mDNSNULL
;
2078 rr
->ImmedUnicast
= mDNSfalse
;
2079 rr
->v4Requester
= zerov4Addr
;
2080 rr
->v6Requester
= zerov6Addr
;
2084 verbosedebugf("SendResponses: Next in %ld ticks", m
->NextScheduledResponse
- m
->timenow
);
2087 // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
2088 // so we want to be lazy about how frequently we do it.
2089 // 1. If a cache record is currently referenced by *no* active questions,
2090 // then we don't mind expiring it up to a minute late (who will know?)
2091 // 2. Else, if a cache record is due for some of its final expiration queries,
2092 // we'll allow them to be late by up to 2% of the TTL
2093 // 3. Else, if a cache record has completed all its final expiration queries without success,
2094 // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
2095 // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
2096 // so allow at most 1/10 second lateness
2097 #define CacheCheckGracePeriod(RR) ( \
2098 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
2099 ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \
2100 ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \
2101 ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10))
2103 // Note: MUST call SetNextCacheCheckTime any time we change:
2105 // rr->resrec.rroriginalttl
2106 // rr->UnansweredQueries
2107 // rr->CRActiveQuestion
2108 // Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary
2109 // Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime
2110 mDNSlocal
void SetNextCacheCheckTime(mDNS
*const m
, CacheRecord
*const rr
)
2112 rr
->NextRequiredQuery
= RRExpireTime(rr
);
2114 // If we have an active question, then see if we want to schedule a refresher query for this record.
2115 // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
2116 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
2118 rr
->NextRequiredQuery
-= TicksTTL(rr
)/20 * (MaxUnansweredQueries
- rr
->UnansweredQueries
);
2119 rr
->NextRequiredQuery
+= mDNSRandom((mDNSu32
)TicksTTL(rr
)/50);
2120 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks",
2121 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
),
2122 (rr
->NextRequiredQuery
- m
->timenow
) / mDNSPlatformOneSecond
, CacheCheckGracePeriod(rr
));
2125 if (m
->NextCacheCheck
- (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
)) > 0)
2126 m
->NextCacheCheck
= (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
));
2128 if (rr
->DelayDelivery
)
2129 if (m
->NextCacheCheck
- rr
->DelayDelivery
> 0)
2130 m
->NextCacheCheck
= rr
->DelayDelivery
;
2133 #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
2134 #define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5)
2135 #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 5)
2136 #define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30)
2138 mDNSlocal mStatus
mDNS_Reconfirm_internal(mDNS
*const m
, CacheRecord
*const rr
, mDNSu32 interval
)
2140 if (interval
< kMinimumReconfirmTime
)
2141 interval
= kMinimumReconfirmTime
;
2142 if (interval
> 0x10000000) // Make sure interval doesn't overflow when we multiply by four below
2143 interval
= 0x10000000;
2145 // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
2146 if (RRExpireTime(rr
) - m
->timenow
> (mDNSs32
)((interval
* 4) / 3))
2148 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
2149 // For all the reconfirmations in a given batch, we want to use the same random value
2150 // so that the reconfirmation questions can be grouped into a single query packet
2151 if (!m
->RandomReconfirmDelay
) m
->RandomReconfirmDelay
= 1 + mDNSRandom(0x3FFFFFFF);
2152 interval
+= mDNSRandomFromFixedSeed(m
->RandomReconfirmDelay
, interval
/3);
2153 rr
->TimeRcvd
= m
->timenow
- (mDNSs32
)interval
* 3;
2154 rr
->resrec
.rroriginalttl
= (interval
* 4 + mDNSPlatformOneSecond
- 1) / mDNSPlatformOneSecond
;
2155 SetNextCacheCheckTime(m
, rr
);
2157 debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s %p",
2158 RRExpireTime(rr
) - m
->timenow
, CRDisplayString(m
, rr
), rr
->CRActiveQuestion
);
2159 return(mStatus_NoError
);
2162 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
2164 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
2165 // It also appends to the list of known answer records that need to be included,
2166 // and updates the forcast for the size of the known answer section.
2167 mDNSlocal mDNSBool
BuildQuestion(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
, DNSQuestion
*q
,
2168 CacheRecord
***kalistptrptr
, mDNSu32
*answerforecast
)
2170 mDNSBool ucast
= (q
->LargeAnswers
|| q
->RequestUnicast
) && m
->CanReceiveUnicastOn5353
;
2171 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
2172 const mDNSu8
*const limit
= query
->data
+ NormalMaxDNSMessageData
;
2173 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &q
->qname
, q
->qtype
, (mDNSu16
)(q
->qclass
| ucbit
));
2176 debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
2179 else if (newptr
+ *answerforecast
>= limit
)
2181 verbosedebugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
2182 q
->qname
.c
, DNSTypeName(q
->qtype
), newptr
+ *answerforecast
- query
->data
);
2183 query
->h
.numQuestions
--;
2188 mDNSu32 forecast
= *answerforecast
;
2189 const mDNSu32 slot
= HashSlot(&q
->qname
);
2190 const CacheGroup
*const cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
2192 CacheRecord
**ka
= *kalistptrptr
; // Make a working copy of the pointer we're going to update
2194 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
2195 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
2196 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not already in the known answer list
2197 rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
2198 SameNameRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
2199 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
> // and its half-way-to-expiry time is at least 1 second away
2200 mDNSPlatformOneSecond
) // (also ensures we never include goodbye records with TTL=1)
2202 *ka
= rr
; // Link this record into our known answer chain
2203 ka
= &rr
->NextInKAList
;
2204 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
2205 forecast
+= 12 + rr
->resrec
.rdestimate
;
2206 // If we're trying to put more than one question in this packet, and it doesn't fit
2207 // then undo that last question and try again next time
2208 if (query
->h
.numQuestions
> 1 && newptr
+ forecast
>= limit
)
2210 debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
2211 q
->qname
.c
, DNSTypeName(q
->qtype
), newptr
+ forecast
- query
->data
);
2212 query
->h
.numQuestions
--;
2213 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
2214 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
2215 return(mDNSfalse
); // Return false, so we'll try again in the next packet
2219 // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
2220 *queryptr
= newptr
; // Update the packet pointer
2221 *answerforecast
= forecast
; // Update the forecast
2222 *kalistptrptr
= ka
; // Update the known answer list pointer
2223 if (ucast
) q
->ExpectUnicastResp
= NonZeroTime(m
->timenow
);
2225 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // For every resource record in our cache,
2226 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
2227 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not in the known answer list
2228 SameNameRecordAnswersQuestion(&rr
->resrec
, q
)) // which answers our question
2230 rr
->UnansweredQueries
++; // indicate that we're expecting a response
2231 rr
->LastUnansweredTime
= m
->timenow
;
2232 SetNextCacheCheckTime(m
, rr
);
2239 // When we have a query looking for a specified name, but there appear to be no answers with
2240 // that name, ReconfirmAntecedents() is called with depth=0 to start the reconfirmation process
2241 // for any records in our cache that reference the given name (e.g. PTR and SRV records).
2242 // For any such cache record we find, we also recursively call ReconfirmAntecedents() for *its* name.
2243 // We increment depth each time we recurse, to guard against possible infinite loops, with a limit of 5.
2244 // A typical reconfirmation scenario might go like this:
2245 // Depth 0: Name "myhost.local" has no address records
2246 // Depth 1: SRV "My Service._example._tcp.local." refers to "myhost.local"; may be stale
2247 // Depth 2: PTR "_example._tcp.local." refers to "My Service"; may be stale
2248 // Depth 3: PTR "_services._dns-sd._udp.local." refers to "_example._tcp.local."; may be stale
2249 // Currently depths 4 and 5 are not expected to occur; if we did get to depth 5 we'd reconfim any records we
2250 // found referring to the given name, but not recursively descend any further reconfirm *their* antecedents.
2251 mDNSlocal
void ReconfirmAntecedents(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const int depth
)
2256 debugf("ReconfirmAntecedents (depth=%d) for %##s", depth
, name
->c
);
2257 FORALL_CACHERECORDS(slot
, cg
, cr
)
2259 domainname
*crtarget
= GetRRDomainNameTarget(&cr
->resrec
);
2260 if (crtarget
&& cr
->resrec
.rdatahash
== namehash
&& SameDomainName(crtarget
, name
))
2262 LogOperation("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth
, CRDisplayString(m
, cr
));
2263 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
2264 if (depth
< 5) ReconfirmAntecedents(m
, cr
->resrec
.name
, cr
->resrec
.namehash
, depth
+1);
2269 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
2270 mDNSlocal
void ExpireDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
)
2273 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
2276 mDNSlocal
void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
, mDNSInterfaceID InterfaceID
)
2279 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
2282 mDNSlocal mDNSBool
SuppressOnThisInterface(const DupSuppressInfo ds
[DupSuppressInfoSize
], const NetworkInterfaceInfo
* const intf
)
2285 mDNSBool v4
= !intf
->IPv4Available
; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
2286 mDNSBool v6
= !intf
->IPv6Available
; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
2287 for (i
=0; i
<DupSuppressInfoSize
; i
++)
2288 if (ds
[i
].InterfaceID
== intf
->InterfaceID
)
2290 if (ds
[i
].Type
== mDNSAddrType_IPv4
) v4
= mDNStrue
;
2291 else if (ds
[i
].Type
== mDNSAddrType_IPv6
) v6
= mDNStrue
;
2292 if (v4
&& v6
) return(mDNStrue
);
2297 mDNSlocal
int RecordDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 Time
, mDNSInterfaceID InterfaceID
, mDNSs32 Type
)
2301 // See if we have this one in our list somewhere already
2302 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Type
== Type
) break;
2304 // If not, find a slot we can re-use
2305 if (i
>= DupSuppressInfoSize
)
2308 for (j
=1; j
<DupSuppressInfoSize
&& ds
[i
].InterfaceID
; j
++)
2309 if (!ds
[j
].InterfaceID
|| ds
[j
].Time
- ds
[i
].Time
< 0)
2313 // Record the info about this query we saw
2315 ds
[i
].InterfaceID
= InterfaceID
;
2321 mDNSlocal mDNSBool
AccelerateThisQuery(mDNS
*const m
, DNSQuestion
*q
)
2323 // If more than 90% of the way to the query time, we should unconditionally accelerate it
2324 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/10))
2327 // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
2328 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/2))
2330 // We forecast: qname (n) type (2) class (2)
2331 mDNSu32 forecast
= (mDNSu32
)DomainNameLength(&q
->qname
) + 4;
2332 const mDNSu32 slot
= HashSlot(&q
->qname
);
2333 const CacheGroup
*const cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
2335 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
2336 if (rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
2337 SameNameRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
2338 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
>= 0 && // and it is less than half-way to expiry
2339 rr
->NextRequiredQuery
- (m
->timenow
+ q
->ThisQInterval
) > 0)// and we'll ask at least once again before NextRequiredQuery
2341 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
2342 forecast
+= 12 + rr
->resrec
.rdestimate
;
2343 if (forecast
>= 512) return(mDNSfalse
); // If this would add 512 bytes or more to the packet, don't accelerate
2351 // How Standard Queries are generated:
2352 // 1. The Question Section contains the question
2353 // 2. The Additional Section contains answers we already know, to suppress duplicate responses
2355 // How Probe Queries are generated:
2356 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
2357 // if some other host is already using *any* records with this name, we want to know about it.
2358 // 2. The Authority Section contains the proposed values we intend to use for one or more
2359 // of our records with that name (analogous to the Update section of DNS Update packets)
2360 // because if some other host is probing at the same time, we each want to know what the other is
2361 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
2363 mDNSlocal
void SendQueries(mDNS
*const m
)
2371 // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
2372 mDNSs32 maxExistingQuestionInterval
= 0;
2373 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
2374 CacheRecord
*KnownAnswerList
= mDNSNULL
;
2376 // 1. If time for a query, work out what we need to do
2377 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
2381 // We're expecting to send a query anyway, so see if any expiring cache records are close enough
2382 // to their NextRequiredQuery to be worth batching them together with this one
2383 FORALL_CACHERECORDS(slot
, cg
, rr
)
2384 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
2385 if (m
->timenow
+ TicksTTL(rr
)/50 - rr
->NextRequiredQuery
>= 0)
2387 LogOperation("Sending %d%% cache expiration query for %s", 80 + 5 * rr
->UnansweredQueries
, CRDisplayString(m
, rr
));
2388 q
= rr
->CRActiveQuestion
;
2389 ExpireDupSuppressInfoOnInterface(q
->DupSuppress
, m
->timenow
- TicksTTL(rr
)/20, rr
->resrec
.InterfaceID
);
2390 // For uDNS queries (TargetQID non-zero) we adjust LastQTime,
2391 // and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly
2392 if (q
->Target
.type
) q
->SendQNow
= mDNSInterfaceMark
; // If targeted query, mark it
2393 else if (!mDNSOpaque16IsZero(q
->TargetQID
)) { q
->LastQTime
= m
->timenow
- q
->ThisQInterval
; rr
->UnansweredQueries
++; }
2394 else if (q
->SendQNow
== mDNSNULL
) q
->SendQNow
= rr
->resrec
.InterfaceID
;
2395 else if (q
->SendQNow
!= rr
->resrec
.InterfaceID
) q
->SendQNow
= mDNSInterfaceMark
;
2398 if (m
->SuppressStdPort53Queries
&& m
->timenow
- m
->SuppressStdPort53Queries
>= 0)
2399 m
->SuppressStdPort53Queries
= 0; // If suppression time has passed, clear it
2401 // Scan our list of questions to see which:
2402 // *WideArea* queries need to be sent
2403 // *unicast* queries need to be sent
2404 // *multicast* queries we're definitely going to send
2405 if (m
->CurrentQuestion
)
2406 LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2407 m
->CurrentQuestion
= m
->Questions
;
2408 while (m
->CurrentQuestion
)
2410 q
= m
->CurrentQuestion
;
2411 if (ActiveQuestion(q
) && !mDNSOpaque16IsZero(q
->TargetQID
)) uDNS_CheckCurrentQuestion(m
);
2412 else if (mDNSOpaque16IsZero(q
->TargetQID
) && q
->Target
.type
&& (q
->SendQNow
|| TimeToSendThisQuestion(q
, m
->timenow
)))
2414 mDNSu8
*qptr
= m
->omsg
.data
;
2415 const mDNSu8
*const limit
= m
->omsg
.data
+ sizeof(m
->omsg
.data
);
2416 InitializeDNSMessage(&m
->omsg
.h
, q
->TargetQID
, QueryFlags
);
2417 qptr
= putQuestion(&m
->omsg
, qptr
, limit
, &q
->qname
, q
->qtype
, q
->qclass
);
2418 mDNSSendDNSMessage(m
, &m
->omsg
, qptr
, mDNSInterface_Any
, &q
->Target
, q
->TargetPort
, mDNSNULL
, mDNSNULL
);
2419 q
->ThisQInterval
*= QuestionIntervalStep
;
2420 if (q
->ThisQInterval
> MaxQuestionInterval
)
2421 q
->ThisQInterval
= MaxQuestionInterval
;
2422 q
->LastQTime
= m
->timenow
;
2423 q
->LastQTxTime
= m
->timenow
;
2424 q
->RecentAnswerPkts
= 0;
2425 q
->SendQNow
= mDNSNULL
;
2426 q
->ExpectUnicastResp
= NonZeroTime(m
->timenow
);
2428 else if (mDNSOpaque16IsZero(q
->TargetQID
) && !q
->Target
.type
&& TimeToSendThisQuestion(q
, m
->timenow
))
2430 //LogOperation("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - (q->LastQTime + q->ThisQInterval));
2431 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
2432 if (maxExistingQuestionInterval
< q
->ThisQInterval
)
2433 maxExistingQuestionInterval
= q
->ThisQInterval
;
2435 // If m->CurrentQuestion wasn't modified out from under us, advance it now
2436 // We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having
2437 // m->CurrentQuestion point to the right question
2438 if (q
== m
->CurrentQuestion
) m
->CurrentQuestion
= m
->CurrentQuestion
->next
;
2441 // Scan our list of questions
2442 // (a) to see if there are any more that are worth accelerating, and
2443 // (b) to update the state variables for *all* the questions we're going to send
2444 // Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list,
2445 // which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very
2446 // 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.
2447 m
->NextScheduledQuery
= m
->timenow
+ 0x78000000;
2448 for (q
= m
->Questions
; q
; q
=q
->next
)
2450 if (mDNSOpaque16IsZero(q
->TargetQID
) && (q
->SendQNow
||
2451 (!q
->Target
.type
&& ActiveQuestion(q
) && q
->ThisQInterval
<= maxExistingQuestionInterval
&& AccelerateThisQuery(m
,q
))))
2453 // If at least halfway to next query time, advance to next interval
2454 // If less than halfway to next query time, then
2455 // treat this as logically a repeat of the last transmission, without advancing the interval
2456 if (m
->timenow
- (q
->LastQTime
+ q
->ThisQInterval
/2) >= 0)
2458 //LogOperation("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - (q->LastQTime + q->ThisQInterval));
2459 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
2460 q
->ThisQInterval
*= QuestionIntervalStep
;
2461 if (q
->ThisQInterval
> MaxQuestionInterval
)
2462 q
->ThisQInterval
= MaxQuestionInterval
;
2463 else if (q
->CurrentAnswers
== 0 && q
->ThisQInterval
== InitialQuestionInterval
* QuestionIntervalStep2
)
2465 // Generally don't need to log this.
2466 // It's not especially noteworthy if a query finds no results -- this usually happens for domain
2467 // enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa")
2468 // and when there simply happen to be no instances of the service the client is looking
2469 // for (e.g. iTunes is set to look for RAOP devices, and the current network has none).
2470 debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents",
2471 q
->qname
.c
, DNSTypeName(q
->qtype
));
2472 // Sending third query, and no answers yet; time to begin doubting the source
2473 ReconfirmAntecedents(m
, &q
->qname
, q
->qnamehash
, 0);
2477 // Mark for sending. (If no active interfaces, then don't even try.)
2478 q
->SendOnAll
= (q
->SendQNow
== mDNSInterfaceMark
);
2481 q
->SendQNow
= !intf
? mDNSNULL
: (q
->InterfaceID
) ? q
->InterfaceID
: intf
->InterfaceID
;
2482 q
->LastQTime
= m
->timenow
;
2485 // If we recorded a duplicate suppression for this question less than half an interval ago,
2486 // then we consider it recent enough that we don't need to do an identical query ourselves.
2487 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
- q
->ThisQInterval
/2);
2489 q
->LastQTxTime
= m
->timenow
;
2490 q
->RecentAnswerPkts
= 0;
2491 if (q
->RequestUnicast
) q
->RequestUnicast
--;
2493 // For all questions (not just the ones we're sending) check what the next scheduled event will be
2494 SetNextQueryTime(m
,q
);
2498 // 2. Scan our authoritative RR list to see what probes we might need to send
2499 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
2501 m
->NextScheduledProbe
= m
->timenow
+ 0x78000000;
2503 if (m
->CurrentRecord
)
2504 LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
2505 m
->CurrentRecord
= m
->ResourceRecords
;
2506 while (m
->CurrentRecord
)
2508 AuthRecord
*rr
= m
->CurrentRecord
;
2509 m
->CurrentRecord
= rr
->next
;
2510 if (!AuthRecord_uDNS(rr
) && rr
->resrec
.RecordType
== kDNSRecordTypeUnique
) // For all records that are still probing...
2512 // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
2513 if (m
->timenow
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) < 0)
2515 SetNextAnnounceProbeTime(m
, rr
);
2517 // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
2518 else if (rr
->ProbeCount
)
2520 // Mark for sending. (If no active interfaces, then don't even try.)
2521 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
2522 rr
->LastAPTime
= m
->timenow
;
2524 SetNextAnnounceProbeTime(m
, rr
);
2525 if (rr
->ProbeCount
== 0)
2527 // If this is the last probe for this record, then see if we have any matching records
2528 // on our duplicate list which should similarly have their ProbeCount cleared to zero...
2530 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
)
2531 if (r2
->resrec
.RecordType
== kDNSRecordTypeUnique
&& RecordIsLocalDuplicate(r2
, rr
))
2533 // ... then acknowledge this record to the client.
2534 // We do this optimistically, just as we're about to send the third probe.
2535 // This helps clients that both advertise and browse, and want to filter themselves
2536 // from the browse results list, because it helps ensure that the registration
2537 // confirmation will be delivered 1/4 second *before* the browse "add" event.
2538 // A potential downside is that we could deliver a registration confirmation and then find out
2539 // moments later that there's a name conflict, but applications have to be prepared to handle
2540 // late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new.
2541 if (!rr
->Acknowledged
) AcknowledgeRecord(m
, rr
);
2544 // else, if it has now finished probing, move it to state Verified,
2545 // and update m->NextScheduledResponse so it will be announced
2548 if (!rr
->Acknowledged
) AcknowledgeRecord(m
, rr
); // Defensive, just in case it got missed somehow
2549 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
2550 rr
->ThisAPInterval
= DefaultAnnounceIntervalForTypeUnique
;
2551 rr
->LastAPTime
= m
->timenow
- DefaultAnnounceIntervalForTypeUnique
;
2552 SetNextAnnounceProbeTime(m
, rr
);
2556 m
->CurrentRecord
= m
->DuplicateRecords
;
2557 while (m
->CurrentRecord
)
2559 AuthRecord
*rr
= m
->CurrentRecord
;
2560 m
->CurrentRecord
= rr
->next
;
2561 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& rr
->ProbeCount
== 0 && !rr
->Acknowledged
)
2562 AcknowledgeRecord(m
, rr
);
2566 // 3. Now we know which queries and probes we're sending,
2567 // go through our interface list sending the appropriate queries on each interface
2571 mDNSu8
*queryptr
= m
->omsg
.data
;
2572 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, QueryFlags
);
2573 if (KnownAnswerList
) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet");
2574 if (!KnownAnswerList
)
2576 // Start a new known-answer list
2577 CacheRecord
**kalistptr
= &KnownAnswerList
;
2578 mDNSu32 answerforecast
= 0;
2580 // Put query questions in this packet
2581 for (q
= m
->Questions
; q
; q
=q
->next
)
2583 if (mDNSOpaque16IsZero(q
->TargetQID
) && (q
->SendQNow
== intf
->InterfaceID
))
2585 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
2586 SuppressOnThisInterface(q
->DupSuppress
, intf
) ? "Suppressing" : "Putting ",
2587 q
->qname
.c
, DNSTypeName(q
->qtype
), queryptr
- m
->omsg
.data
, queryptr
+ answerforecast
- m
->omsg
.data
);
2588 // If we're suppressing this question, or we successfully put it, update its SendQNow state
2589 if (SuppressOnThisInterface(q
->DupSuppress
, intf
) ||
2590 BuildQuestion(m
, &m
->omsg
, &queryptr
, q
, &kalistptr
, &answerforecast
))
2591 q
->SendQNow
= (q
->InterfaceID
|| !q
->SendOnAll
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
2595 // Put probe questions in this packet
2596 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2597 if (rr
->SendRNow
== intf
->InterfaceID
)
2599 mDNSBool ucast
= (rr
->ProbeCount
>= DefaultProbeCountForTypeUnique
-1) && m
->CanReceiveUnicastOn5353
;
2600 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
2601 const mDNSu8
*const limit
= m
->omsg
.data
+ ((m
->omsg
.h
.numQuestions
) ? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
2602 mDNSu8
*newptr
= putQuestion(&m
->omsg
, queryptr
, limit
, rr
->resrec
.name
, kDNSQType_ANY
, (mDNSu16
)(rr
->resrec
.rrclass
| ucbit
));
2603 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
2604 mDNSu32 forecast
= answerforecast
+ 12 + rr
->resrec
.rdestimate
;
2605 if (newptr
&& newptr
+ forecast
< limit
)
2608 answerforecast
= forecast
;
2609 rr
->SendRNow
= (rr
->resrec
.InterfaceID
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
2610 rr
->IncludeInProbe
= mDNStrue
;
2611 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d",
2612 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->ProbeCount
);
2616 verbosedebugf("SendQueries: Retracting Question %##s (%s)",
2617 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2618 m
->omsg
.h
.numQuestions
--;
2623 // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
2624 while (KnownAnswerList
)
2626 CacheRecord
*rr
= KnownAnswerList
;
2627 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
2628 mDNSu8
*newptr
= PutResourceRecordTTL(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, rr
->resrec
.rroriginalttl
- SecsSinceRcvd
);
2631 verbosedebugf("SendQueries: Put %##s (%s) at %d - %d",
2632 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), queryptr
- m
->omsg
.data
, newptr
- m
->omsg
.data
);
2634 KnownAnswerList
= rr
->NextInKAList
;
2635 rr
->NextInKAList
= mDNSNULL
;
2639 // If we ran out of space and we have more than one question in the packet, that's an error --
2640 // we shouldn't have put more than one question if there was a risk of us running out of space.
2641 if (m
->omsg
.h
.numQuestions
> 1)
2642 LogMsg("SendQueries: Put %d answers; No more space for known answers", m
->omsg
.h
.numAnswers
);
2643 m
->omsg
.h
.flags
.b
[0] |= kDNSFlag0_TC
;
2648 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2649 if (rr
->IncludeInProbe
)
2651 mDNSu8
*newptr
= PutResourceRecord(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAuthorities
, &rr
->resrec
);
2652 rr
->IncludeInProbe
= mDNSfalse
;
2653 if (newptr
) queryptr
= newptr
;
2654 else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?",
2655 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2658 if (queryptr
> m
->omsg
.data
)
2660 if ((m
->omsg
.h
.flags
.b
[0] & kDNSFlag0_TC
) && m
->omsg
.h
.numQuestions
> 1)
2661 LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m
->omsg
.h
.numQuestions
);
2662 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
2663 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
2664 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
2665 m
->omsg
.h
.numAuthorities
, m
->omsg
.h
.numAuthorities
== 1 ? "" : "s", intf
->InterfaceID
);
2666 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
2667 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, mDNSNULL
, mDNSNULL
);
2668 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
2669 if (++pktcount
>= 1000)
2670 { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount
); break; }
2671 // There might be more records left in the known answer list, or more questions to send
2672 // on this interface, so go around one more time and try again.
2674 else // Nothing more to send on this interface; go to next
2676 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
2677 #if MDNS_DEBUGMSGS && 0
2678 const char *const msg
= next
? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p";
2679 debugf(msg
, intf
, next
);
2685 // 4. Final housekeeping
2687 // 4a. Debugging check: Make sure we announced all our records
2688 for (ar
= m
->ResourceRecords
; ar
; ar
=ar
->next
)
2691 if (ar
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
2692 LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m
, ar
));
2693 ar
->SendRNow
= mDNSNULL
;
2696 // 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope
2697 // that their interface which went away might come back again, the logic will want to send queries
2698 // for those records, but we can't because their interface isn't here any more, so to keep the
2699 // state machine ticking over we just pretend we did so.
2700 // If the interface does not come back in time, the cache record will expire naturally
2701 FORALL_CACHERECORDS(slot
, cg
, cr
)
2702 if (cr
->CRActiveQuestion
&& cr
->UnansweredQueries
< MaxUnansweredQueries
&& m
->timenow
- cr
->NextRequiredQuery
>= 0)
2704 cr
->UnansweredQueries
++;
2705 cr
->CRActiveQuestion
->SendQNow
= mDNSNULL
;
2706 SetNextCacheCheckTime(m
, cr
);
2709 // 4c. Debugging check: Make sure we sent all our planned questions
2710 // Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions
2711 // we legitimately couldn't send because the interface is no longer available
2712 for (q
= m
->Questions
; q
; q
=q
->next
)
2715 LogMsg("SendQueries: No active interface to send: %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
2716 q
->SendQNow
= mDNSNULL
;
2720 // ***************************************************************************
2721 #if COMPILER_LIKES_PRAGMA_MARK
2723 #pragma mark - RR List Management & Task Management
2726 // NOTE: AnswerCurrentQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
2727 // Any code walking either list must use the m->CurrentQuestion (and possibly m->CurrentRecord) mechanism to protect against this.
2728 // In fact, to enforce this, the routine will *only* answer the question currently pointed to by m->CurrentQuestion,
2729 // which will be auto-advanced (possibly to NULL) if the client callback cancels the question.
2730 mDNSexport
void AnswerCurrentQuestionWithResourceRecord(mDNS
*const m
, CacheRecord
*const rr
, const QC_result AddRecord
)
2732 DNSQuestion
*const q
= m
->CurrentQuestion
;
2733 mDNSBool followcname
= rr
->resrec
.RecordType
!= kDNSRecordTypePacketNegative
&& AddRecord
&&
2734 rr
->resrec
.rrtype
== kDNSType_CNAME
&& q
->qtype
!= kDNSType_CNAME
;
2735 verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s", q
->CurrentAnswers
, AddRecord
? "Add" : "Rmv", rr
->resrec
.rroriginalttl
, CRDisplayString(m
, rr
));
2737 // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue)
2738 // may be called twice, once when the record is received, and again when it's time to notify local clients.
2739 // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
2741 rr
->LastUsed
= m
->timenow
;
2742 if (AddRecord
== QC_add
&& !q
->DuplicateOf
&& rr
->CRActiveQuestion
!= q
)
2744 if (!rr
->CRActiveQuestion
) m
->rrcache_active
++; // If not previously active, increment rrcache_active count
2745 debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion to %p for cache record %s", q
, CRDisplayString(m
,rr
));
2746 rr
->CRActiveQuestion
= q
; // We know q is non-null
2747 SetNextCacheCheckTime(m
, rr
);
2751 // (a) a no-cache add, where we've already done at least one 'QM' query, or
2752 // (b) a normal add, where we have at least one unique-type answer,
2753 // then there's no need to keep polling the network.
2754 // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
2755 // We do this for mDNS questions and uDNS one-shot questions, but not for
2756 // uDNS LongLived questions, because that would mess up our LLQ lease renewal timing.
2757 if ((AddRecord
== QC_addnocache
&& !q
->RequestUnicast
) ||
2758 (AddRecord
== QC_add
&& (q
->ExpectUnique
|| (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))))
2759 if (ActiveQuestion(q
) && (mDNSOpaque16IsZero(q
->TargetQID
) || !q
->LongLived
))
2761 q
->LastQTime
= m
->timenow
;
2762 q
->LastQTxTime
= m
->timenow
;
2763 q
->RecentAnswerPkts
= 0;
2764 q
->ThisQInterval
= MaxQuestionInterval
;
2765 q
->RequestUnicast
= mDNSfalse
;
2768 if (rr
->DelayDelivery
) return; // We'll come back later when CacheRecordDeferredAdd() calls us
2770 // Only deliver negative answers if client has explicitly requested them
2771 if (rr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
&& (!AddRecord
|| !q
->ReturnIntermed
)) return;
2773 // For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that
2774 if (q
->QuestionCallback
&& !q
->NoAnswer
&& (!followcname
|| q
->ReturnIntermed
))
2776 mDNS_DropLockBeforeCallback(); // Allow client (and us) to legally make mDNS API calls
2777 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
2778 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
2780 // NOTE: Proceed with caution here because client callback function is allowed to do anything,
2781 // including starting/stopping queries, registering/deregistering records, etc.
2783 if (followcname
&& m
->CurrentQuestion
== q
&& q
->CNAMEReferrals
< 10)
2785 const mDNSu32 c
= q
->CNAMEReferrals
+ 1;
2786 // Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
2787 // and track CNAMEs coming and going, we should really create a subbordinate query here,
2788 // which we would subsequently cancel and retract if the CNAME referral record were removed.
2789 // In reality this is such a corner case we'll ignore it until someone actually needs it.
2790 LogOperation("AnswerCurrentQuestionWithResourceRecord: following CNAME referral for %s", CRDisplayString(m
, rr
));
2791 mDNS_StopQuery_internal(m
, q
); // Stop old query
2792 AssignDomainName(&q
->qname
, &rr
->resrec
.rdata
->u
.name
); // Update qname
2793 q
->qnamehash
= DomainNameHashValue(&q
->qname
); // and namehash
2794 mDNS_StartQuery_internal(m
, q
); // start new query
2795 q
->CNAMEReferrals
= c
; // and keep count of how many times we've done this
2799 mDNSlocal
void CacheRecordDeferredAdd(mDNS
*const m
, CacheRecord
*rr
)
2801 rr
->DelayDelivery
= 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
2802 if (m
->CurrentQuestion
)
2803 LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2804 m
->CurrentQuestion
= m
->Questions
;
2805 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2807 DNSQuestion
*q
= m
->CurrentQuestion
;
2808 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2809 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_add
);
2810 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2811 m
->CurrentQuestion
= q
->next
;
2813 m
->CurrentQuestion
= mDNSNULL
;
2816 mDNSlocal mDNSs32
CheckForSoonToExpireRecords(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const mDNSu32 slot
)
2818 const mDNSs32 threshhold
= m
->timenow
+ mDNSPlatformOneSecond
; // See if there are any records expiring within one second
2819 const mDNSs32 start
= m
->timenow
- 0x10000000;
2820 mDNSs32 delay
= start
;
2821 CacheGroup
*cg
= CacheGroupForName(m
, slot
, namehash
, name
);
2823 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
2824 if (threshhold
- RRExpireTime(rr
) >= 0) // If we have records about to expire within a second
2825 if (delay
- RRExpireTime(rr
) < 0) // then delay until after they've been deleted
2826 delay
= RRExpireTime(rr
);
2827 if (delay
- start
> 0) return(NonZeroTime(delay
));
2831 // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
2832 // If new questions are created as a result of invoking client callbacks, they will be added to
2833 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2834 // rr is a new CacheRecord just received into our cache
2835 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
2836 // NOTE: CacheRecordAdd calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
2837 // which may change the record list and/or question list.
2838 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2839 mDNSlocal
void CacheRecordAdd(mDNS
*const m
, CacheRecord
*rr
)
2842 for (q
= m
->Questions
; q
; q
=q
->next
)
2844 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2846 // If this question is one that's actively sending queries, and it's received ten answers within one
2847 // second of sending the last query packet, then that indicates some radical network topology change,
2848 // so reset its exponential backoff back to the start. We must be at least at the eight-second interval
2849 // to do this. If we're at the four-second interval, or less, there's not much benefit accelerating
2850 // because we will anyway send another query within a few seconds. The first reset query is sent out
2851 // randomized over the next four seconds to reduce possible synchronization between machines.
2852 if (q
->LastAnswerPktNum
!= m
->PktNum
)
2854 q
->LastAnswerPktNum
= m
->PktNum
;
2855 if (mDNSOpaque16IsZero(q
->TargetQID
) && ActiveQuestion(q
) && ++q
->RecentAnswerPkts
>= 10 &&
2856 q
->ThisQInterval
> InitialQuestionInterval
* QuestionIntervalStep3
&& m
->timenow
- q
->LastQTxTime
< mDNSPlatformOneSecond
)
2858 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
2859 q
->qname
.c
, DNSTypeName(q
->qtype
));
2860 q
->LastQTime
= m
->timenow
- InitialQuestionInterval
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*4);
2861 q
->ThisQInterval
= InitialQuestionInterval
;
2862 SetNextQueryTime(m
,q
);
2865 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu",
2866 rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->resrec
.rroriginalttl
);
2867 q
->CurrentAnswers
++;
2868 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
2869 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
2870 if (q
->CurrentAnswers
> 4000)
2872 static int msgcount
= 0;
2873 if (msgcount
++ < 10)
2874 LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
2875 q
->qname
.c
, DNSTypeName(q
->qtype
), q
->CurrentAnswers
);
2876 rr
->resrec
.rroriginalttl
= 0;
2877 rr
->UnansweredQueries
= MaxUnansweredQueries
;
2882 if (!rr
->DelayDelivery
)
2884 if (m
->CurrentQuestion
)
2885 LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2886 m
->CurrentQuestion
= m
->Questions
;
2887 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2889 DNSQuestion
*q
= m
->CurrentQuestion
;
2890 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2891 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_add
);
2892 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2893 m
->CurrentQuestion
= q
->next
;
2895 m
->CurrentQuestion
= mDNSNULL
;
2898 SetNextCacheCheckTime(m
, rr
);
2901 // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
2902 // If new questions are created as a result of invoking client callbacks, they will be added to
2903 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2904 // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
2905 // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
2906 // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
2907 // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
2908 // NOTE: NoCacheAnswer calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
2909 // which may change the record list and/or question list.
2910 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2911 mDNSlocal
void NoCacheAnswer(mDNS
*const m
, CacheRecord
*rr
)
2913 LogMsg("No cache space: Delivering non-cached result for %##s", m
->rec
.r
.resrec
.name
->c
);
2914 if (m
->CurrentQuestion
)
2915 LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2916 m
->CurrentQuestion
= m
->Questions
;
2917 while (m
->CurrentQuestion
)
2919 DNSQuestion
*q
= m
->CurrentQuestion
;
2920 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2921 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_addnocache
); // QC_addnocache means "don't expect remove events for this"
2922 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2923 m
->CurrentQuestion
= q
->next
;
2925 m
->CurrentQuestion
= mDNSNULL
;
2928 // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
2929 // If new questions are created as a result of invoking client callbacks, they will be added to
2930 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
2931 // rr is an existing cache CacheRecord that just expired and is being deleted
2932 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
2933 // NOTE: CacheRecordRmv calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
2934 // which may change the record list and/or question list.
2935 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2936 mDNSlocal
void CacheRecordRmv(mDNS
*const m
, CacheRecord
*rr
)
2938 if (m
->CurrentQuestion
)
2939 LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
2940 m
->CurrentQuestion
= m
->Questions
;
2941 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2943 DNSQuestion
*q
= m
->CurrentQuestion
;
2944 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2946 verbosedebugf("CacheRecordRmv %p %s", rr
, CRDisplayString(m
, rr
));
2947 q
->FlappingInterface1
= mDNSNULL
;
2948 q
->FlappingInterface2
= mDNSNULL
;
2949 if (q
->CurrentAnswers
== 0)
2950 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?",
2951 q
, q
->qname
.c
, DNSTypeName(q
->qtype
));
2954 q
->CurrentAnswers
--;
2955 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
--;
2956 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
--;
2958 if (rr
->resrec
.rdata
->MaxRDLength
) // Never generate "remove" events for negative results
2960 if (q
->CurrentAnswers
== 0)
2962 LogOperation("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents",
2963 q
->qname
.c
, DNSTypeName(q
->qtype
));
2964 ReconfirmAntecedents(m
, &q
->qname
, q
->qnamehash
, 0);
2966 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_rmv
);
2969 if (m
->CurrentQuestion
== q
) // If m->CurrentQuestion was not auto-advanced, do it ourselves now
2970 m
->CurrentQuestion
= q
->next
;
2972 m
->CurrentQuestion
= mDNSNULL
;
2975 mDNSlocal
void ReleaseCacheEntity(mDNS
*const m
, CacheEntity
*e
)
2977 #if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
2979 for (i
=0; i
<sizeof(*e
); i
++) ((char*)e
)[i
] = 0xFF;
2981 e
->next
= m
->rrcache_free
;
2982 m
->rrcache_free
= e
;
2983 m
->rrcache_totalused
--;
2986 mDNSlocal
void ReleaseCacheGroup(mDNS
*const m
, CacheGroup
**cp
)
2988 CacheEntity
*e
= (CacheEntity
*)(*cp
);
2989 //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
2990 if ((*cp
)->rrcache_tail
!= &(*cp
)->members
)
2991 LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
2992 //if ((*cp)->name != (domainname*)((*cp)->namestorage))
2993 // LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
2994 if ((*cp
)->name
!= (domainname
*)((*cp
)->namestorage
)) mDNSPlatformMemFree((*cp
)->name
);
2995 (*cp
)->name
= mDNSNULL
;
2996 *cp
= (*cp
)->next
; // Cut record from list
2997 ReleaseCacheEntity(m
, e
);
3000 mDNSlocal
void ReleaseCacheRecord(mDNS
*const m
, CacheRecord
*r
)
3002 //LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r));
3003 if (r
->resrec
.rdata
&& r
->resrec
.rdata
!= (RData
*)&r
->rdatastorage
) mDNSPlatformMemFree(r
->resrec
.rdata
);
3004 r
->resrec
.rdata
= mDNSNULL
;
3005 ReleaseCacheEntity(m
, (CacheEntity
*)r
);
3008 // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering
3009 // CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all
3010 // callbacks for old records are delivered before callbacks for newer records.
3011 mDNSlocal
void CheckCacheExpiration(mDNS
*const m
, CacheGroup
*const cg
)
3013 CacheRecord
**rp
= &cg
->members
;
3015 if (m
->lock_rrcache
) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
3016 m
->lock_rrcache
= 1;
3020 CacheRecord
*const rr
= *rp
;
3021 mDNSs32 event
= RRExpireTime(rr
);
3022 if (m
->timenow
- event
>= 0) // If expired, delete it
3024 *rp
= rr
->next
; // Cut it from the list
3025 verbosedebugf("CheckCacheExpiration: Deleting%7d %4d %p %s",
3026 m
->timenow
- rr
->TimeRcvd
, rr
->resrec
.rroriginalttl
, rr
->CRActiveQuestion
, CRDisplayString(m
, rr
));
3027 if (rr
->CRActiveQuestion
) // If this record has one or more active questions, tell them it's going away
3029 CacheRecordRmv(m
, rr
);
3030 m
->rrcache_active
--;
3032 ReleaseCacheRecord(m
, rr
);
3034 else // else, not expired; see if we need to query
3036 if (rr
->DelayDelivery
&& rr
->DelayDelivery
- m
->timenow
> 0)
3037 event
= rr
->DelayDelivery
;
3040 if (rr
->DelayDelivery
) CacheRecordDeferredAdd(m
, rr
);
3041 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
3043 if (m
->timenow
- rr
->NextRequiredQuery
< 0) // If not yet time for next query
3044 event
= rr
->NextRequiredQuery
; // then just record when we want the next query
3045 else // else trigger our question to go out now
3047 // Set NextScheduledQuery to timenow so that SendQueries() will run.
3048 // SendQueries() will see that we have records close to expiration, and send FEQs for them.
3049 m
->NextScheduledQuery
= m
->timenow
;
3050 // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(),
3051 // which will correctly update m->NextCacheCheck for us.
3052 event
= m
->timenow
+ 0x3FFFFFFF;
3056 verbosedebugf("CheckCacheExpiration:%6d %5d %s",
3057 (event
- m
->timenow
) / mDNSPlatformOneSecond
, CacheCheckGracePeriod(rr
), CRDisplayString(m
, rr
));
3058 if (m
->NextCacheCheck
- (event
+ CacheCheckGracePeriod(rr
)) > 0)
3059 m
->NextCacheCheck
= (event
+ CacheCheckGracePeriod(rr
));
3063 if (cg
->rrcache_tail
!= rp
) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg
->rrcache_tail
, rp
);
3064 cg
->rrcache_tail
= rp
;
3065 m
->lock_rrcache
= 0;
3068 mDNSlocal
void AnswerNewQuestion(mDNS
*const m
)
3070 mDNSBool ShouldQueryImmediately
= mDNStrue
;
3072 DNSQuestion
*q
= m
->NewQuestions
; // Grab the question we're going to answer
3073 const mDNSu32 slot
= HashSlot(&q
->qname
);
3074 CacheGroup
*const cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
3076 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
3078 if (cg
) CheckCacheExpiration(m
, cg
);
3079 m
->NewQuestions
= q
->next
; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
3081 if (m
->lock_rrcache
) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
3082 // This should be safe, because calling the client's question callback may cause the
3083 // question list to be modified, but should not ever cause the rrcache list to be modified.
3084 // If the client's question callback deletes the question, then m->CurrentQuestion will
3085 // be advanced, and we'll exit out of the loop
3086 m
->lock_rrcache
= 1;
3087 if (m
->CurrentQuestion
)
3088 LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
3089 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
3091 if (q
->NoAnswer
== NoAnswer_Fail
)
3093 LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
3094 MakeNegativeCacheRecord(m
, &q
->qname
, q
->qnamehash
, q
->qtype
, q
->qclass
, 60);
3095 q
->NoAnswer
= NoAnswer_Normal
; // Temporarily turn off answer suppression
3096 AnswerCurrentQuestionWithResourceRecord(m
, &m
->rec
.r
, QC_addnocache
);
3097 q
->NoAnswer
= NoAnswer_Fail
; // Restore NoAnswer state
3098 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
3101 // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
3102 if (m
->CurrentQuestion
== q
&& q
->InterfaceID
== mDNSInterface_Any
)
3104 if (m
->CurrentRecord
)
3105 LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
3106 m
->CurrentRecord
= m
->ResourceRecords
;
3107 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
3109 AuthRecord
*rr
= m
->CurrentRecord
;
3110 m
->CurrentRecord
= rr
->next
;
3111 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
3112 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3114 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3115 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
3118 m
->CurrentRecord
= mDNSNULL
;
3121 if (m
->CurrentQuestion
== q
)
3123 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3124 if (SameNameRecordAnswersQuestion(&rr
->resrec
, q
))
3126 // SecsSinceRcvd is whole number of elapsed seconds, rounded down
3127 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
3128 if (rr
->resrec
.rroriginalttl
<= SecsSinceRcvd
)
3130 LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s) %d %d",
3131 rr
->resrec
.rroriginalttl
, SecsSinceRcvd
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), m
->timenow
, rr
->TimeRcvd
);
3132 continue; // Go to next one in loop
3135 // If this record set is marked unique, then that means we can reasonably assume we have the whole set
3136 // -- we don't need to rush out on the network and query immediately to see if there are more answers out there
3137 if ((rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) || (q
->ExpectUnique
))
3138 ShouldQueryImmediately
= mDNSfalse
;
3139 q
->CurrentAnswers
++;
3140 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
3141 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
3142 AnswerCurrentQuestionWithResourceRecord(m
, rr
, QC_add
);
3143 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
3145 else if (RRTypeIsAddressType(rr
->resrec
.rrtype
) && RRTypeIsAddressType(q
->qtype
))
3146 if (rr
->resrec
.namehash
== q
->qnamehash
&& SameDomainName(rr
->resrec
.name
, &q
->qname
))
3147 ShouldQueryImmediately
= mDNSfalse
;
3150 if (m
->CurrentQuestion
== q
&& ShouldQueryImmediately
&& ActiveQuestion(q
))
3152 q
->ThisQInterval
= InitialQuestionInterval
;
3153 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
3154 if (mDNSOpaque16IsZero(q
->TargetQID
))
3156 // Compute random delay in the range 1-6 seconds, then divide by 50 to get 20-120ms
3157 if (!m
->RandomQueryDelay
)
3158 m
->RandomQueryDelay
= (mDNSPlatformOneSecond
+ mDNSRandom(mDNSPlatformOneSecond
*5) - 1) / 50 + 1;
3159 q
->LastQTime
+= m
->RandomQueryDelay
;
3162 m
->NextScheduledQuery
= m
->timenow
;
3165 m
->CurrentQuestion
= mDNSNULL
;
3166 m
->lock_rrcache
= 0;
3169 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any
3170 // appropriate answers, stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions
3171 mDNSlocal
void AnswerNewLocalOnlyQuestion(mDNS
*const m
)
3173 DNSQuestion
*q
= m
->NewLocalOnlyQuestions
; // Grab the question we're going to answer
3174 m
->NewLocalOnlyQuestions
= q
->next
; // Advance NewQuestions to the next (if any)
3176 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
3178 if (m
->CurrentQuestion
)
3179 LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
3180 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
3182 if (m
->CurrentRecord
)
3183 LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
3184 m
->CurrentRecord
= m
->ResourceRecords
;
3186 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
3188 AuthRecord
*rr
= m
->CurrentRecord
;
3189 m
->CurrentRecord
= rr
->next
;
3190 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3192 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3193 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
3197 m
->CurrentQuestion
= mDNSNULL
;
3198 m
->CurrentRecord
= mDNSNULL
;
3201 mDNSlocal CacheEntity
*GetCacheEntity(mDNS
*const m
, const CacheGroup
*const PreserveCG
)
3203 CacheEntity
*e
= mDNSNULL
;
3205 if (m
->lock_rrcache
) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL
); }
3206 m
->lock_rrcache
= 1;
3208 // If we have no free records, ask the client layer to give us some more memory
3209 if (!m
->rrcache_free
&& m
->MainCallback
)
3211 if (m
->rrcache_totalused
!= m
->rrcache_size
)
3212 LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
3213 m
->rrcache_totalused
, m
->rrcache_size
);
3215 // We don't want to be vulnerable to a malicious attacker flooding us with an infinite
3216 // number of bogus records so that we keep growing our cache until the machine runs out of memory.
3217 // To guard against this, if our cache grows above 512kB (approx 3168 records at 164 bytes each),
3218 // and we're actively using less than 1/32 of that cache, then we purge all the unused records
3219 // and recycle them, instead of allocating more memory.
3220 if (m
->rrcache_size
> 3000 && m
->rrcache_size
/ 32 > m
->rrcache_active
)
3221 LogOperation("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
3222 m
->rrcache_size
, m
->rrcache_active
);
3225 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
3226 m
->MainCallback(m
, mStatus_GrowCache
);
3227 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
3231 // If we still have no free records, recycle all the records we can.
3232 // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
3233 if (!m
->rrcache_free
)
3235 #if LogAllOperations || MDNS_DEBUGMSGS
3236 mDNSu32 oldtotalused
= m
->rrcache_totalused
;
3239 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
3241 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
3244 CacheRecord
**rp
= &(*cp
)->members
;
3247 // Records that answer still-active questions are not candidates for recycling
3248 // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
3249 if ((*rp
)->CRActiveQuestion
|| (*rp
)->NextInCFList
)
3253 CacheRecord
*rr
= *rp
;
3254 *rp
= (*rp
)->next
; // Cut record from list
3255 ReleaseCacheRecord(m
, rr
);
3258 if ((*cp
)->rrcache_tail
!= rp
)
3259 verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot
, (*cp
)->rrcache_tail
, rp
);
3260 (*cp
)->rrcache_tail
= rp
;
3261 if ((*cp
)->members
|| (*cp
)==PreserveCG
) cp
=&(*cp
)->next
;
3262 else ReleaseCacheGroup(m
, cp
);
3265 LogOperation("GetCacheEntity recycled %d records to reduce cache from %d to %d",
3266 oldtotalused
- m
->rrcache_totalused
, oldtotalused
, m
->rrcache_totalused
);
3269 if (m
->rrcache_free
) // If there are records in the free list, take one
3271 e
= m
->rrcache_free
;
3272 m
->rrcache_free
= e
->next
;
3273 if (++m
->rrcache_totalused
>= m
->rrcache_report
)
3275 LogOperation("RR Cache now using %ld objects", m
->rrcache_totalused
);
3276 if (m
->rrcache_report
< 100) m
->rrcache_report
+= 10;
3277 else m
->rrcache_report
+= 100;
3279 mDNSPlatformMemZero(e
, sizeof(*e
));
3282 m
->lock_rrcache
= 0;
3287 mDNSlocal CacheRecord
*GetCacheRecord(mDNS
*const m
, CacheGroup
*cg
, mDNSu16 RDLength
)
3289 CacheRecord
*r
= (CacheRecord
*)GetCacheEntity(m
, cg
);
3292 r
->resrec
.rdata
= (RData
*)&r
->rdatastorage
; // By default, assume we're usually going to be using local storage
3293 if (RDLength
> InlineCacheRDSize
) // If RDLength is too big, allocate extra storage
3295 r
->resrec
.rdata
= (RData
*)mDNSPlatformMemAllocate(sizeofRDataHeader
+ RDLength
);
3296 if (r
->resrec
.rdata
) r
->resrec
.rdata
->MaxRDLength
= r
->resrec
.rdlength
= RDLength
;
3297 else { ReleaseCacheEntity(m
, (CacheEntity
*)r
); r
= mDNSNULL
; }
3303 mDNSlocal CacheGroup
*GetCacheGroup(mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
3305 mDNSu16 namelen
= DomainNameLength(rr
->name
);
3306 CacheGroup
*cg
= (CacheGroup
*)GetCacheEntity(m
, mDNSNULL
);
3307 if (!cg
) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr
->name
->c
); return(mDNSNULL
); }
3308 cg
->next
= m
->rrcache_hash
[slot
];
3309 cg
->namehash
= rr
->namehash
;
3310 cg
->members
= mDNSNULL
;
3311 cg
->rrcache_tail
= &cg
->members
;
3312 cg
->name
= (domainname
*)cg
->namestorage
;
3313 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s",
3314 // (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
3315 if (namelen
> InlineCacheGroupNameSize
) cg
->name
= mDNSPlatformMemAllocate(namelen
);
3318 LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr
->name
->c
);
3319 ReleaseCacheEntity(m
, (CacheEntity
*)cg
);
3322 AssignDomainName(cg
->name
, rr
->name
);
3324 if (CacheGroupForRecord(m
, slot
, rr
)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr
->name
->c
);
3325 m
->rrcache_hash
[slot
] = cg
;
3326 if (CacheGroupForRecord(m
, slot
, rr
) != cg
) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr
->name
->c
);
3331 mDNSexport
void mDNS_PurgeCacheResourceRecord(mDNS
*const m
, CacheRecord
*rr
)
3333 // Make sure we mark this record as thoroughly expired -- we don't ever want to give
3334 // a positive answer using an expired record (e.g. from an interface that has gone away).
3335 // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
3336 // summary deletion without giving the proper callback to any questions that are monitoring it.
3337 // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
3338 rr
->TimeRcvd
= m
->timenow
- mDNSPlatformOneSecond
* 60;
3339 rr
->UnansweredQueries
= MaxUnansweredQueries
;
3340 rr
->resrec
.rroriginalttl
= 0;
3341 SetNextCacheCheckTime(m
, rr
);
3344 mDNSexport mDNSs32
mDNS_TimeNow(const mDNS
*const m
)
3347 mDNSPlatformLock(m
);
3350 LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
3351 if (!m
->timenow
) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
3354 if (m
->timenow
) time
= m
->timenow
;
3355 else time
= mDNS_TimeNow_NoLock(m
);
3356 mDNSPlatformUnlock(m
);
3360 mDNSexport mDNSs32
mDNS_Execute(mDNS
*const m
)
3362 mDNS_Lock(m
); // Must grab lock before trying to read m->timenow
3364 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
3368 verbosedebugf("mDNS_Execute");
3369 if (m
->CurrentQuestion
)
3370 LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
3372 // 1. If we're past the probe suppression time, we can clear it
3373 if (m
->SuppressProbes
&& m
->timenow
- m
->SuppressProbes
>= 0) m
->SuppressProbes
= 0;
3375 // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
3376 if (m
->NumFailedProbes
&& m
->timenow
- m
->ProbeFailTime
>= mDNSPlatformOneSecond
* 10) m
->NumFailedProbes
= 0;
3378 // 3. Purge our cache of stale old records
3379 if (m
->rrcache_size
&& m
->timenow
- m
->NextCacheCheck
>= 0)
3382 m
->NextCacheCheck
= m
->timenow
+ 0x3FFFFFFF;
3383 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
3385 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
3388 CheckCacheExpiration(m
, *cp
);
3389 if ((*cp
)->members
) cp
=&(*cp
)->next
;
3390 else ReleaseCacheGroup(m
, cp
);
3395 // 4. See if we can answer any of our new local questions from the cache
3396 for (i
=0; m
->NewQuestions
&& i
<1000; i
++)
3398 if (m
->NewQuestions
->DelayAnswering
&& m
->timenow
- m
->NewQuestions
->DelayAnswering
< 0) break;
3399 AnswerNewQuestion(m
);
3401 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
3403 for (i
=0; m
->NewLocalOnlyQuestions
&& i
<1000; i
++) AnswerNewLocalOnlyQuestion(m
);
3404 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
3406 for (i
=0; i
<1000 && m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
); i
++)
3408 AuthRecord
*rr
= m
->NewLocalRecords
;
3409 m
->NewLocalRecords
= m
->NewLocalRecords
->next
;
3410 AnswerLocalQuestions(m
, rr
, mDNStrue
);
3412 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
3414 // 5. See what packets we need to send
3415 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) DiscardDeregistrations(m
);
3416 else if (m
->SuppressSending
== 0 || m
->timenow
- m
->SuppressSending
>= 0)
3418 // If the platform code is ready, and we're not suppressing packet generation right now
3419 // then send our responses, probes, and questions.
3420 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them.
3421 // We send queries next, because there might be final-stage probes that complete their probing here, causing
3422 // them to advance to announcing state, and we want those to be included in any announcements we send out.
3423 // Finally, we send responses, including the previously mentioned records that just completed probing.
3424 m
->SuppressSending
= 0;
3426 // 6. Send Query packets. This may cause some probing records to advance to announcing state
3427 if (m
->timenow
- m
->NextScheduledQuery
>= 0 || m
->timenow
- m
->NextScheduledProbe
>= 0) SendQueries(m
);
3428 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3431 LogMsg("mDNS_Execute: SendQueries didn't send all its queries (%d - %d = %d) will try again in one second",
3432 m
->timenow
, m
->NextScheduledQuery
, m
->timenow
- m
->NextScheduledQuery
);
3433 m
->NextScheduledQuery
= m
->timenow
+ mDNSPlatformOneSecond
;
3434 for (q
= m
->Questions
; q
; q
=q
->next
)
3435 if (ActiveQuestion(q
) && q
->LastQTime
+ q
->ThisQInterval
- m
->timenow
<= 0)
3436 LogMsg("mDNS_Execute: SendQueries didn't send %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
3438 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3440 LogMsg("mDNS_Execute: SendQueries didn't send all its probes (%d - %d = %d) will try again in one second",
3441 m
->timenow
, m
->NextScheduledProbe
, m
->timenow
- m
->NextScheduledProbe
);
3442 m
->NextScheduledProbe
= m
->timenow
+ mDNSPlatformOneSecond
;
3445 // 7. Send Response packets, including probing records just advanced to announcing state
3446 if (m
->timenow
- m
->NextScheduledResponse
>= 0) SendResponses(m
);
3447 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
3449 LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
3450 m
->NextScheduledResponse
= m
->timenow
+ mDNSPlatformOneSecond
;
3454 // Clear RandomDelay values, ready to pick a new different value next time
3455 m
->RandomQueryDelay
= 0;
3456 m
->RandomReconfirmDelay
= 0;
3459 // Note about multi-threaded systems:
3460 // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
3461 // performing mDNS API operations that change our next scheduled event time.
3463 // On multi-threaded systems (like the current Windows implementation) that have a single main thread
3464 // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
3465 // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
3466 // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
3467 // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
3468 // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
3469 // does, the state of the signal will be noticed, causing the blocking primitive to return immediately
3470 // without blocking. This avoids the race condition between the signal from the other thread arriving
3471 // just *before* or just *after* the main thread enters the blocking primitive.
3473 // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
3474 // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
3475 // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
3476 // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
3477 // by the time it gets to the timer callback function).
3479 #ifndef UNICAST_DISABLED
3482 mDNS_Unlock(m
); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
3483 return(m
->NextScheduledEvent
);
3486 mDNSlocal
void SuspendLLQs(mDNS
*m
)
3489 for (q
= m
->Questions
; q
; q
= q
->next
)
3490 if (ActiveQuestion(q
) && !mDNSOpaque16IsZero(q
->TargetQID
) && q
->LongLived
)
3492 // If necessary, tell server it can delete this LLQ state
3493 if (q
->state
== LLQ_Established
) sendLLQRefresh(m
, q
, 0);
3494 if (q
->nta
) { CancelGetZoneData(m
, q
->nta
); q
->nta
= mDNSNULL
; }
3495 if (q
->tcp
) { DisposeTCPConn(q
->tcp
); q
->tcp
= mDNSNULL
; }
3496 q
->state
= LLQ_InitialRequest
; // Will need to set up new LLQ on wake from sleep
3497 q
->id
= zeroOpaque64
;
3501 mDNSlocal
void ActivateUnicastQuery(mDNS
*const m
, DNSQuestion
*const question
)
3503 // For now this AutoTunnel stuff is specific to Mac OS X.
3504 // In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
3505 #if APPLE_OSX_mDNSResponder
3506 if (question
->qtype
== kDNSType_AAAA
&& question
->AuthInfo
&& question
->AuthInfo
->AutoTunnel
&& question
->QuestionCallback
!= AutoTunnelCallback
)
3508 question
->NoAnswer
= NoAnswer_Suspended
;
3509 AddNewClientTunnel(m
, question
);
3512 #endif // APPLE_OSX_mDNSResponder
3514 if (!question
->DuplicateOf
)
3516 LogOperation("ActivateUnicastQuery: %##s %s%s",
3517 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->AuthInfo
? " (Private)" : "");
3518 if (question
->nta
) { CancelGetZoneData(m
, question
->nta
); question
->nta
= mDNSNULL
; }
3519 if (question
->LongLived
) question
->state
= LLQ_InitialRequest
;
3520 question
->ThisQInterval
= InitialQuestionInterval
;
3521 question
->LastQTime
= m
->timenow
- question
->ThisQInterval
;
3522 SetNextQueryTime(m
, question
);
3526 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
3527 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
3528 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
3529 // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
3530 // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
3531 // found itself in a new network environment. For example, if the Ethernet hardware indicates that the
3532 // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
3533 // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
3534 // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
3535 // traffic, so it should only be called when there is legitimate reason to believe the machine
3536 // may have become attached to a new network.
3537 mDNSexport
void mDNSCoreMachineSleep(mDNS
*const m
, mDNSBool sleepstate
)
3543 m
->SleepState
= sleepstate
;
3544 LogOperation("%s at %ld", sleepstate
? "Sleeping" : "Waking", m
->timenow
);
3548 #ifndef UNICAST_DISABLED
3550 SleepServiceRegistrations(m
);
3551 SleepRecordRegistrations(m
);
3553 // Mark all the records we need to deregister and send them
3554 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3555 if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
3556 rr
->ImmedAnswer
= mDNSInterfaceMark
;
3566 #ifndef UNICAST_DISABLED
3567 // On wake, retrigger all our uDNS questions
3568 if (m
->CurrentQuestion
)
3569 LogMsg("RestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)", m
->CurrentQuestion
->qname
.c
, DNSTypeName(m
->CurrentQuestion
->qtype
));
3570 m
->CurrentQuestion
= m
->Questions
;
3571 while (m
->CurrentQuestion
)
3573 DNSQuestion
*q
= m
->CurrentQuestion
;
3574 m
->CurrentQuestion
= m
->CurrentQuestion
->next
;
3575 if (!mDNSOpaque16IsZero(q
->TargetQID
)) ActivateUnicastQuery(m
, q
);
3577 // and reactivtate record (and service) registrations
3580 // 1. Retrigger all our mDNS questions
3581 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
3582 if (mDNSOpaque16IsZero(q
->TargetQID
) && ActiveQuestion(q
))
3584 q
->ThisQInterval
= InitialQuestionInterval
; // MUST be > zero for an active question
3585 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
3586 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
3587 q
->RecentAnswerPkts
= 0;
3588 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
);
3589 m
->NextScheduledQuery
= m
->timenow
;
3592 // 2. Re-validate our cache records
3593 m
->NextCacheCheck
= m
->timenow
;
3594 FORALL_CACHERECORDS(slot
, cg
, cr
)
3595 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForWake
);
3597 // 3. Retrigger probing and announcing for all our authoritative records
3598 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3599 if (!AuthRecord_uDNS(rr
))
3601 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
3602 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
3603 rr
->AnnounceCount
= InitialAnnounceCount
;
3604 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
3605 InitializeLastAPTime(m
, rr
);
3612 // ***************************************************************************
3613 #if COMPILER_LIKES_PRAGMA_MARK
3615 #pragma mark - Packet Reception Functions
3618 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
3620 mDNSlocal mDNSu8
*GenerateUnicastResponse(const DNSMessage
*const query
, const mDNSu8
*const end
,
3621 const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, DNSMessage
*const response
, AuthRecord
*ResponseRecords
)
3623 mDNSu8
*responseptr
= response
->data
;
3624 const mDNSu8
*const limit
= response
->data
+ sizeof(response
->data
);
3625 const mDNSu8
*ptr
= query
->data
;
3627 mDNSu32 maxttl
= 0x70000000;
3630 // Initialize the response fields so we can answer the questions
3631 InitializeDNSMessage(&response
->h
, query
->h
.id
, ResponseFlags
);
3634 // *** 1. Write out the list of questions we are actually going to answer with this packet
3639 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
3642 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &q
); // get the question...
3643 if (!ptr
) return(mDNSNULL
);
3645 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // and search our list of proposed answers
3647 if (rr
->NR_AnswerTo
== ptr
) // If we're going to generate a record answering this question
3648 { // then put the question in the question section
3649 responseptr
= putQuestion(response
, responseptr
, limit
, &q
.qname
, q
.qtype
, q
.qclass
);
3650 if (!responseptr
) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL
); }
3651 break; // break out of the ResponseRecords loop, and go on to the next question
3656 if (response
->h
.numQuestions
== 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL
); }
3660 // *** 2. Write Answers
3662 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3663 if (rr
->NR_AnswerTo
)
3665 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAnswers
, &rr
->resrec
, maxttl
);
3666 if (p
) responseptr
= p
;
3667 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response
->h
.flags
.b
[0] |= kDNSFlag0_TC
; }
3671 // *** 3. Write Additionals
3673 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3674 if (rr
->NR_AdditionalTo
&& !rr
->NR_AnswerTo
)
3676 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAdditionals
, &rr
->resrec
, maxttl
);
3677 if (p
) responseptr
= p
;
3678 else debugf("GenerateUnicastResponse: No more space for additionals");
3681 return(responseptr
);
3684 // AuthRecord *our is our Resource Record
3685 // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
3686 // Returns 0 if there is no conflict
3687 // Returns +1 if there was a conflict and we won
3688 // Returns -1 if there was a conflict and we lost and have to rename
3689 mDNSlocal
int CompareRData(AuthRecord
*our
, CacheRecord
*pkt
)
3691 mDNSu8 ourdata
[256], *ourptr
= ourdata
, *ourend
;
3692 mDNSu8 pktdata
[256], *pktptr
= pktdata
, *pktend
;
3693 if (!our
) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
3694 if (!pkt
) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
3696 ourend
= putRData(mDNSNULL
, ourdata
, ourdata
+ sizeof(ourdata
), &our
->resrec
);
3697 pktend
= putRData(mDNSNULL
, pktdata
, pktdata
+ sizeof(pktdata
), &pkt
->resrec
);
3698 while (ourptr
< ourend
&& pktptr
< pktend
&& *ourptr
== *pktptr
) { ourptr
++; pktptr
++; }
3699 if (ourptr
>= ourend
&& pktptr
>= pktend
) return(0); // If data identical, not a conflict
3701 if (ourptr
>= ourend
) return(-1); // Our data ran out first; We lost
3702 if (pktptr
>= pktend
) return(+1); // Packet data ran out first; We won
3703 if (*pktptr
> *ourptr
) return(-1); // Our data is numerically lower; We lost
3704 if (*pktptr
< *ourptr
) return(+1); // Packet data is numerically lower; We won
3706 LogMsg("CompareRData ERROR: Invalid state");
3710 // See if we have an authoritative record that's identical to this packet record,
3711 // whose canonical DependentOn record is the specified master record.
3712 // The DependentOn pointer is typically used for the TXT record of service registrations
3713 // It indicates that there is no inherent conflict detection for the TXT record
3714 // -- it depends on the SRV record to resolve name conflicts
3715 // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
3716 // pointer chain (if any) to make sure we reach the canonical DependentOn record
3717 // If the record has no DependentOn, then just return that record's pointer
3718 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
3719 mDNSlocal mDNSBool
MatchDependentOn(const mDNS
*const m
, const CacheRecord
*const pktrr
, const AuthRecord
*const master
)
3721 const AuthRecord
*r1
;
3722 for (r1
= m
->ResourceRecords
; r1
; r1
=r1
->next
)
3724 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
3726 const AuthRecord
*r2
= r1
;
3727 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
3728 if (r2
== master
) return(mDNStrue
);
3731 for (r1
= m
->DuplicateRecords
; r1
; r1
=r1
->next
)
3733 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
3735 const AuthRecord
*r2
= r1
;
3736 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
3737 if (r2
== master
) return(mDNStrue
);
3743 // Find the canonical RRSet pointer for this RR received in a packet.
3744 // If we find any identical AuthRecord in our authoritative list, then follow its RRSet
3745 // pointers (if any) to make sure we return the canonical member of this name/type/class
3746 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
3747 mDNSlocal
const AuthRecord
*FindRRSet(const mDNS
*const m
, const CacheRecord
*const pktrr
)
3749 const AuthRecord
*rr
;
3750 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3752 if (IdenticalResourceRecord(&rr
->resrec
, &pktrr
->resrec
))
3754 while (rr
->RRSet
&& rr
!= rr
->RRSet
) rr
= rr
->RRSet
;
3761 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
3762 // as one of our records (our) but different rdata.
3763 // 1. If our record is not a type that's supposed to be unique, we don't care.
3764 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
3765 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
3766 // points to our record, ignore this conflict (e.g. the packet record matches one of our
3767 // TXT records, and that record is marked as dependent on 'our', its SRV record).
3768 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
3769 // are members of the same RRSet, then this is not a conflict.
3770 mDNSlocal mDNSBool
PacketRRConflict(const mDNS
*const m
, const AuthRecord
*const our
, const CacheRecord
*const pktrr
)
3772 const AuthRecord
*ourset
= our
->RRSet
? our
->RRSet
: our
;
3774 // If not supposed to be unique, not a conflict
3775 if (!(our
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)) return(mDNSfalse
);
3777 // If a dependent record, not a conflict
3778 if (our
->DependentOn
|| MatchDependentOn(m
, pktrr
, our
)) return(mDNSfalse
);
3780 // If the pktrr matches a member of ourset, not a conflict
3781 if (FindRRSet(m
, pktrr
) == ourset
) return(mDNSfalse
);
3783 // Okay, this is a conflict
3787 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
3788 // the record list and/or question list.
3789 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3790 mDNSlocal
void ResolveSimultaneousProbe(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
3791 DNSQuestion
*q
, AuthRecord
*our
)
3794 const mDNSu8
*ptr
= LocateAuthorities(query
, end
);
3795 mDNSBool FoundUpdate
= mDNSfalse
;
3797 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
3799 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, kDNSRecordTypePacketAuth
, &m
->rec
);
3801 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
3803 FoundUpdate
= mDNStrue
;
3804 if (PacketRRConflict(m
, our
, &m
->rec
.r
))
3806 int result
= (int)our
->resrec
.rrclass
- (int)m
->rec
.r
.resrec
.rrclass
;
3807 if (!result
) result
= (int)our
->resrec
.rrtype
- (int)m
->rec
.r
.resrec
.rrtype
;
3808 if (!result
) result
= CompareRData(our
, &m
->rec
.r
);
3810 LogOperation("ResolveSimultaneousProbe: %##s (%s): We won", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
3811 else if (result
< 0)
3813 LogOperation("ResolveSimultaneousProbe: %##s (%s): We lost", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
3814 mDNS_Deregister_internal(m
, our
, mDNS_Dereg_conflict
);
3819 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
3822 LogOperation("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
3824 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
3827 mDNSlocal CacheRecord
*FindIdenticalRecordInCache(const mDNS
*const m
, ResourceRecord
*pktrr
)
3829 mDNSu32 slot
= HashSlot(pktrr
->name
);
3830 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, pktrr
);
3832 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3833 if (pktrr
->InterfaceID
== rr
->resrec
.InterfaceID
&& IdenticalSameNameRecord(pktrr
, &rr
->resrec
)) break;
3837 // ProcessQuery examines a received query to see if we have any answers to give
3838 mDNSlocal mDNSu8
*ProcessQuery(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
3839 const mDNSAddr
*srcaddr
, const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, mDNSBool QueryWasMulticast
,
3840 mDNSBool QueryWasLocalUnicast
, DNSMessage
*const response
)
3842 mDNSBool FromLocalSubnet
= srcaddr
&& AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
3843 AuthRecord
*ResponseRecords
= mDNSNULL
;
3844 AuthRecord
**nrp
= &ResponseRecords
;
3845 CacheRecord
*ExpectedAnswers
= mDNSNULL
; // Records in our cache we expect to see updated
3846 CacheRecord
**eap
= &ExpectedAnswers
;
3847 DNSQuestion
*DupQuestions
= mDNSNULL
; // Our questions that are identical to questions in this packet
3848 DNSQuestion
**dqp
= &DupQuestions
;
3849 mDNSs32 delayresponse
= 0;
3850 mDNSBool SendLegacyResponse
= mDNSfalse
;
3851 const mDNSu8
*ptr
= query
->data
;
3852 mDNSu8
*responseptr
= mDNSNULL
;
3857 // *** 1. Parse Question Section and mark potential answers
3859 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
3861 mDNSBool QuestionNeedsMulticastResponse
;
3862 int NumAnswersForThisQuestion
= 0;
3863 DNSQuestion pktq
, *q
;
3864 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &pktq
); // get the question...
3865 if (!ptr
) goto exit
;
3867 // The only queries that *need* a multicast response are:
3868 // * Queries sent via multicast
3870 // * that don't have the kDNSQClass_UnicastResponse bit set
3871 // These queries need multicast responses because other clients will:
3872 // * suppress their own identical questions when they see these questions, and
3873 // * expire their cache records if they don't see the expected responses
3874 // For other queries, we may still choose to send the occasional multicast response anyway,
3875 // to keep our neighbours caches warm, and for ongoing conflict detection.
3876 QuestionNeedsMulticastResponse
= QueryWasMulticast
&& !LegacyQuery
&& !(pktq
.qclass
& kDNSQClass_UnicastResponse
);
3877 // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
3878 pktq
.qclass
&= ~kDNSQClass_UnicastResponse
;
3880 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
3881 // can result in user callbacks which may change the record list and/or question list.
3882 // Also note: we just mark potential answer records here, without trying to build the
3883 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
3884 // from that list while we're in the middle of trying to build it.
3885 if (m
->CurrentRecord
)
3886 LogMsg("ProcessQuery ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
3887 m
->CurrentRecord
= m
->ResourceRecords
;
3888 while (m
->CurrentRecord
)
3890 rr
= m
->CurrentRecord
;
3891 m
->CurrentRecord
= rr
->next
;
3892 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && (QueryWasMulticast
|| QueryWasLocalUnicast
|| rr
->AllowRemoteQuery
))
3894 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
3895 ResolveSimultaneousProbe(m
, query
, end
, &pktq
, rr
);
3896 else if (ResourceRecordIsValidAnswer(rr
))
3898 NumAnswersForThisQuestion
++;
3900 // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
3901 // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
3902 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
3903 // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
3904 // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link)
3905 // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
3906 if (QuestionNeedsMulticastResponse
|| (!FromLocalSubnet
&& QueryWasMulticast
&& !LegacyQuery
))
3908 // We only mark this question for sending if it is at least one second since the last time we multicast it
3909 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
3910 // This is to guard against the case where someone blasts us with queries as fast as they can.
3911 if (m
->timenow
- (rr
->LastMCTime
+ mDNSPlatformOneSecond
) >= 0 ||
3912 (rr
->LastMCInterface
!= mDNSInterfaceMark
&& rr
->LastMCInterface
!= InterfaceID
))
3913 rr
->NR_AnswerTo
= (mDNSu8
*)~0;
3915 else if (!rr
->NR_AnswerTo
) rr
->NR_AnswerTo
= LegacyQuery
? ptr
: (mDNSu8
*)~1;
3920 // If we couldn't answer this question, someone else might be able to,
3921 // so use random delay on response to reduce collisions
3922 if (NumAnswersForThisQuestion
== 0) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
3924 // We only do the following accelerated cache expiration processing and duplicate question suppression processing
3925 // for multicast queries with multicast responses.
3926 // For any query generating a unicast response we don't do this because we can't assume we will see the response
3927 if (QuestionNeedsMulticastResponse
)
3929 const mDNSu32 slot
= HashSlot(&pktq
.qname
);
3930 CacheGroup
*cg
= CacheGroupForName(m
, slot
, pktq
.qnamehash
, &pktq
.qname
);
3933 // Make a list indicating which of our own cache records we expect to see updated as a result of this query
3934 // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
3935 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3936 if (SameNameRecordAnswersQuestion(&rr
->resrec
, &pktq
) && rr
->resrec
.rdlength
<= SmallRecordLimit
)
3937 if (!rr
->NextInKAList
&& eap
!= &rr
->NextInKAList
)
3940 eap
= &rr
->NextInKAList
;
3941 if (rr
->MPUnansweredQ
== 0 || m
->timenow
- rr
->MPLastUnansweredQT
>= mDNSPlatformOneSecond
)
3943 // Although MPUnansweredQ is only really used for multi-packet query processing,
3944 // we increment it for both single-packet and multi-packet queries, so that it stays in sync
3945 // with the MPUnansweredKA value, which by necessity is incremented for both query types.
3946 rr
->MPUnansweredQ
++;
3947 rr
->MPLastUnansweredQT
= m
->timenow
;
3948 rr
->MPExpectingKA
= mDNStrue
;
3952 // Check if this question is the same as any of mine.
3953 // We only do this for non-truncated queries. Right now it would be too complicated to try
3954 // to keep track of duplicate suppression state between multiple packets, especially when we
3955 // can't guarantee to receive all of the Known Answer packets that go with a particular query.
3956 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
3957 for (q
= m
->Questions
; q
; q
=q
->next
)
3958 if (!q
->Target
.type
&& ActiveQuestion(q
) && m
->timenow
- q
->LastQTxTime
> mDNSPlatformOneSecond
/ 4)
3959 if (!q
->InterfaceID
|| q
->InterfaceID
== InterfaceID
)
3960 if (q
->NextInDQList
== mDNSNULL
&& dqp
!= &q
->NextInDQList
)
3961 if (q
->qtype
== pktq
.qtype
&&
3962 q
->qclass
== pktq
.qclass
&&
3963 q
->qnamehash
== pktq
.qnamehash
&& SameDomainName(&q
->qname
, &pktq
.qname
))
3964 { *dqp
= q
; dqp
= &q
->NextInDQList
; }
3969 // *** 2. Now we can safely build the list of marked answers
3971 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Now build our list of potential answers
3972 if (rr
->NR_AnswerTo
) // If we marked the record...
3973 AddRecordToResponseList(&nrp
, rr
, mDNSNULL
); // ... add it to the list
3976 // *** 3. Add additional records
3978 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
3981 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
3983 for (i
=0; i
<query
->h
.numAnswers
; i
++) // For each record in the query's answer section...
3985 // Get the record...
3987 CacheRecord
*ourcacherr
;
3988 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &m
->rec
);
3989 if (!ptr
) goto exit
;
3991 // See if this Known-Answer suppresses any of our currently planned answers
3992 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
3993 if (MustSendRecord(rr
) && ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
3994 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
3996 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
3997 for (rr
=m
->ResourceRecords
; rr
; rr
=rr
->next
)
3999 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
4000 if (rr
->ImmedAnswer
== InterfaceID
&& ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
4002 if (srcaddr
->type
== mDNSAddrType_IPv4
)
4004 if (mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= zerov4Addr
;
4006 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
4008 if (mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= zerov6Addr
;
4010 if (mDNSIPv4AddressIsZero(rr
->v4Requester
) && mDNSIPv6AddressIsZero(rr
->v6Requester
))
4012 rr
->ImmedAnswer
= mDNSNULL
;
4013 rr
->ImmedUnicast
= mDNSfalse
;
4014 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4015 LogMsg("Suppressed after%4d: %s", m
->timenow
- rr
->ImmedAnswerMarkTime
, ARDisplayString(m
, rr
));
4021 // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
4022 // 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).
4023 ourcacherr
= FindIdenticalRecordInCache(m
, &m
->rec
.r
.resrec
);
4024 if (ourcacherr
&& ourcacherr
->MPExpectingKA
&& m
->timenow
- ourcacherr
->MPLastUnansweredQT
< mDNSPlatformOneSecond
)
4026 ourcacherr
->MPUnansweredKA
++;
4027 ourcacherr
->MPExpectingKA
= mDNSfalse
;
4030 // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
4031 // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
4032 // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
4033 eap
= &ExpectedAnswers
;
4036 CacheRecord
*rr
= *eap
;
4037 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
4038 { *eap
= rr
->NextInKAList
; rr
->NextInKAList
= mDNSNULL
; }
4039 else eap
= &rr
->NextInKAList
;
4042 // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
4045 dqp
= &DupQuestions
;
4048 DNSQuestion
*q
= *dqp
;
4049 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
4050 { *dqp
= q
->NextInDQList
; q
->NextInDQList
= mDNSNULL
; }
4051 else dqp
= &q
->NextInDQList
;
4054 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4058 // *** 5. Cancel any additionals that were added because of now-deleted records
4060 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4061 if (rr
->NR_AdditionalTo
&& !MustSendRecord(rr
->NR_AdditionalTo
))
4062 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
4065 // *** 6. Mark the send flags on the records we plan to send
4067 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4069 if (rr
->NR_AnswerTo
)
4071 mDNSBool SendMulticastResponse
= mDNSfalse
; // Send modern multicast response
4072 mDNSBool SendUnicastResponse
= mDNSfalse
; // Send modern unicast response (not legacy unicast response)
4074 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
4075 if (m
->timenow
- (rr
->LastMCTime
+ TicksTTL(rr
)/4) >= 0)
4077 SendMulticastResponse
= mDNStrue
;
4078 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
4079 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
4080 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
4081 if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) rr
->NR_AnswerTo
= (mDNSu8
*)~0;
4084 // If the client insists on a multicast response, then we'd better send one
4085 if (rr
->NR_AnswerTo
== (mDNSu8
*)~0) SendMulticastResponse
= mDNStrue
;
4086 else if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) SendUnicastResponse
= mDNStrue
;
4087 else if (rr
->NR_AnswerTo
) SendLegacyResponse
= mDNStrue
;
4089 if (SendMulticastResponse
|| SendUnicastResponse
)
4091 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4092 rr
->ImmedAnswerMarkTime
= m
->timenow
;
4094 m
->NextScheduledResponse
= m
->timenow
;
4095 // If we're already planning to send this on another interface, just send it on all interfaces
4096 if (rr
->ImmedAnswer
&& rr
->ImmedAnswer
!= InterfaceID
)
4097 rr
->ImmedAnswer
= mDNSInterfaceMark
;
4100 rr
->ImmedAnswer
= InterfaceID
; // Record interface to send it on
4101 if (SendUnicastResponse
) rr
->ImmedUnicast
= mDNStrue
;
4102 if (srcaddr
->type
== mDNSAddrType_IPv4
)
4104 if (mDNSIPv4AddressIsZero(rr
->v4Requester
)) rr
->v4Requester
= srcaddr
->ip
.v4
;
4105 else if (!mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= onesIPv4Addr
;
4107 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
4109 if (mDNSIPv6AddressIsZero(rr
->v6Requester
)) rr
->v6Requester
= srcaddr
->ip
.v6
;
4110 else if (!mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= onesIPv6Addr
;
4114 // If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
4115 // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
4116 // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
4117 // else, for a simple unique record reply, we can reply immediately; no need for delay
4118 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
) delayresponse
= mDNSPlatformOneSecond
* 20; // Divided by 50 = 400ms
4119 else if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
4121 else if (rr
->NR_AdditionalTo
&& rr
->NR_AdditionalTo
->NR_AnswerTo
== (mDNSu8
*)~0)
4123 // Since additional records are an optimization anyway, we only ever send them on one interface at a time
4124 // If two clients on different interfaces do queries that invoke the same optional additional answer,
4125 // then the earlier client is out of luck
4126 rr
->ImmedAdditional
= InterfaceID
;
4127 // No need to set m->NextScheduledResponse here
4128 // We'll send these additional records when we send them, or not, as the case may be
4133 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
4135 if (delayresponse
&& (!m
->SuppressSending
|| (m
->SuppressSending
- m
->timenow
) < (delayresponse
+ 49) / 50))
4137 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4138 mDNSs32 oldss
= m
->SuppressSending
;
4139 if (oldss
&& delayresponse
)
4140 LogMsg("Current SuppressSending delay%5ld; require%5ld", m
->SuppressSending
- m
->timenow
, (delayresponse
+ 49) / 50);
4142 // Pick a random delay:
4143 // We start with the base delay chosen above (typically either 1 second or 20 seconds),
4144 // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
4145 // This is an integer value, with resolution determined by the platform clock rate.
4146 // We then divide that by 50 to get the delay value in ticks. We defer the division until last
4147 // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
4148 // The +49 before dividing is to ensure we round up, not down, to ensure that even
4149 // on platforms where the native clock rate is less than fifty ticks per second,
4150 // we still guarantee that the final calculated delay is at least one platform tick.
4151 // We want to make sure we don't ever allow the delay to be zero ticks,
4152 // because if that happens we'll fail the Bonjour Conformance Test.
4153 // Our final computed delay is 20-120ms for normal delayed replies,
4154 // or 400-500ms in the case of multi-packet known-answer lists.
4155 m
->SuppressSending
= m
->timenow
+ (delayresponse
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*5) + 49) / 50;
4156 if (m
->SuppressSending
== 0) m
->SuppressSending
= 1;
4157 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4158 if (oldss
&& delayresponse
)
4159 LogMsg("Set SuppressSending to %5ld", m
->SuppressSending
- m
->timenow
);
4164 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
4166 if (SendLegacyResponse
)
4167 responseptr
= GenerateUnicastResponse(query
, end
, InterfaceID
, LegacyQuery
, response
, ResponseRecords
);
4170 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4173 // *** 9. Finally, clear our link chains ready for use next time
4175 while (ResponseRecords
)
4177 rr
= ResponseRecords
;
4178 ResponseRecords
= rr
->NextResponse
;
4179 rr
->NextResponse
= mDNSNULL
;
4180 rr
->NR_AnswerTo
= mDNSNULL
;
4181 rr
->NR_AdditionalTo
= mDNSNULL
;
4184 while (ExpectedAnswers
)
4187 rr
= ExpectedAnswers
;
4188 ExpectedAnswers
= rr
->NextInKAList
;
4189 rr
->NextInKAList
= mDNSNULL
;
4191 // For non-truncated queries, we can definitively say that we should expect
4192 // to be seeing a response for any records still left in the ExpectedAnswers list
4193 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
4194 if (rr
->UnansweredQueries
== 0 || m
->timenow
- rr
->LastUnansweredTime
>= mDNSPlatformOneSecond
)
4196 rr
->UnansweredQueries
++;
4197 rr
->LastUnansweredTime
= m
->timenow
;
4198 if (rr
->UnansweredQueries
> 1)
4199 debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
4200 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
4201 SetNextCacheCheckTime(m
, rr
);
4204 // If we've seen multiple unanswered queries for this record,
4205 // then mark it to expire in five seconds if we don't get a response by then.
4206 if (rr
->UnansweredQueries
>= MaxUnansweredQueries
)
4208 // Only show debugging message if this record was not about to expire anyway
4209 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
4210 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
4211 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
4212 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
4214 // Make a guess, based on the multi-packet query / known answer counts, whether we think we
4215 // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
4216 // possible packet loss of up to 20% of the additional KA packets.)
4217 else if (rr
->MPUnansweredQ
* 4 > rr
->MPUnansweredKA
* 5 + 8)
4219 // We want to do this conservatively.
4220 // If there are so many machines on the network that they have to use multi-packet known-answer lists,
4221 // then we don't want them to all hit the network simultaneously with their final expiration queries.
4222 // By setting the record to expire in four minutes, we achieve two things:
4223 // (a) the 90-95% final expiration queries will be less bunched together
4224 // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
4225 mDNSu32 remain
= (mDNSu32
)(RRExpireTime(rr
) - m
->timenow
) / 4;
4226 if (remain
> 240 * (mDNSu32
)mDNSPlatformOneSecond
)
4227 remain
= 240 * (mDNSu32
)mDNSPlatformOneSecond
;
4229 // Only show debugging message if this record was not about to expire anyway
4230 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
4231 debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
4232 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
4234 if (remain
<= 60 * (mDNSu32
)mDNSPlatformOneSecond
)
4235 rr
->UnansweredQueries
++; // Treat this as equivalent to one definite unanswered query
4236 rr
->MPUnansweredQ
= 0; // Clear MPQ/MPKA statistics
4237 rr
->MPUnansweredKA
= 0;
4238 rr
->MPExpectingKA
= mDNSfalse
;
4240 if (remain
< kDefaultReconfirmTimeForNoAnswer
)
4241 remain
= kDefaultReconfirmTimeForNoAnswer
;
4242 mDNS_Reconfirm_internal(m
, rr
, remain
);
4246 while (DupQuestions
)
4249 DNSQuestion
*q
= DupQuestions
;
4250 DupQuestions
= q
->NextInDQList
;
4251 q
->NextInDQList
= mDNSNULL
;
4252 i
= RecordDupSuppressInfo(q
->DupSuppress
, m
->timenow
, InterfaceID
, srcaddr
->type
);
4253 debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q
->qname
.c
, DNSTypeName(q
->qtype
), InterfaceID
,
4254 srcaddr
->type
== mDNSAddrType_IPv4
? "v4" : "v6", i
);
4257 return(responseptr
);
4260 mDNSlocal
void mDNSCoreReceiveQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
4261 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
4262 const mDNSInterfaceID InterfaceID
)
4264 mDNSu8
*responseend
= mDNSNULL
;
4265 mDNSBool QueryWasLocalUnicast
= srcaddr
&& dstaddr
&&
4266 !mDNSAddrIsDNSMulticast(dstaddr
) && AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
4268 if (!InterfaceID
&& dstaddr
&& mDNSAddrIsDNSMulticast(dstaddr
))
4270 LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
4271 "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)",
4272 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
4273 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
4274 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
4275 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
4276 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
4280 verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
4281 "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
4282 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
4283 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
4284 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
4285 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
4286 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
4288 responseend
= ProcessQuery(m
, msg
, end
, srcaddr
, InterfaceID
,
4289 !mDNSSameIPPort(srcport
, MulticastDNSPort
), mDNSAddrIsDNSMulticast(dstaddr
), QueryWasLocalUnicast
, &m
->omsg
);
4291 if (responseend
) // If responseend is non-null, that means we built a unicast response packet
4293 debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
4294 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
4295 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
4296 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s",
4297 srcaddr
, mDNSVal16(srcport
), InterfaceID
, srcaddr
->type
);
4298 mDNSSendDNSMessage(m
, &m
->omsg
, responseend
, InterfaceID
, srcaddr
, srcport
, mDNSNULL
, mDNSNULL
);
4302 mDNSlocal mDNSBool
TrustedSource(const mDNS
*const m
, const mDNSAddr
*const srcaddr
)
4306 (void)srcaddr
; // Unused
4307 for (s
= m
->DNSServers
; s
; s
= s
->next
)
4308 if (mDNSSameAddress(srcaddr
, &s
->addr
)) return(mDNStrue
);
4312 mDNSlocal
const DNSQuestion
*ExpectingUnicastResponseForQuestion(const mDNS
*const m
, const mDNSOpaque16 id
, const DNSQuestion
*const question
)
4315 for (q
= m
->Questions
; q
; q
=q
->next
)
4316 if (mDNSSameOpaque16(q
->TargetQID
, id
) &&
4317 q
->qtype
== question
->qtype
&&
4318 q
->qclass
== question
->qclass
&&
4319 q
->qnamehash
== question
->qnamehash
&&
4320 SameDomainName(&q
->qname
, &question
->qname
))
4325 mDNSlocal mDNSBool
ExpectingUnicastResponseForRecord(mDNS
*const m
, const mDNSAddr
*const srcaddr
, const mDNSBool SrcLocal
, const mDNSOpaque16 id
, const CacheRecord
*const rr
)
4329 for (q
= m
->Questions
; q
; q
=q
->next
)
4330 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4332 if (!mDNSOpaque16IsZero(q
->TargetQID
))
4334 // For now we don't do this check -- for LLQ updates, the ID doesn't seem to match the ID in the question
4335 // if (mDNSSameOpaque16(q->TargetQID, id)
4337 if (mDNSSameAddress(srcaddr
, &q
->Target
)) return(mDNStrue
);
4338 if (mDNSSameOpaque16(q
->TargetQID
, id
)) return(mDNStrue
);
4339 // if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking
4340 if (TrustedSource(m
, srcaddr
)) return(mDNStrue
);
4341 LogOperation("WARNING: Ignoring suspect uDNS response for %##s (%s) %#a from %#a: %s",
4342 q
->qname
.c
, DNSTypeName(q
->qtype
), &q
->Target
, srcaddr
, CRDisplayString(m
, rr
));
4348 if (SrcLocal
&& q
->ExpectUnicastResp
&& (mDNSu32
)(m
->timenow
- q
->ExpectUnicastResp
) < (mDNSu32
)(mDNSPlatformOneSecond
*2))
4355 mDNSexport CacheRecord
*CreateNewCacheEntry(mDNS
*const m
, const mDNSu32 slot
, CacheGroup
*cg
)
4357 CacheRecord
*rr
= mDNSNULL
;
4359 // Certain data types need more space for in-memory storage than their in-packet rdlength would imply
4360 // Currently this applies only to rdata types containing more than one domainname,
4361 // or types where the domainname is not the last item in the structure
4363 switch (m
->rec
.r
.resrec
.rrtype
)
4365 case kDNSType_SOA
: RDLength
= sizeof(rdataSOA
); break;
4366 case kDNSType_RP
: RDLength
= sizeof(rdataRP
); break;
4367 case kDNSType_PX
: RDLength
= sizeof(rdataPX
); break;
4368 default: RDLength
= m
->rec
.r
.resrec
.rdlength
; break;
4371 //if (RDLength > InlineCacheRDSize)
4372 // LogOperation("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r));
4374 if (!cg
) cg
= GetCacheGroup(m
, slot
, &m
->rec
.r
.resrec
); // If we don't have a CacheGroup for this name, make one now
4375 if (cg
) rr
= GetCacheRecord(m
, cg
, RDLength
); // Make a cache record, being careful not to recycle cg
4376 if (!rr
) NoCacheAnswer(m
, &m
->rec
.r
);
4379 RData
*saveptr
= rr
->resrec
.rdata
; // Save the rr->resrec.rdata pointer
4380 *rr
= m
->rec
.r
; // Block copy the CacheRecord object
4381 rr
->resrec
.rdata
= saveptr
; // Restore rr->resrec.rdata after the structure assignment
4382 rr
->resrec
.name
= cg
->name
; // And set rr->resrec.name to point into our CacheGroup header
4384 // If this is an oversized record with external storage allocated, copy rdata to external storage
4385 if (rr
->resrec
.rdata
== (RData
*)&rr
->rdatastorage
&& RDLength
> InlineCacheRDSize
)
4386 LogMsg("rr->resrec.rdata == &rr->rdatastorage but length > InlineCacheRDSize %##s", m
->rec
.r
.resrec
.name
->c
);
4387 else if (rr
->resrec
.rdata
!= (RData
*)&rr
->rdatastorage
&& RDLength
<= InlineCacheRDSize
)
4388 LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m
->rec
.r
.resrec
.name
->c
);
4389 if (RDLength
> InlineCacheRDSize
)
4390 mDNSPlatformMemCopy(rr
->resrec
.rdata
, m
->rec
.r
.resrec
.rdata
, sizeofRDataHeader
+ RDLength
);
4392 rr
->next
= mDNSNULL
; // Clear 'next' pointer
4393 *(cg
->rrcache_tail
) = rr
; // Append this record to tail of cache slot list
4394 cg
->rrcache_tail
= &(rr
->next
); // Advance tail pointer
4395 if (rr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
)
4396 rr
->DelayDelivery
= NonZeroTime(m
->timenow
);
4397 else if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
&& // If marked unique,
4398 rr
->resrec
.rdata
->MaxRDLength
!= 0) // and non-negative, assume we may have
4399 rr
->DelayDelivery
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
); // to delay delivery of this 'add' event
4401 rr
->DelayDelivery
= CheckForSoonToExpireRecords(m
, rr
->resrec
.name
, rr
->resrec
.namehash
, slot
);
4403 CacheRecordAdd(m
, rr
); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us
4408 mDNSlocal
void RefreshCacheRecord(mDNS
*const m
, CacheRecord
*rr
, mDNSu32 ttl
)
4410 rr
->TimeRcvd
= m
->timenow
;
4411 rr
->resrec
.rroriginalttl
= ttl
;
4412 rr
->UnansweredQueries
= 0;
4413 rr
->MPUnansweredQ
= 0;
4414 rr
->MPUnansweredKA
= 0;
4415 rr
->MPExpectingKA
= mDNSfalse
;
4416 SetNextCacheCheckTime(m
, rr
);
4419 mDNSexport
void GrantCacheExtensions(mDNS
*const m
, DNSQuestion
*q
, mDNSu32 lease
)
4422 const mDNSu32 slot
= HashSlot(&q
->qname
);
4423 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
4424 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4425 if (rr
->CRActiveQuestion
== q
)
4427 //LogOperation("GrantCacheExtensions: new lease %d / %s", lease, CRDisplayString(m, rr));
4428 RefreshCacheRecord(m
, rr
, lease
);
4432 mDNSlocal mDNSu32
GetEffectiveTTL(const uDNS_LLQType LLQType
, mDNSu32 ttl
) // TTL in seconds
4434 if (LLQType
== uDNS_LLQ_Entire
) ttl
= kLLQ_DefLease
;
4435 else if (LLQType
== uDNS_LLQ_Events
)
4437 // If the TTL is -1 for uDNS LLQ event packet, that means "remove"
4438 if (ttl
== 0xFFFFFFFF) ttl
= 0;
4439 else ttl
= kLLQ_DefLease
;
4441 else // else not LLQ (standard uDNS response)
4443 // The TTL is already capped to a maximum value in GetLargeResourceRecord, but just to be extra safe we
4444 // also do this check here to make sure we can't get integer overflow below
4445 if (ttl
> 0x8000000UL
) ttl
= 0x8000000UL
;
4447 // Adjustment factor to avoid race condition:
4448 // 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.
4449 // If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another
4450 // 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox.
4451 // To avoid this, we extend the record's effective TTL to give it a little extra grace period.
4452 // We adjust the 100 second TTL to 126. This means that when we do our 80% query at 101 seconds,
4453 // the cached copy at our local caching server will already have expired, so the server will be forced
4454 // to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds.
4457 // For mDNS, TTL zero means "delete this record"
4458 // For uDNS, TTL zero means: this data is true at this moment, but don't cache it.
4459 // For the sake of network efficiency, we impose a minimum effective TTL of 15 seconds.
4460 // If we allow a TTL of less than 2 seconds things really break (e.g. we end up making a negative cache entry).
4461 // In the future we may want to revisit this and consider properly supporting non-cached (TTL=0) uDNS answers.
4462 if (ttl
< 15) ttl
= 15;
4468 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
4469 // the record list and/or question list.
4470 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4471 mDNSlocal
void mDNSCoreReceiveResponse(mDNS
*const m
,
4472 const DNSMessage
*const response
, const mDNSu8
*end
,
4473 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
4474 const mDNSInterfaceID InterfaceID
)
4477 mDNSBool ResponseMCast
= dstaddr
&& mDNSAddrIsDNSMulticast(dstaddr
);
4478 mDNSBool ResponseSrcLocal
= !srcaddr
|| AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
4479 uDNS_LLQType LLQType
= uDNS_recvLLQResponse(m
, response
, end
, srcaddr
, srcport
);
4481 // "(CacheRecord*)1" is a special (non-zero) end-of-list marker
4482 // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList
4483 // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling.
4484 CacheRecord
*CacheFlushRecords
= (CacheRecord
*)1;
4485 CacheRecord
**cfp
= &CacheFlushRecords
;
4487 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
4488 // to guard against spoof responses, then the only credible protection against that is cryptographic
4489 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
4490 int totalrecords
= response
->h
.numAnswers
+ response
->h
.numAuthorities
+ response
->h
.numAdditionals
;
4491 const mDNSu8
*ptr
= response
->data
;
4493 // Currently used only for display in debugging message
4497 debugf("Received Response from %#-15a addressed to %#-15a on %p with "
4498 "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s LLQType %d",
4499 srcaddr
, dstaddr
, InterfaceID
,
4500 response
->h
.numQuestions
, response
->h
.numQuestions
== 1 ? ", " : "s,",
4501 response
->h
.numAnswers
, response
->h
.numAnswers
== 1 ? ", " : "s,",
4502 response
->h
.numAuthorities
, response
->h
.numAuthorities
== 1 ? "y, " : "ies,",
4503 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s", LLQType
);
4505 if (LLQType
== uDNS_LLQ_Ignore
) return;
4507 // 1. We ignore questions (if any) in mDNS response packets
4508 // 2. If this is an LLQ response, we handle it much the same
4509 // 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this
4510 // answer as being the authoritative complete RRSet, and respond by deleting all other
4511 // matching cache records that don't appear in this packet.
4512 // Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged
4513 if (ResponseMCast
|| LLQType
== uDNS_LLQ_Events
|| (response
->h
.flags
.b
[0] & kDNSFlag0_TC
))
4514 ptr
= LocateAnswers(response
, end
);
4515 // Otherwise, for one-shot queries, any answers in our cache that are not also contained
4516 // in this response packet are immediately deemed to be invalid.
4519 // We could possibly combine this with the similar loop at the end of this function --
4520 // instead of tagging cache records here and then rescuing them if we find them in the answer section,
4521 // we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in
4522 // which it was received (or refreshed), and then at the end if we find any cache records which
4523 // answer questions in this packet's question section, but which aren't tagged with this packet's
4524 // packet number, then we deduce they are old and delete them
4525 for (i
= 0; i
< response
->h
.numQuestions
&& ptr
&& ptr
< end
; i
++)
4528 ptr
= getQuestion(response
, ptr
, end
, InterfaceID
, &q
);
4529 if (ptr
&& ExpectingUnicastResponseForQuestion(m
, response
->h
.id
, &q
))
4532 const mDNSu32 slot
= HashSlot(&q
.qname
);
4533 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
.qnamehash
, &q
.qname
);
4534 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4535 if (SameNameRecordAnswersQuestion(&rr
->resrec
, &q
))
4537 //LogMsg("uDNS marking %s", CRDisplayString(m, rr));
4538 // Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm
4539 rr
->TimeRcvd
= m
->timenow
- TicksTTL(rr
) - 1;
4540 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4546 for (i
= 0; i
< totalrecords
&& ptr
&& ptr
< end
; i
++)
4548 // All responses sent via LL multicast are acceptable for caching
4549 // All responses received over our outbound TCP connections are acceptable for caching
4550 mDNSBool AcceptableResponse
= ResponseMCast
|| !dstaddr
|| LLQType
;
4551 // (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer
4552 // to any specific question -- any code reading records from the cache needs to make that determination for itself.)
4554 const mDNSu8 RecordType
= (mDNSu8
)((i
< response
->h
.numAnswers
) ? kDNSRecordTypePacketAns
: kDNSRecordTypePacketAdd
);
4555 ptr
= GetLargeResourceRecord(m
, response
, ptr
, end
, InterfaceID
, RecordType
, &m
->rec
);
4556 if (!ptr
) goto exit
; // Break out of the loop and clean up our CacheFlushRecords list before exiting
4557 // Don't want to cache OPT or TSIG pseudo-RRs
4558 if (m
->rec
.r
.resrec
.rrtype
== kDNSType_OPT
|| m
->rec
.r
.resrec
.rrtype
== kDNSType_TSIG
)
4559 { m
->rec
.r
.resrec
.RecordType
= 0; continue; }
4561 // When we receive uDNS LLQ responses, we assume a long cache lifetime --
4562 // In the case of active LLQs, we'll get remove events when the records actually do go away
4563 // In the case of polling LLQs, we assume the record remains valid until the next poll
4564 if (!mDNSOpaque16IsZero(response
->h
.id
))
4565 m
->rec
.r
.resrec
.rroriginalttl
= GetEffectiveTTL(LLQType
, m
->rec
.r
.resrec
.rroriginalttl
);
4567 // If response was not sent via LL multicast,
4568 // then see if it answers a recent query of ours, which would also make it acceptable for caching.
4569 if (!AcceptableResponse
) AcceptableResponse
= ExpectingUnicastResponseForRecord(m
, srcaddr
, ResponseSrcLocal
, response
->h
.id
, &m
->rec
.r
);
4571 // 1. Check that this packet resource record does not conflict with any of ours
4572 if (mDNSOpaque16IsZero(response
->h
.id
))
4574 if (m
->CurrentRecord
)
4575 LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
4576 m
->CurrentRecord
= m
->ResourceRecords
;
4577 while (m
->CurrentRecord
)
4579 AuthRecord
*rr
= m
->CurrentRecord
;
4580 m
->CurrentRecord
= rr
->next
;
4581 // We accept all multicast responses, and unicast responses resulting from queries we issued
4582 // For other unicast responses, this code accepts them only for responses with an
4583 // (apparently) local source address that pertain to a record of our own that's in probing state
4584 if (!AcceptableResponse
&& !(ResponseSrcLocal
&& rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)) continue;
4585 if (PacketRRMatchesSignature(&m
->rec
.r
, rr
)) // If interface, name, type (if shared record) and class match...
4587 // ... check to see if type and rdata are identical
4588 if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
&& SameRData(&m
->rec
.r
.resrec
, &rr
->resrec
))
4590 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
4591 if (m
->rec
.r
.resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/2 || m
->SleepState
)
4593 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
4594 if (rr
->ImmedAnswer
== InterfaceID
) { rr
->ImmedAnswer
= mDNSNULL
; rr
->ImmedUnicast
= mDNSfalse
; }
4598 if (rr
->ImmedAnswer
== mDNSNULL
) { rr
->ImmedAnswer
= InterfaceID
; m
->NextScheduledResponse
= m
->timenow
; }
4599 else if (rr
->ImmedAnswer
!= InterfaceID
) { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
4602 // else, the packet RR has different type or different rdata -- check to see if this is a conflict
4603 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0 && PacketRRConflict(m
, rr
, &m
->rec
.r
))
4605 debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr
-> resrec
.rdatahash
, ARDisplayString(m
, rr
));
4606 debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m
->rec
.r
.resrec
.rdatahash
, CRDisplayString(m
, &m
->rec
.r
));
4608 // If this record is marked DependentOn another record for conflict detection purposes,
4609 // then *that* record has to be bumped back to probing state to resolve the conflict
4610 while (rr
->DependentOn
) rr
= rr
->DependentOn
;
4612 // If we've just whacked this record's ProbeCount, don't need to do it again
4613 if (rr
->ProbeCount
<= DefaultProbeCountForTypeUnique
)
4615 // If we'd previously verified this record, put it back to probing state and try again
4616 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
)
4618 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4619 rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
4620 rr
->ProbeCount
= DefaultProbeCountForTypeUnique
+ 1;
4621 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(kDNSRecordTypeUnique
);
4622 InitializeLastAPTime(m
, rr
);
4623 RecordProbeFailure(m
, rr
); // Repeated late conflicts also cause us to back off to the slower probing rate
4625 // If we're probing for this record, we just failed
4626 else if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
4628 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4629 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
4631 // We assumed this record must be unique, but we were wrong.
4632 // (e.g. There are two mDNSResponders on the same machine giving
4633 // different answers for the reverse mapping record.)
4634 // This is simply a misconfiguration, and we don't try to recover from it.
4635 else if (rr
->resrec
.RecordType
== kDNSRecordTypeKnownUnique
)
4637 debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
4638 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4639 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
4642 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
4643 rr
->resrec
.RecordType
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4646 // Else, matching signature, different type or rdata, but not a considered a conflict.
4647 // If the packet record has the cache-flush bit set, then we check to see if we
4648 // have any record(s) of the same type that we should re-assert to rescue them
4649 // (see note about "multi-homing and bridged networks" at the end of this function).
4650 else if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
)
4651 if ((m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) && m
->timenow
- rr
->LastMCTime
> mDNSPlatformOneSecond
/2)
4652 { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
4657 // 2. See if we want to add this packet resource record to our cache
4658 // We only try to cache answers if we have a cache to put them in
4659 // Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query
4660 if (m
->rrcache_size
&& AcceptableResponse
)
4662 const mDNSu32 slot
= HashSlot(m
->rec
.r
.resrec
.name
);
4663 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &m
->rec
.r
.resrec
);
4665 // 2a. Check if this packet resource record is already in our cache
4666 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4668 // If we found this exact resource record, refresh its TTL
4669 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalSameNameRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
4671 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
4672 verbosedebugf("Found record size %5d interface %p already in cache: %s",
4673 m
->rec
.r
.resrec
.rdlength
, InterfaceID
, CRDisplayString(m
, &m
->rec
.r
));
4674 rr
->TimeRcvd
= m
->timenow
;
4676 if (m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
4678 // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
4679 if (rr
->NextInCFList
== mDNSNULL
&& cfp
!= &rr
->NextInCFList
&& LLQType
!= uDNS_LLQ_Events
)
4680 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
4682 // If this packet record is marked unique, and our previous cached copy was not, then fix it
4683 if (!(rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))
4686 for (q
= m
->Questions
; q
; q
=q
->next
) if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) q
->UniqueAnswers
++;
4687 rr
->resrec
.RecordType
= m
->rec
.r
.resrec
.RecordType
;
4691 if (!mDNSPlatformMemSame(m
->rec
.r
.resrec
.rdata
->u
.data
, rr
->resrec
.rdata
->u
.data
, m
->rec
.r
.resrec
.rdlength
))
4693 // If the rdata of the packet record differs in name capitalization from the record in our cache
4694 // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
4695 // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
4696 rr
->resrec
.rroriginalttl
= 0;
4697 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4698 SetNextCacheCheckTime(m
, rr
);
4699 // DO NOT break out here -- we want to continue as if we never found it
4701 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0)
4703 //if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr));
4704 RefreshCacheRecord(m
, rr
, m
->rec
.r
.resrec
.rroriginalttl
);
4709 // If the packet TTL is zero, that means we're deleting this record.
4710 // To give other hosts on the network a chance to protest, we push the deletion
4711 // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
4712 // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
4713 // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
4714 rr
->resrec
.rroriginalttl
= 1;
4715 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4716 SetNextCacheCheckTime(m
, rr
);
4722 // If packet resource record not in our cache, add it now
4723 // (unless it is just a deletion of a record we never had, in which case we don't care)
4724 if (!rr
&& m
->rec
.r
.resrec
.rroriginalttl
> 0)
4726 rr
= CreateNewCacheEntry(m
, slot
, cg
);
4727 if (rr
&& (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) && LLQType
!= uDNS_LLQ_Events
)
4728 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
4731 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4735 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4737 // If we've just received one or more records with their cache flush bits set,
4738 // then scan that cache slot to see if there are any old stale records we need to flush
4739 while (CacheFlushRecords
!= (CacheRecord
*)1)
4741 CacheRecord
*r1
= CacheFlushRecords
, *r2
;
4742 const mDNSu32 slot
= HashSlot(r1
->resrec
.name
);
4743 const CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &r1
->resrec
);
4744 CacheFlushRecords
= CacheFlushRecords
->NextInCFList
;
4745 r1
->NextInCFList
= mDNSNULL
;
4747 // Look for records in the cache with the same signature as this new one with the cache flush
4748 // bit set, and either (a) if they're fresh, just make sure the whole RRSet has the same TTL
4749 // (as required by DNS semantics) or (b) if they're old, mark them for deletion in one second.
4750 // We make these TTL adjustments *only* for records that still have *more* than one second
4751 // remaining to live. Otherwise, a record that we tagged for deletion half a second ago
4752 // (and now has half a second remaining) could inadvertently get its life extended, by either
4753 // (a) if we got an explicit goodbye packet half a second ago, the record would be considered
4754 // "fresh" and would be incorrectly resurrected back to the same TTL as the rest of the RRSet,
4755 // or (b) otherwise, the record would not be fully resurrected, but would be reset to expire
4756 // in one second, thereby inadvertently delaying its actual expiration, instead of hastening it.
4757 // If this were to happen repeatedly, the record's expiration could be deferred indefinitely.
4758 // To avoid this, we need to ensure that the cache flushing operation will only act to
4759 // *decrease* a record's remaining lifetime, never *increase* it. If a record has less than
4760 // one second to go, we simply leave it alone, and leave it to expire at its assigned time.
4761 for (r2
= cg
? cg
->members
: mDNSNULL
; r2
; r2
=r2
->next
)
4762 if (r1
->resrec
.InterfaceID
== r2
->resrec
.InterfaceID
&&
4763 r1
->resrec
.rrtype
== r2
->resrec
.rrtype
&&
4764 r1
->resrec
.rrclass
== r2
->resrec
.rrclass
)
4765 if (RRExpireTime(r2
) - m
->timenow
> mDNSPlatformOneSecond
)
4767 // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
4768 // else, if record is old, mark it to be flushed
4769 if (m
->timenow
- r2
->TimeRcvd
< mDNSPlatformOneSecond
)
4771 // If we find mismatched TTLs in an RRSet, correct them.
4772 // We only do this for records with a TTL of 2 or higher. It's possible to have a
4773 // goodbye announcement with the cache flush bit set (or a case change on record rdata,
4774 // which we treat as a goodbye followed by an addition) and in that case it would be
4775 // inappropriate to synchronize all the other records to a TTL of 0 (or 1).
4776 // We suppress the message for the specific case of correcting from 240 to 60 for type TXT,
4777 // because certain early Bonjour devices are known to have this specific mismatch, and
4778 // there's no point filling syslog with messages about something we already know about.
4779 // We also don't log this for uDNS responses, since a caching name server is obliged
4780 // to give us an aged TTL to correct for how long it has held the record,
4781 // so our received TTLs are expected to vary in that case
4782 if (r2
->resrec
.rroriginalttl
!= r1
->resrec
.rroriginalttl
&& r1
->resrec
.rroriginalttl
> 1)
4784 if (!(r2
->resrec
.rroriginalttl
== 240 && r1
->resrec
.rroriginalttl
== 60 && r2
->resrec
.rrtype
== kDNSType_TXT
) &&
4785 mDNSOpaque16IsZero(response
->h
.id
))
4786 LogOperation("Correcting TTL from %4d to %4d for %s",
4787 r2
->resrec
.rroriginalttl
, r1
->resrec
.rroriginalttl
, CRDisplayString(m
, r2
));
4788 r2
->resrec
.rroriginalttl
= r1
->resrec
.rroriginalttl
;
4791 else // else, if record is old, mark it to be flushed
4793 verbosedebugf("Cache flush %p X %p %s", r1
, r2
, CRDisplayString(m
, r2
));
4794 // We set stale records to expire in one second.
4795 // This gives the owner a chance to rescue it if necessary.
4796 // This is important in the case of multi-homing and bridged networks:
4797 // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
4798 // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
4799 // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
4800 // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
4801 // By delaying the deletion by one second, we give X a change to notice that this bridging has
4802 // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
4803 // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
4804 // final expiration queries for this record.
4805 r2
->resrec
.rroriginalttl
= 1;
4806 r2
->UnansweredQueries
= MaxUnansweredQueries
;
4808 r2
->TimeRcvd
= m
->timenow
;
4809 SetNextCacheCheckTime(m
, r2
);
4811 if (r1
->DelayDelivery
) // If we were planning to delay delivery of this record, see if we still need to
4813 // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
4814 r1
->DelayDelivery
= CheckForSoonToExpireRecords(m
, r1
->resrec
.name
, r1
->resrec
.namehash
, slot
);
4815 if (!r1
->DelayDelivery
) CacheRecordDeferredAdd(m
, r1
);
4819 // See if we need to generate negative cache entries for unanswered unicast questions
4820 ptr
= response
->data
;
4821 for (i
= 0; i
< response
->h
.numQuestions
&& ptr
&& ptr
< end
; i
++)
4824 ptr
= getQuestion(response
, ptr
, end
, InterfaceID
, &q
);
4825 if (ptr
&& ExpectingUnicastResponseForQuestion(m
, response
->h
.id
, &q
))
4827 CacheRecord
*rr
, *neg
= mDNSNULL
;
4828 mDNSu32 slot
= HashSlot(&q
.qname
);
4829 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
.qnamehash
, &q
.qname
);
4830 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4831 if (SameNameRecordAnswersQuestion(&rr
->resrec
, &q
))
4833 // 1. If we got a fresh answer to this query, then don't need to generate a negative entry
4834 if (rr
->TimeRcvd
+ TicksTTL(rr
) - m
->timenow
> 0) break;
4835 // 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
4836 if (rr
->resrec
.RecordType
== kDNSRecordTypePacketNegative
) neg
= rr
;
4841 // We start off assuming a negative caching TTL of 60 seconds
4842 // but then look to see if we can find an SOA authority record to tell us a better value we should be using
4843 mDNSu32 negttl
= 60;
4845 const domainname
*name
= &q
.qname
;
4846 mDNSu32 hash
= q
.qnamehash
;
4848 // If we're going to make (or update) a negative entry, then look for the appropriate TTL from the SOA record
4849 if (response
->h
.numAuthorities
&& (ptr
= LocateAuthorities(response
, end
)) != mDNSNULL
)
4851 ptr
= GetLargeResourceRecord(m
, response
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAuth
, &m
->rec
);
4852 if (ptr
&& m
->rec
.r
.resrec
.rrtype
== kDNSType_SOA
)
4854 mDNSu32 ttl_s
= m
->rec
.r
.resrec
.rroriginalttl
< m
->rec
.r
.resrec
.rdata
->u
.soa
.min
?
4855 m
->rec
.r
.resrec
.rroriginalttl
: m
->rec
.r
.resrec
.rdata
->u
.soa
.min
;
4856 if (negttl
< ttl_s
) negttl
= ttl_s
;
4858 // Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer,
4859 // with an Authority Section SOA record for d.com, then this is a hint that the authority
4860 // is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either.
4861 // To do this we set the repeat count so the while loop below will make a series of negative cache entries for us
4862 if (q
.qtype
== kDNSType_SOA
)
4864 int qcount
= CountLabels(&q
.qname
);
4865 int scount
= CountLabels(m
->rec
.r
.resrec
.name
);
4866 if (qcount
- 1 > scount
)
4867 if (SameDomainName(SkipLeadingLabels(&q
.qname
, qcount
- scount
), m
->rec
.r
.resrec
.name
))
4868 repeat
= qcount
- 1 - scount
;
4871 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4874 // If we already had a negative entry in the cache, then we double our existing negative TTL. This is to avoid
4875 // the case where the record doesn't exist (e.g. particularly for things like our lb._dns-sd._udp.<domain> query),
4876 // and the server returns no SOA record (or an SOA record with a small MIN TTL) so we assume a TTL
4877 // of 60 seconds, and we end up polling the server every minute for a record that doesn't exist.
4878 // With this fix in place, when this happens, we double the effective TTL each time (up to one hour),
4879 // so that we back off our polling rate and don't keep hitting the server continually.
4882 if (negttl
< neg
->resrec
.rroriginalttl
* 2)
4883 negttl
= neg
->resrec
.rroriginalttl
* 2;
4888 negttl
= GetEffectiveTTL(LLQType
, negttl
); // Add 25% grace period if necessary
4890 // If we already had a negative cache entry just update it, else make one or more new negative cache entries
4893 LogOperation("Renewing negative TTL from %d to %d %s", neg
->resrec
.rroriginalttl
, negttl
, CRDisplayString(m
, neg
));
4894 RefreshCacheRecord(m
, neg
, negttl
);
4898 LogOperation("mDNSCoreReceiveResponse making negative cache entry TTL %d for %##s (%s)", negttl
, name
->c
, DNSTypeName(q
.qtype
));
4899 MakeNegativeCacheRecord(m
, name
, hash
, q
.qtype
, q
.qclass
, negttl
);
4900 CreateNewCacheEntry(m
, slot
, cg
);
4901 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4904 name
= (const domainname
*)(name
->c
+ 1 + name
->c
[0]);
4905 hash
= DomainNameHashValue(name
);
4906 slot
= HashSlot(name
);
4907 cg
= CacheGroupForName(m
, slot
, hash
, name
);
4914 mDNSexport
void MakeNegativeCacheRecord(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const mDNSu16 rrtype
, const mDNSu16 rrclass
, mDNSu32 ttl_seconds
)
4916 // Create empty resource record
4917 m
->rec
.r
.resrec
.RecordType
= kDNSRecordTypePacketNegative
;
4918 m
->rec
.r
.resrec
.InterfaceID
= mDNSInterface_Any
;
4919 m
->rec
.r
.resrec
.name
= name
; // Will be updated to point to cg->name when we call CreateNewCacheEntry
4920 m
->rec
.r
.resrec
.rrtype
= rrtype
;
4921 m
->rec
.r
.resrec
.rrclass
= rrclass
;
4922 m
->rec
.r
.resrec
.rroriginalttl
= ttl_seconds
;
4923 m
->rec
.r
.resrec
.rdlength
= 0;
4924 m
->rec
.r
.resrec
.rdestimate
= 0;
4925 m
->rec
.r
.resrec
.namehash
= namehash
;
4926 m
->rec
.r
.resrec
.rdatahash
= 0;
4927 m
->rec
.r
.resrec
.rdata
= (RData
*)&m
->rec
.r
.rdatastorage
;
4928 m
->rec
.r
.resrec
.rdata
->MaxRDLength
= m
->rec
.r
.resrec
.rdlength
;
4930 m
->rec
.r
.NextInKAList
= mDNSNULL
;
4931 m
->rec
.r
.TimeRcvd
= m
->timenow
;
4932 m
->rec
.r
.DelayDelivery
= 0;
4933 m
->rec
.r
.NextRequiredQuery
= m
->timenow
;
4934 m
->rec
.r
.LastUsed
= m
->timenow
;
4935 m
->rec
.r
.CRActiveQuestion
= mDNSNULL
;
4936 m
->rec
.r
.UnansweredQueries
= 0;
4937 m
->rec
.r
.LastUnansweredTime
= 0;
4938 m
->rec
.r
.MPUnansweredQ
= 0;
4939 m
->rec
.r
.MPLastUnansweredQT
= 0;
4940 m
->rec
.r
.MPUnansweredKA
= 0;
4941 m
->rec
.r
.MPExpectingKA
= mDNSfalse
;
4942 m
->rec
.r
.NextInCFList
= mDNSNULL
;
4945 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, void *const pkt
, const mDNSu8
*const end
,
4946 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, const mDNSIPPort dstport
,
4947 const mDNSInterfaceID InterfaceID
)
4949 mDNSInterfaceID ifid
= InterfaceID
;
4950 DNSMessage
*msg
= (DNSMessage
*)pkt
;
4951 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
4952 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
4953 const mDNSu8 UpdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
4955 mDNSu8
*ptr
= mDNSNULL
;
4956 mDNSBool TLS
= (dstaddr
== (mDNSAddr
*)1); // For debug logs: dstaddr = 0 means TCP; dstaddr = 1 means TLS
4957 if (TLS
) dstaddr
= mDNSNULL
;
4959 #ifndef UNICAST_DISABLED
4960 if (mDNSSameAddress(srcaddr
, &m
->Router
))
4962 if (mDNSSameIPPort(srcport
, NATPMPPort
))
4965 uDNS_ReceiveNATPMPPacket(m
, InterfaceID
, pkt
, (mDNSu16
)(end
- (mDNSu8
*)pkt
));
4969 #ifdef _LEGACY_NAT_TRAVERSAL_
4970 if (mDNSSameIPPort(srcport
, SSDPPort
))
4973 LNT_ConfigureRouterInfo(m
, InterfaceID
, pkt
, (mDNSu16
)(end
- (mDNSu8
*)pkt
));
4980 if ((unsigned)(end
- (mDNSu8
*)pkt
) < sizeof(DNSMessageHeader
)) { LogMsg("DNS Message too short"); return; }
4981 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
4982 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
4983 ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
4984 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
4985 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
4986 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
4987 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
4989 if (!m
) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
4991 // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
4992 // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
4993 if (srcaddr
&& !mDNSAddressIsValid(srcaddr
)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr
); return; }
4997 #ifndef UNICAST_DISABLED
4998 if (!dstaddr
|| (!mDNSAddressIsAllDNSLinkGroup(dstaddr
) && (QR_OP
== StdR
|| QR_OP
== UpdR
)))
4999 if (!mDNSOpaque16IsZero(msg
->h
.id
)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses
5001 ifid
= mDNSInterface_Any
;
5002 if (mDNS_LogLevel
>= MDNS_LOG_VERBOSE_DEBUG
)
5003 DumpPacket(m
, mDNSfalse
, TLS
? "TLS" : !dstaddr
? "TCP" : "UDP", srcaddr
, srcport
, msg
, end
);
5004 uDNS_ReceiveMsg(m
, msg
, end
, srcaddr
, srcport
);
5005 // Note: mDNSCore also needs to get access to received unicast responses
5008 if (QR_OP
== StdQ
) mDNSCoreReceiveQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, ifid
);
5009 else if (QR_OP
== StdR
) mDNSCoreReceiveResponse(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, ifid
);
5010 else if (QR_OP
!= UpdR
)
5012 LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
5013 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1], srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
);
5015 // Packet reception often causes a change to the task list:
5016 // 1. Inbound queries can cause us to need to send responses
5017 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
5018 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
5019 // 4. Response packets that answer questions may cause our client to issue new questions
5023 // ***************************************************************************
5024 #if COMPILER_LIKES_PRAGMA_MARK
5026 #pragma mark - Searcher Functions
5029 // Targets are considered the same if both queries are untargeted, or
5030 // if both are targeted to the same address+port
5031 // (If Target address is zero, TargetPort is undefined)
5032 #define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \
5033 (mDNSSameAddress(&(A)->Target, &(B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort)))
5035 // Note: We explicitly disallow making a public query be a duplicate of a private one. This is to avoid the
5036 // circular deadlock where a client does a query for something like "dns-sd -Q _dns-query-tls._tcp.company.com SRV"
5037 // and we have a key for company.com, so we try to locate the private query server for company.com, which necessarily entails
5038 // doing a standard DNS query for the _dns-query-tls._tcp SRV record for company.com. If we make the latter (public) query
5039 // a duplicate of the former (private) query, then it will block forever waiting for an answer that will never come.
5041 mDNSlocal DNSQuestion
*FindDuplicateQuestion(const mDNS
*const m
, const DNSQuestion
*const question
)
5044 // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
5045 // This prevents circular references, where two questions are each marked as a duplicate of the other.
5046 // Accordingly, we break out of the loop when we get to 'question', because there's no point searching
5047 // further in the list.
5048 for (q
= m
->Questions
; q
&& q
!= question
; q
=q
->next
) // Scan our list for another question
5049 if (q
->InterfaceID
== question
->InterfaceID
&& // with the same InterfaceID,
5050 SameQTarget(q
, question
) && // and same unicast/multicast target settings
5051 q
->qtype
== question
->qtype
&& // type,
5052 q
->qclass
== question
->qclass
&& // class,
5053 q
->LongLived
== question
->LongLived
&& // and long-lived status matches
5054 (!q
->AuthInfo
|| question
->AuthInfo
) && // to avoid deadlock, don't make public query dup of a private one
5055 q
->qnamehash
== question
->qnamehash
&&
5056 SameDomainName(&q
->qname
, &question
->qname
)) // and name
5061 // This is called after a question is deleted, in case other identical questions were being
5062 // suppressed as duplicates
5063 mDNSlocal
void UpdateQuestionDuplicates(mDNS
*const m
, DNSQuestion
*const question
)
5066 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5067 if (q
->DuplicateOf
== question
) // To see if any questions were referencing this as their duplicate
5068 if ((q
->DuplicateOf
= FindDuplicateQuestion(m
, q
)) == mDNSNULL
)
5070 // If q used to be a duplicate, but now is not,
5071 // then inherit the state from the question that's going away
5072 q
->LastQTime
= question
->LastQTime
;
5073 q
->ThisQInterval
= question
->ThisQInterval
;
5074 q
->ExpectUnicastResp
= question
->ExpectUnicastResp
;
5075 q
->LastAnswerPktNum
= question
->LastAnswerPktNum
;
5076 q
->RecentAnswerPkts
= question
->RecentAnswerPkts
;
5077 q
->RequestUnicast
= question
->RequestUnicast
;
5078 q
->LastQTxTime
= question
->LastQTxTime
;
5079 q
->CNAMEReferrals
= question
->CNAMEReferrals
;
5080 q
->nta
= question
->nta
;
5081 q
->servAddr
= question
->servAddr
;
5082 q
->servPort
= question
->servPort
;
5084 q
->state
= question
->state
;
5085 // q->tcp = question->tcp;
5086 q
->origLease
= question
->origLease
;
5087 q
->expire
= question
->expire
;
5088 q
->ntries
= question
->ntries
;
5089 q
->id
= question
->id
;
5091 question
->nta
= mDNSNULL
; // If we've got a GetZoneData in progress, transfer it to the newly active question
5092 // question->tcp = mDNSNULL;
5095 LogOperation("UpdateQuestionDuplicates transferred nta pointer for %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
5096 q
->nta
->ZoneDataContext
= q
;
5099 // Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash
5100 if (question
->tcp
) LogOperation("UpdateQuestionDuplicates did not transfer tcp pointer");
5102 if (question
->state
== LLQ_Established
)
5104 LogOperation("UpdateQuestionDuplicates transferred LLQ state for %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
5105 question
->state
= 0; // Must zero question->state, or mDNS_StopQuery_internal will clean up and cancel our LLQ from the server
5108 SetNextQueryTime(m
,q
);
5112 // lookup a DNS Server, matching by name in split-dns configurations. Result stored in addr parameter if successful
5113 mDNSlocal DNSServer
*GetServerForName(mDNS
*m
, const domainname
*name
)
5115 DNSServer
*curmatch
= mDNSNULL
, *p
;
5116 int curmatchlen
= -1, ncount
= name
? CountLabels(name
) : 0;
5118 for (p
= m
->DNSServers
; p
; p
= p
->next
)
5120 int scount
= CountLabels(&p
->domain
);
5121 if (!p
->del
&& ncount
>= scount
&& scount
> curmatchlen
)
5122 if (SameDomainName(SkipLeadingLabels(name
, ncount
- scount
), &p
->domain
))
5123 { curmatch
= p
; curmatchlen
= scount
; }
5128 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
5129 (mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort)))
5131 // Called in normal client context (lock not held)
5132 mDNSlocal
void LLQNATCallback(mDNS
*m
, NATTraversalInfo
*n
)
5137 LogOperation("LLQNATCallback external address:port %.4a:%u", &n
->ExternalAddress
, mDNSVal16(n
->ExternalPort
));
5138 for (q
= m
->Questions
; q
; q
=q
->next
)
5139 if (ActiveQuestion(q
) && !mDNSOpaque16IsZero(q
->TargetQID
) && q
->LongLived
)
5140 startLLQHandshake(m
, q
); // If ExternalPort is zero, will do StartLLQPolling instead
5144 mDNSexport mStatus
mDNS_StartQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5146 if (question
->Target
.type
&& !ValidQuestionTarget(question
))
5148 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5149 question
->Target
.type
, mDNSVal16(question
->TargetPort
));
5150 question
->Target
.type
= mDNSAddrType_None
;
5153 if (!question
->Target
.type
) // No question->Target specified, so clear TargetPort and TargetQID
5155 question
->TargetPort
= zeroIPPort
;
5156 question
->TargetQID
= zeroID
;
5159 #ifndef UNICAST_DISABLED
5160 // If the client has specified 'kDNSServiceFlagsForceMulticast'
5161 // then we do a multicast query on that interface, even for unicast domains.
5162 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5163 question
->TargetQID
= zeroID
;
5165 question
->TargetQID
= mDNS_NewMessageID(m
);
5167 question
->TargetQID
= zeroID
;
5168 #endif // UNICAST_DISABLED
5170 debugf("mDNS_StartQuery: %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5172 if (m
->rrcache_size
== 0) // Can't do queries if we have no cache space allocated
5173 return(mStatus_NoCache
);
5179 if (!ValidateDomainName(&question
->qname
))
5181 LogMsg("Attempt to start query with invalid qname %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5182 return(mStatus_Invalid
);
5185 // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
5187 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5188 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5192 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5193 question
->qname
.c
, DNSTypeName(question
->qtype
));
5194 return(mStatus_AlreadyRegistered
);
5199 // If this question is referencing a specific interface, verify it exists
5200 if (question
->InterfaceID
&& question
->InterfaceID
!= mDNSInterface_LocalOnly
)
5202 NetworkInterfaceInfo
*intf
;
5203 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5204 if (intf
->InterfaceID
== question
->InterfaceID
) break;
5206 LogMsg("Note: InterfaceID %p for question %##s (%s) not currently found in active interface list",
5207 question
->InterfaceID
, question
->qname
.c
, DNSTypeName(question
->qtype
));
5210 // Note: In the case where we already have the answer to this question in our cache, that may be all the client
5211 // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
5212 // be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval).
5213 // If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate
5214 // that to go out immediately.
5215 question
->next
= mDNSNULL
;
5216 question
->qnamehash
= DomainNameHashValue(&question
->qname
); // MUST do this before FindDuplicateQuestion()
5217 question
->DelayAnswering
= CheckForSoonToExpireRecords(m
, &question
->qname
, question
->qnamehash
, HashSlot(&question
->qname
));
5218 question
->LastQTime
= m
->timenow
;
5219 question
->ThisQInterval
= InitialQuestionInterval
; // MUST be > zero for an active question
5220 question
->ExpectUnicastResp
= 0;
5221 question
->LastAnswerPktNum
= m
->PktNum
;
5222 question
->RecentAnswerPkts
= 0;
5223 question
->CurrentAnswers
= 0;
5224 question
->LargeAnswers
= 0;
5225 question
->UniqueAnswers
= 0;
5226 question
->FlappingInterface1
= mDNSNULL
;
5227 question
->FlappingInterface2
= mDNSNULL
;
5228 question
->AuthInfo
= GetAuthInfoForQuestion(m
, question
); // Must do this before calling FindDuplicateQuestion()
5229 question
->DuplicateOf
= FindDuplicateQuestion(m
, question
);
5230 question
->NextInDQList
= mDNSNULL
;
5231 question
->SendQNow
= mDNSNULL
;
5232 question
->SendOnAll
= mDNSfalse
;
5233 question
->RequestUnicast
= 0;
5234 question
->LastQTxTime
= m
->timenow
;
5235 question
->CNAMEReferrals
= 0;
5237 question
->qDNSServer
= mDNSNULL
;
5238 question
->nta
= mDNSNULL
;
5239 question
->servAddr
= zeroAddr
;
5240 question
->servPort
= zeroIPPort
;
5241 question
->tcp
= mDNSNULL
;
5242 question
->NoAnswer
= NoAnswer_Normal
;
5244 question
->state
= LLQ_InitialRequest
;
5245 question
->origLease
= 0;
5246 question
->expire
= 0;
5247 question
->ntries
= 0;
5248 question
->id
= zeroOpaque64
;
5250 if (question
->DuplicateOf
) question
->AuthInfo
= question
->DuplicateOf
->AuthInfo
;
5252 for (i
=0; i
<DupSuppressInfoSize
; i
++)
5253 question
->DupSuppress
[i
].InterfaceID
= mDNSNULL
;
5255 if (!question
->DuplicateOf
)
5256 debugf("mDNS_StartQuery: Question %##s (%s) %p %d (%p) started",
5257 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
,
5258 question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
);
5260 debugf("mDNS_StartQuery: Question %##s (%s) %p %d (%p) duplicate of (%p)",
5261 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
,
5262 question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
, question
->DuplicateOf
);
5264 if (question
->InterfaceID
== mDNSInterface_LocalOnly
)
5266 if (!m
->NewLocalOnlyQuestions
) m
->NewLocalOnlyQuestions
= question
;
5270 if (!m
->NewQuestions
) m
->NewQuestions
= question
;
5272 // If the question's id is non-zero, then it's Wide Area
5273 // MUST NOT do this Wide Area setup until near the end of
5274 // mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA,
5275 // NS, etc.) and if we haven't finished setting up our own question and setting
5276 // m->NewQuestions if necessary then we could end up recursively re-entering
5277 // this routine with the question list data structures in an inconsistent state.
5278 if (!mDNSOpaque16IsZero(question
->TargetQID
))
5280 question
->qDNSServer
= GetServerForName(m
, &question
->qname
);
5281 ActivateUnicastQuery(m
, question
);
5283 // If long-lived query, and we don't have our NAT mapping active, start it now
5284 if (question
->LongLived
&& !m
->LLQNAT
.clientContext
)
5286 m
->LLQNAT
.Protocol
= NATOp_MapUDP
;
5287 m
->LLQNAT
.IntPort
= m
->UnicastPort4
;
5288 m
->LLQNAT
.RequestedPort
= m
->UnicastPort4
;
5289 m
->LLQNAT
.clientCallback
= LLQNATCallback
;
5290 m
->LLQNAT
.clientContext
= (void*)1; // Means LLQ NAT Traversal is active
5291 mDNS_StartNATOperation_internal(m
, &m
->LLQNAT
);
5294 SetNextQueryTime(m
,question
);
5297 return(mStatus_NoError
);
5301 // CancelGetZoneData is an internal routine (i.e. must be called with the lock already held)
5302 mDNSexport
void CancelGetZoneData(mDNS
*const m
, ZoneData
*nta
)
5304 LogOperation("CancelGetZoneData %##s (%s)", nta
->question
.qname
.c
, DNSTypeName(nta
->question
.qtype
));
5305 mDNS_StopQuery_internal(m
, &nta
->question
);
5306 mDNSPlatformMemFree(nta
);
5309 mDNSexport mStatus
mDNS_StopQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5311 const mDNSu32 slot
= HashSlot(&question
->qname
);
5312 CacheGroup
*cg
= CacheGroupForName(m
, slot
, question
->qnamehash
, &question
->qname
);
5314 DNSQuestion
**q
= &m
->Questions
;
5316 //LogOperation("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5318 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5319 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5320 if (*q
) *q
= (*q
)->next
;
5324 if (question
->ThisQInterval
>= 0) // Only log error message if the query was supposed to be active
5326 LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
5327 question
->qname
.c
, DNSTypeName(question
->qtype
));
5331 return(mStatus_BadReferenceErr
);
5334 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
5335 UpdateQuestionDuplicates(m
, question
);
5336 // But don't trash ThisQInterval until afterwards.
5337 question
->ThisQInterval
= -1;
5339 // If there are any cache records referencing this as their active question, then see if there is any
5340 // other question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
5341 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5343 if (rr
->CRActiveQuestion
== question
)
5346 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5347 if (ActiveQuestion(q
) && ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
5349 debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s", q
, CRDisplayString(m
,rr
));
5350 rr
->CRActiveQuestion
= q
; // Question used to be active; new value may or may not be null
5351 if (!q
) m
->rrcache_active
--; // If no longer active, decrement rrcache_active count
5355 // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv() is about to look at,
5356 // bump its pointer forward one question.
5357 if (m
->CurrentQuestion
== question
)
5359 debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
5360 question
->qname
.c
, DNSTypeName(question
->qtype
));
5361 m
->CurrentQuestion
= question
->next
;
5364 if (m
->NewQuestions
== question
)
5366 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
5367 question
->qname
.c
, DNSTypeName(question
->qtype
));
5368 m
->NewQuestions
= question
->next
;
5371 if (m
->NewLocalOnlyQuestions
== question
) m
->NewLocalOnlyQuestions
= question
->next
;
5373 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5374 question
->next
= mDNSNULL
;
5376 // LogMsg("mDNS_StopQuery_internal: Question %##s (%s) removed", question->qname.c, DNSTypeName(question->qtype));
5378 // And finally, cancel any associated GetZoneData operation that's still running.
5379 // Must not do this until last, because there's a good chance the GetZoneData question is the next in the list,
5380 // so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already
5381 // invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary
5382 // *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query.
5383 if (question
->nta
) { CancelGetZoneData(m
, question
->nta
); question
->nta
= mDNSNULL
; }
5384 if (question
->tcp
) { DisposeTCPConn(question
->tcp
); question
->tcp
= mDNSNULL
; }
5385 if (!mDNSOpaque16IsZero(question
->TargetQID
) && question
->LongLived
)
5387 // Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal.
5389 for (q
= m
->Questions
; q
; q
=q
->next
)
5390 if (!mDNSOpaque16IsZero(q
->TargetQID
) && q
->LongLived
) break;
5393 if (!m
->LLQNAT
.clientContext
) // Should never happen, but just in case...
5394 LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL");
5397 LogOperation("Stopping LLQNAT");
5398 mDNS_StopNATOperation_internal(m
, &m
->LLQNAT
);
5399 m
->LLQNAT
.clientContext
= mDNSNULL
; // Means LLQ NAT Traversal not running
5403 // If necessary, tell server it can delete this LLQ state
5404 if (question
->state
== LLQ_Established
) sendLLQRefresh(m
, question
, 0);
5407 return(mStatus_NoError
);
5410 mDNSexport mStatus
mDNS_StartQuery(mDNS
*const m
, DNSQuestion
*const question
)
5414 status
= mDNS_StartQuery_internal(m
, question
);
5419 mDNSexport mStatus
mDNS_StopQuery(mDNS
*const m
, DNSQuestion
*const question
)
5423 status
= mDNS_StopQuery_internal(m
, question
);
5428 // Note that mDNS_StopQueryWithRemoves() does not currently implement the full generality of the other APIs
5429 // Specifically, question callbacks invoked as a result of this call cannot themselves make API calls.
5430 // We invoke the callback without using mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback
5431 // specifically to catch and report if the client callback does try to make API calls
5432 mDNSexport mStatus
mDNS_StopQueryWithRemoves(mDNS
*const m
, DNSQuestion
*const question
)
5438 // Check if question is new -- don't want to give remove events for a question we haven't even answered yet
5439 for (qq
= m
->NewQuestions
; qq
; qq
=qq
->next
) if (qq
== question
) break;
5441 status
= mDNS_StopQuery_internal(m
, question
);
5442 if (status
== mStatus_NoError
&& !qq
)
5445 const mDNSu32 slot
= HashSlot(&question
->qname
);
5446 CacheGroup
*const cg
= CacheGroupForName(m
, slot
, question
->qnamehash
, &question
->qname
);
5447 LogOperation("Generating terminal removes for %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5448 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5449 if (rr
->resrec
.RecordType
!= kDNSRecordTypePacketNegative
&& SameNameRecordAnswersQuestion(&rr
->resrec
, question
))
5451 // Don't use mDNS_DropLockBeforeCallback() here, since we don't allow API calls
5452 if (question
->QuestionCallback
)
5453 question
->QuestionCallback(m
, question
, &rr
->resrec
, mDNSfalse
);
5460 mDNSexport mStatus
mDNS_Reconfirm(mDNS
*const m
, CacheRecord
*const cr
)
5464 status
= mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
5465 if (status
== mStatus_NoError
) ReconfirmAntecedents(m
, cr
->resrec
.name
, cr
->resrec
.namehash
, 0);
5470 mDNSexport mStatus
mDNS_ReconfirmByValue(mDNS
*const m
, ResourceRecord
*const rr
)
5472 mStatus status
= mStatus_BadReferenceErr
;
5475 cr
= FindIdenticalRecordInCache(m
, rr
);
5476 debugf("mDNS_ReconfirmByValue: %p %s", cr
, RRDisplayString(m
, rr
));
5477 if (cr
) status
= mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
5478 if (status
== mStatus_NoError
) ReconfirmAntecedents(m
, cr
->resrec
.name
, cr
->resrec
.namehash
, 0);
5483 mDNSexport mStatus
mDNS_StartBrowse(mDNS
*const m
, DNSQuestion
*const question
,
5484 const domainname
*const srv
, const domainname
*const domain
,
5485 const mDNSInterfaceID InterfaceID
, mDNSBool ForceMCast
, mDNSQuestionCallback
*Callback
, void *Context
)
5487 question
->InterfaceID
= InterfaceID
;
5488 question
->Target
= zeroAddr
;
5489 question
->qtype
= kDNSType_PTR
;
5490 question
->qclass
= kDNSClass_IN
;
5491 question
->LongLived
= mDNSfalse
;
5492 question
->ExpectUnique
= mDNSfalse
;
5493 question
->ForceMCast
= ForceMCast
;
5494 question
->ReturnIntermed
= mDNSfalse
;
5495 question
->QuestionCallback
= Callback
;
5496 question
->QuestionContext
= Context
;
5497 if (!ConstructServiceName(&question
->qname
, mDNSNULL
, srv
, domain
)) return(mStatus_BadParamErr
);
5499 #ifndef UNICAST_DISABLED
5500 if (Question_uDNS(question
))
5502 question
->LongLived
= mDNStrue
;
5503 question
->ThisQInterval
= InitialQuestionInterval
;
5504 question
->LastQTime
= m
->timenow
- question
->ThisQInterval
;
5506 #endif // UNICAST_DISABLED
5507 return(mDNS_StartQuery(m
, question
));
5510 mDNSlocal mDNSBool
MachineHasActiveIPv6(mDNS
*const m
)
5512 NetworkInterfaceInfo
*intf
;
5513 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5514 if (intf
->ip
.type
== mDNSAddrType_IPv6
) return(mDNStrue
);
5518 mDNSlocal
void FoundServiceInfoSRV(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
5520 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5521 mDNSBool PortChanged
= !mDNSSameIPPort(query
->info
->port
, answer
->rdata
->u
.srv
.port
);
5522 if (!AddRecord
) return;
5523 if (answer
->rrtype
!= kDNSType_SRV
) return;
5525 query
->info
->port
= answer
->rdata
->u
.srv
.port
;
5527 // If this is our first answer, then set the GotSRV flag and start the address query
5530 query
->GotSRV
= mDNStrue
;
5531 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5532 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5533 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5534 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5535 mDNS_StartQuery(m
, &query
->qAv4
);
5536 // Only do the AAAA query if this machine actually has IPv6 active
5537 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5539 // If this is not our first answer, only re-issue the address query if the target host name has changed
5540 else if ((query
->qAv4
.InterfaceID
!= query
->qSRV
.InterfaceID
&& query
->qAv4
.InterfaceID
!= answer
->InterfaceID
) ||
5541 !SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
))
5543 mDNS_StopQuery(m
, &query
->qAv4
);
5544 if (query
->qAv6
.ThisQInterval
>= 0) mDNS_StopQuery(m
, &query
->qAv6
);
5545 if (SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
) && !PortChanged
)
5547 // If we get here, it means:
5548 // 1. This is not our first SRV answer
5549 // 2. The interface ID is different, but the target host and port are the same
5550 // This implies that we're seeing the exact same SRV record on more than one interface, so we should
5551 // make our address queries at least as broad as the original SRV query so that we catch all the answers.
5552 query
->qAv4
.InterfaceID
= query
->qSRV
.InterfaceID
; // Will be mDNSInterface_Any, or a specific interface
5553 query
->qAv6
.InterfaceID
= query
->qSRV
.InterfaceID
;
5557 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5558 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5559 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5560 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5562 debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query
->qAv4
.qname
.c
, DNSTypeName(query
->qAv4
.qtype
));
5563 mDNS_StartQuery(m
, &query
->qAv4
);
5564 // Only do the AAAA query if this machine actually has IPv6 active
5565 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5567 else if (query
->ServiceInfoQueryCallback
&& query
->GotADD
&& query
->GotTXT
&& PortChanged
)
5569 if (++query
->Answers
>= 100)
5570 debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
5571 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.srv
.target
.c
,
5572 mDNSVal16(answer
->rdata
->u
.srv
.port
));
5573 query
->ServiceInfoQueryCallback(m
, query
);
5575 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5576 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5579 mDNSlocal
void FoundServiceInfoTXT(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
5581 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5582 if (!AddRecord
) return;
5583 if (answer
->rrtype
!= kDNSType_TXT
) return;
5584 if (answer
->rdlength
> sizeof(query
->info
->TXTinfo
)) return;
5586 query
->GotTXT
= mDNStrue
;
5587 query
->info
->TXTlen
= answer
->rdlength
;
5588 query
->info
->TXTinfo
[0] = 0; // In case answer->rdlength is zero
5589 mDNSPlatformMemCopy(query
->info
->TXTinfo
, answer
->rdata
->u
.txt
.c
, answer
->rdlength
);
5591 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query
->info
->name
.c
, query
->GotADD
);
5593 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5594 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5595 if (query
->ServiceInfoQueryCallback
&& query
->GotADD
)
5597 if (++query
->Answers
>= 100)
5598 debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
5599 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.txt
.c
);
5600 query
->ServiceInfoQueryCallback(m
, query
);
5604 mDNSlocal
void FoundServiceInfo(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, QC_result AddRecord
)
5606 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5607 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
5608 if (!AddRecord
) return;
5610 if (answer
->rrtype
== kDNSType_A
)
5612 query
->info
->ip
.type
= mDNSAddrType_IPv4
;
5613 query
->info
->ip
.ip
.v4
= answer
->rdata
->u
.ipv4
;
5615 else if (answer
->rrtype
== kDNSType_AAAA
)
5617 query
->info
->ip
.type
= mDNSAddrType_IPv6
;
5618 query
->info
->ip
.ip
.v6
= answer
->rdata
->u
.ipv6
;
5622 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer
->name
->c
, answer
->rrtype
, DNSTypeName(answer
->rrtype
));
5626 query
->GotADD
= mDNStrue
;
5627 query
->info
->InterfaceID
= answer
->InterfaceID
;
5629 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query
->info
->ip
.type
, query
->info
->name
.c
, query
->GotTXT
);
5631 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5632 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5633 if (query
->ServiceInfoQueryCallback
&& query
->GotTXT
)
5635 if (++query
->Answers
>= 100)
5636 debugf(answer
->rrtype
== kDNSType_A
?
5637 "**** WARNING **** have given %lu answers for %##s (A) %.4a" :
5638 "**** WARNING **** have given %lu answers for %##s (AAAA) %.16a",
5639 query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.data
);
5640 query
->ServiceInfoQueryCallback(m
, query
);
5644 // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
5645 // If the query is not interface-specific, then InterfaceID may be zero
5646 // Each time the Callback is invoked, the remainder of the fields will have been filled in
5647 // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
5648 mDNSexport mStatus
mDNS_StartResolveService(mDNS
*const m
,
5649 ServiceInfoQuery
*query
, ServiceInfo
*info
, mDNSServiceInfoQueryCallback
*Callback
, void *Context
)
5654 query
->qSRV
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5655 query
->qSRV
.InterfaceID
= info
->InterfaceID
;
5656 query
->qSRV
.Target
= zeroAddr
;
5657 AssignDomainName(&query
->qSRV
.qname
, &info
->name
);
5658 query
->qSRV
.qtype
= kDNSType_SRV
;
5659 query
->qSRV
.qclass
= kDNSClass_IN
;
5660 query
->qSRV
.LongLived
= mDNSfalse
;
5661 query
->qSRV
.ExpectUnique
= mDNStrue
;
5662 query
->qSRV
.ForceMCast
= mDNSfalse
;
5663 query
->qSRV
.ReturnIntermed
= mDNSfalse
;
5664 query
->qSRV
.QuestionCallback
= FoundServiceInfoSRV
;
5665 query
->qSRV
.QuestionContext
= query
;
5667 query
->qTXT
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5668 query
->qTXT
.InterfaceID
= info
->InterfaceID
;
5669 query
->qTXT
.Target
= zeroAddr
;
5670 AssignDomainName(&query
->qTXT
.qname
, &info
->name
);
5671 query
->qTXT
.qtype
= kDNSType_TXT
;
5672 query
->qTXT
.qclass
= kDNSClass_IN
;
5673 query
->qTXT
.LongLived
= mDNSfalse
;
5674 query
->qTXT
.ExpectUnique
= mDNStrue
;
5675 query
->qTXT
.ForceMCast
= mDNSfalse
;
5676 query
->qTXT
.ReturnIntermed
= mDNSfalse
;
5677 query
->qTXT
.QuestionCallback
= FoundServiceInfoTXT
;
5678 query
->qTXT
.QuestionContext
= query
;
5680 query
->qAv4
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5681 query
->qAv4
.InterfaceID
= info
->InterfaceID
;
5682 query
->qAv4
.Target
= zeroAddr
;
5683 query
->qAv4
.qname
.c
[0] = 0;
5684 query
->qAv4
.qtype
= kDNSType_A
;
5685 query
->qAv4
.qclass
= kDNSClass_IN
;
5686 query
->qAv4
.LongLived
= mDNSfalse
;
5687 query
->qAv4
.ExpectUnique
= mDNStrue
;
5688 query
->qAv4
.ForceMCast
= mDNSfalse
;
5689 query
->qAv4
.ReturnIntermed
= mDNSfalse
;
5690 query
->qAv4
.QuestionCallback
= FoundServiceInfo
;
5691 query
->qAv4
.QuestionContext
= query
;
5693 query
->qAv6
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
5694 query
->qAv6
.InterfaceID
= info
->InterfaceID
;
5695 query
->qAv6
.Target
= zeroAddr
;
5696 query
->qAv6
.qname
.c
[0] = 0;
5697 query
->qAv6
.qtype
= kDNSType_AAAA
;
5698 query
->qAv6
.qclass
= kDNSClass_IN
;
5699 query
->qAv6
.LongLived
= mDNSfalse
;
5700 query
->qAv6
.ExpectUnique
= mDNStrue
;
5701 query
->qAv6
.ForceMCast
= mDNSfalse
;
5702 query
->qAv6
.ReturnIntermed
= mDNSfalse
;
5703 query
->qAv6
.QuestionCallback
= FoundServiceInfo
;
5704 query
->qAv6
.QuestionContext
= query
;
5706 query
->GotSRV
= mDNSfalse
;
5707 query
->GotTXT
= mDNSfalse
;
5708 query
->GotADD
= mDNSfalse
;
5712 query
->ServiceInfoQueryCallback
= Callback
;
5713 query
->ServiceInfoQueryContext
= Context
;
5715 // info->name = Must already be set up by client
5716 // info->interface = Must already be set up by client
5717 info
->ip
= zeroAddr
;
5718 info
->port
= zeroIPPort
;
5721 // We use mDNS_StartQuery_internal here because we're already holding the lock
5722 status
= mDNS_StartQuery_internal(m
, &query
->qSRV
);
5723 if (status
== mStatus_NoError
) status
= mDNS_StartQuery_internal(m
, &query
->qTXT
);
5724 if (status
!= mStatus_NoError
) mDNS_StopResolveService(m
, query
);
5730 mDNSexport
void mDNS_StopResolveService (mDNS
*const m
, ServiceInfoQuery
*q
)
5733 // We use mDNS_StopQuery_internal here because we're already holding the lock
5734 if (q
->qSRV
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qSRV
);
5735 if (q
->qTXT
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qTXT
);
5736 if (q
->qAv4
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qAv4
);
5737 if (q
->qAv6
.ThisQInterval
>= 0) mDNS_StopQuery_internal(m
, &q
->qAv6
);
5741 mDNSexport mStatus
mDNS_GetDomains(mDNS
*const m
, DNSQuestion
*const question
, mDNS_DomainType DomainType
, const domainname
*dom
,
5742 const mDNSInterfaceID InterfaceID
, mDNSQuestionCallback
*Callback
, void *Context
)
5744 question
->InterfaceID
= InterfaceID
;
5745 question
->Target
= zeroAddr
;
5746 question
->qtype
= kDNSType_PTR
;
5747 question
->qclass
= kDNSClass_IN
;
5748 question
->LongLived
= mDNSfalse
;
5749 question
->ExpectUnique
= mDNSfalse
;
5750 question
->ForceMCast
= mDNSfalse
;
5751 question
->ReturnIntermed
= mDNSfalse
;
5752 question
->QuestionCallback
= Callback
;
5753 question
->QuestionContext
= Context
;
5754 if (DomainType
> mDNS_DomainTypeMax
) return(mStatus_BadParamErr
);
5755 if (!MakeDomainNameFromDNSNameString(&question
->qname
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
5756 if (!dom
) dom
= &localdomain
;
5757 if (!AppendDomainName(&question
->qname
, dom
)) return(mStatus_BadParamErr
);
5758 return(mDNS_StartQuery(m
, question
));
5761 // ***************************************************************************
5762 #if COMPILER_LIKES_PRAGMA_MARK
5764 #pragma mark - Responder Functions
5767 mDNSexport mStatus
mDNS_Register(mDNS
*const m
, AuthRecord
*const rr
)
5771 status
= mDNS_Register_internal(m
, rr
);
5776 mDNSexport mStatus
mDNS_Update(mDNS
*const m
, AuthRecord
*const rr
, mDNSu32 newttl
,
5777 const mDNSu16 newrdlength
, RData
*const newrdata
, mDNSRecordUpdateCallback
*Callback
)
5779 #ifndef UNICAST_DISABLED
5780 mDNSBool unicast
= !(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(rr
->resrec
.name
));
5782 mDNSBool unicast
= mDNSfalse
;
5785 if (!ValidateRData(rr
->resrec
.rrtype
, newrdlength
, newrdata
))
5787 LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr
->resrec
, &newrdata
->u
, m
->MsgBuffer
));
5788 return(mStatus_Invalid
);
5793 // If TTL is unspecified, leave TTL unchanged
5794 if (newttl
== 0) newttl
= rr
->resrec
.rroriginalttl
;
5796 // If we already have an update queued up which has not gone through yet,
5797 // give the client a chance to free that memory
5798 if (!unicast
&& rr
->NewRData
)
5800 RData
*n
= rr
->NewRData
;
5801 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
5802 if (rr
->UpdateCallback
)
5803 rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
5806 rr
->NewRData
= newrdata
;
5807 rr
->newrdlength
= newrdlength
;
5808 rr
->UpdateCallback
= Callback
;
5810 if (unicast
) { mStatus status
= uDNS_UpdateRecord(m
, rr
); mDNS_Unlock(m
); return(status
); }
5812 if (rr
->resrec
.rroriginalttl
== newttl
&&
5813 rr
->resrec
.rdlength
== newrdlength
&& mDNSPlatformMemSame(rr
->resrec
.rdata
->u
.data
, newrdata
->u
.data
, newrdlength
))
5814 CompleteRDataUpdate(m
, rr
);
5818 domainname type
, domain
;
5819 DeconstructServiceName(rr
->resrec
.name
, &name
, &type
, &domain
);
5820 rr
->AnnounceCount
= InitialAnnounceCount
;
5821 // iChat often does suprious record updates where no data has changed. For the _presence service type, using
5822 // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
5823 // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
5824 // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
5825 // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
5826 if (SameDomainLabel(type
.c
, (mDNSu8
*)"\x6_ichat")) rr
->AnnounceCount
= 1;
5827 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
5828 InitializeLastAPTime(m
, rr
);
5829 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
5830 if (!rr
->UpdateBlocked
&& rr
->UpdateCredits
) rr
->UpdateCredits
--;
5831 if (!rr
->NextUpdateCredit
) rr
->NextUpdateCredit
= NonZeroTime(m
->timenow
+ kUpdateCreditRefreshInterval
);
5832 if (rr
->AnnounceCount
> rr
->UpdateCredits
+ 1) rr
->AnnounceCount
= (mDNSu8
)(rr
->UpdateCredits
+ 1);
5833 if (rr
->UpdateCredits
<= 5)
5835 mDNSu32 delay
= 6 - rr
->UpdateCredits
; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
5836 if (!rr
->UpdateBlocked
) rr
->UpdateBlocked
= NonZeroTime(m
->timenow
+ (mDNSs32
)delay
* mDNSPlatformOneSecond
);
5837 rr
->ThisAPInterval
*= 4;
5838 rr
->LastAPTime
= rr
->UpdateBlocked
- rr
->ThisAPInterval
;
5839 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s",
5840 rr
->resrec
.name
->c
, delay
, delay
> 1 ? "s" : "");
5842 rr
->resrec
.rroriginalttl
= newttl
;
5846 return(mStatus_NoError
);
5849 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
5850 // the record list and/or question list.
5851 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
5852 mDNSexport mStatus
mDNS_Deregister(mDNS
*const m
, AuthRecord
*const rr
)
5856 status
= mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
5861 // Circular reference: AdvertiseInterface references mDNS_HostNameCallback, which calls mDNS_SetFQDN, which call AdvertiseInterface
5862 mDNSlocal
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
);
5864 mDNSlocal NetworkInterfaceInfo
*FindFirstAdvertisedInterface(mDNS
*const m
)
5866 NetworkInterfaceInfo
*intf
;
5867 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5868 if (intf
->Advertise
) break;
5872 mDNSlocal
void AdvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
5874 char buffer
[MAX_REVERSE_MAPPING_NAME
];
5875 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
5876 if (!primary
) primary
= set
; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
5878 // Send dynamic update for non-linklocal IPv4 Addresses
5879 mDNS_SetupResourceRecord(&set
->RR_A
, mDNSNULL
, set
->InterfaceID
, kDNSType_A
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNS_HostNameCallback
, set
);
5880 mDNS_SetupResourceRecord(&set
->RR_PTR
, mDNSNULL
, set
->InterfaceID
, kDNSType_PTR
, kHostNameTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
5881 mDNS_SetupResourceRecord(&set
->RR_HINFO
, mDNSNULL
, set
->InterfaceID
, kDNSType_HINFO
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
5883 #if ANSWER_REMOTE_HOSTNAME_QUERIES
5884 set
->RR_A
.AllowRemoteQuery
= mDNStrue
;
5885 set
->RR_PTR
.AllowRemoteQuery
= mDNStrue
;
5886 set
->RR_HINFO
.AllowRemoteQuery
= mDNStrue
;
5888 // 1. Set up Address record to map from host name ("foo.local.") to IP address
5889 // 2. Set up reverse-lookup PTR record to map from our address back to our host name
5890 AssignDomainName(&set
->RR_A
.namestorage
, &m
->MulticastHostname
);
5891 if (set
->ip
.type
== mDNSAddrType_IPv4
)
5893 set
->RR_A
.resrec
.rrtype
= kDNSType_A
;
5894 set
->RR_A
.resrec
.rdata
->u
.ipv4
= set
->ip
.ip
.v4
;
5895 // Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
5896 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.",
5897 set
->ip
.ip
.v4
.b
[3], set
->ip
.ip
.v4
.b
[2], set
->ip
.ip
.v4
.b
[1], set
->ip
.ip
.v4
.b
[0]);
5899 else if (set
->ip
.type
== mDNSAddrType_IPv6
)
5902 set
->RR_A
.resrec
.rrtype
= kDNSType_AAAA
;
5903 set
->RR_A
.resrec
.rdata
->u
.ipv6
= set
->ip
.ip
.v6
;
5904 for (i
= 0; i
< 16; i
++)
5906 static const char hexValues
[] = "0123456789ABCDEF";
5907 buffer
[i
* 4 ] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] & 0x0F];
5908 buffer
[i
* 4 + 1] = '.';
5909 buffer
[i
* 4 + 2] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] >> 4];
5910 buffer
[i
* 4 + 3] = '.';
5912 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
5915 MakeDomainNameFromDNSNameString(&set
->RR_PTR
.namestorage
, buffer
);
5916 set
->RR_PTR
.AutoTarget
= Target_AutoHost
; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
5917 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
5919 set
->RR_A
.RRSet
= &primary
->RR_A
; // May refer to self
5921 mDNS_Register_internal(m
, &set
->RR_A
);
5922 mDNS_Register_internal(m
, &set
->RR_PTR
);
5924 if (!NO_HINFO
&& m
->HIHardware
.c
[0] > 0 && m
->HISoftware
.c
[0] > 0 && m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0] <= 254)
5926 mDNSu8
*p
= set
->RR_HINFO
.resrec
.rdata
->u
.data
;
5927 AssignDomainName(&set
->RR_HINFO
.namestorage
, &m
->MulticastHostname
);
5928 set
->RR_HINFO
.DependentOn
= &set
->RR_A
;
5929 mDNSPlatformMemCopy(p
, &m
->HIHardware
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
5931 mDNSPlatformMemCopy(p
, &m
->HISoftware
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
5932 mDNS_Register_internal(m
, &set
->RR_HINFO
);
5936 debugf("Not creating HINFO record: platform support layer provided no information");
5937 set
->RR_HINFO
.resrec
.RecordType
= kDNSRecordTypeUnregistered
;
5941 mDNSlocal
void DeadvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
5943 NetworkInterfaceInfo
*intf
;
5945 // If we still have address records referring to this one, update them
5946 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
5947 AuthRecord
*A
= primary
? &primary
->RR_A
: mDNSNULL
;
5948 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5949 if (intf
->RR_A
.RRSet
== &set
->RR_A
)
5950 intf
->RR_A
.RRSet
= A
;
5952 // Unregister these records.
5953 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
5954 // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
5955 // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
5956 // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
5957 if (set
->RR_A
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_A
, mDNS_Dereg_normal
);
5958 if (set
->RR_PTR
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_PTR
, mDNS_Dereg_normal
);
5959 if (set
->RR_HINFO
.resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_HINFO
, mDNS_Dereg_normal
);
5962 mDNSexport
void mDNS_SetFQDN(mDNS
*const m
)
5964 domainname newmname
;
5965 NetworkInterfaceInfo
*intf
;
5969 if (!AppendDomainLabel(&newmname
, &m
->hostlabel
)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
5970 if (!AppendLiteralLabelString(&newmname
, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
5971 if (SameDomainNameCS(&m
->MulticastHostname
, &newmname
)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
5975 AssignDomainName(&m
->MulticastHostname
, &newmname
);
5976 // 1. Stop advertising our address records on all interfaces
5977 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5978 if (intf
->Advertise
) DeadvertiseInterface(m
, intf
);
5980 // 2. Start advertising our address records using the new name
5981 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5982 if (intf
->Advertise
) AdvertiseInterface(m
, intf
);
5984 // 3. Make sure that any SRV records (and the like) that reference our
5985 // host name in their rdata get updated to reference this new host name
5986 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) if (rr
->AutoTarget
) SetTargetToHostName(m
, rr
);
5987 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
) if (rr
->AutoTarget
) SetTargetToHostName(m
, rr
);
5992 mDNSlocal
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
5994 (void)rr
; // Unused parameter
5998 char *msg
= "Unknown result";
5999 if (result
== mStatus_NoError
) msg
= "Name registered";
6000 else if (result
== mStatus_NameConflict
) msg
= "Name conflict";
6001 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6005 if (result
== mStatus_NoError
)
6007 // Notify the client that the host name is successfully registered
6008 if (m
->MainCallback
)
6009 m
->MainCallback(m
, mStatus_NoError
);
6011 else if (result
== mStatus_NameConflict
)
6013 domainlabel oldlabel
= m
->hostlabel
;
6015 // 1. First give the client callback a chance to pick a new name
6016 if (m
->MainCallback
)
6017 m
->MainCallback(m
, mStatus_NameConflict
);
6019 // 2. If the client callback didn't do it, add (or increment) an index ourselves
6020 // This needs to be case-insensitive compare, because we need to know that the name has been changed so as to
6021 // remedy the conflict, and a name that differs only in capitalization will just suffer the exact same conflict again.
6022 if (SameDomainLabelCS(m
->hostlabel
.c
, oldlabel
.c
))
6023 IncrementLabelSuffix(&m
->hostlabel
, mDNSfalse
);
6025 // 3. Generate the FQDNs from the hostlabel,
6026 // and make sure all SRV records, etc., are updated to reference our new hostname
6028 LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel
.c
, m
->hostlabel
.c
);
6030 else if (result
== mStatus_MemFree
)
6032 // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
6033 // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
6034 debugf("mDNS_HostNameCallback: MemFree (ignored)");
6037 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result
, rr
->resrec
.name
->c
);
6040 mDNSlocal
void UpdateInterfaceProtocols(mDNS
*const m
, NetworkInterfaceInfo
*active
)
6042 NetworkInterfaceInfo
*intf
;
6043 active
->IPv4Available
= mDNSfalse
;
6044 active
->IPv6Available
= mDNSfalse
;
6045 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6046 if (intf
->InterfaceID
== active
->InterfaceID
)
6048 if (intf
->ip
.type
== mDNSAddrType_IPv4
&& intf
->McastTxRx
) active
->IPv4Available
= mDNStrue
;
6049 if (intf
->ip
.type
== mDNSAddrType_IPv6
&& intf
->McastTxRx
) active
->IPv6Available
= mDNStrue
;
6053 mDNSexport mStatus
mDNS_RegisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, mDNSBool flapping
)
6056 ServiceRecordSet
*s
;
6057 mDNSBool FirstOfType
= mDNStrue
;
6058 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6060 if (!set
->InterfaceID
)
6061 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set
->ip
); return(mStatus_Invalid
); }
6063 if (!mDNSAddressIsValidNonZero(&set
->mask
))
6064 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set
->ip
, &set
->mask
); return(mStatus_Invalid
); }
6068 // Assume this interface will be active now, unless we find a duplicate already in the list
6069 set
->InterfaceActive
= mDNStrue
;
6070 set
->IPv4Available
= (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
);
6071 set
->IPv6Available
= (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
);
6073 // Scan list to see if this InterfaceID is already represented
6078 LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
6080 return(mStatus_AlreadyRegistered
);
6083 if ((*p
)->InterfaceID
== set
->InterfaceID
)
6085 // This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now
6086 set
->InterfaceActive
= mDNSfalse
;
6087 if (set
->ip
.type
== (*p
)->ip
.type
) FirstOfType
= mDNSfalse
;
6088 if (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
) (*p
)->IPv4Available
= mDNStrue
;
6089 if (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
) (*p
)->IPv6Available
= mDNStrue
;
6095 set
->next
= mDNSNULL
;
6099 AdvertiseInterface(m
, set
);
6101 LogOperation("mDNS_RegisterInterface: InterfaceID %p %s (%#a) %s", set
->InterfaceID
, set
->ifname
, &set
->ip
,
6102 set
->InterfaceActive
?
6103 "not represented in list; marking active and retriggering queries" :
6104 "already represented in list; marking inactive for now");
6106 // In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6107 // giving the false impression that there's an active representative of this interface when there really isn't.
6108 // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
6109 // even if we believe that we previously had an active representative of this interface.
6110 if (set
->McastTxRx
&& ((m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) || FirstOfType
|| set
->InterfaceActive
))
6113 // If flapping, delay between first and second queries is eight seconds instead of one
6114 mDNSs32 delay
= flapping
? mDNSPlatformOneSecond
* 5 : 0;
6115 mDNSu8 announce
= flapping
? (mDNSu8
)1 : InitialAnnounceCount
;
6117 // Use a small amount of randomness:
6118 // In the case of a network administrator turning on an Ethernet hub so that all the
6119 // connected machines establish link at exactly the same time, we don't want them all
6120 // to go and hit the network with identical queries at exactly the same moment.
6121 if (!m
->SuppressSending
) m
->SuppressSending
= m
->timenow
+ (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
6125 LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", set
->ifname
, &set
->ip
);
6126 if (!m
->SuppressProbes
||
6127 m
->SuppressProbes
- (m
->timenow
+ delay
) < 0)
6128 m
->SuppressProbes
= (m
->timenow
+ delay
);
6131 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
6132 if (mDNSOpaque16IsZero(q
->TargetQID
))
6133 if (!q
->InterfaceID
|| q
->InterfaceID
== set
->InterfaceID
) // If non-specific Q, or Q on this specific interface,
6134 { // then reactivate this question
6135 mDNSBool dodelay
= flapping
&& (q
->FlappingInterface1
== set
->InterfaceID
|| q
->FlappingInterface2
== set
->InterfaceID
);
6136 mDNSs32 initial
= dodelay
? InitialQuestionInterval
* QuestionIntervalStep2
: InitialQuestionInterval
;
6137 mDNSs32 qdelay
= dodelay
? mDNSPlatformOneSecond
* 5 : 0;
6138 if (dodelay
) LogOperation("No cache records for expired %##s (%s); okay to delay questions a little", q
->qname
.c
, DNSTypeName(q
->qtype
));
6140 if (!q
->ThisQInterval
|| q
->ThisQInterval
> initial
)
6142 q
->ThisQInterval
= initial
;
6143 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
6145 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
+ qdelay
;
6146 q
->RecentAnswerPkts
= 0;
6147 SetNextQueryTime(m
,q
);
6150 // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
6151 // we now need them to re-probe if necessary, and then re-announce.
6152 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
6153 if (!AuthRecord_uDNS(rr
))
6154 if (!rr
->resrec
.InterfaceID
|| rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6156 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
6157 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
6158 if (rr
->AnnounceCount
< announce
) rr
->AnnounceCount
= announce
;
6159 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6160 InitializeLastAPTime(m
, rr
);
6164 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
6165 if (AuthRecord_uDNS(rr
))
6167 LogOperation("mDNS_RegisterInterface: StartGetZoneData for %##s", rr
->resrec
.name
->c
);
6168 if (rr
->nta
) CancelGetZoneData(m
, rr
->nta
);
6169 rr
->nta
= StartGetZoneData(m
, rr
->resrec
.name
, ZoneServiceUpdate
, RecordRegistrationGotZoneData
, rr
);
6172 for (s
= m
->ServiceRegistrations
; s
; s
= s
->uDNS_next
)
6174 LogOperation("mDNS_RegisterInterface: StartGetZoneData for %##s", s
->RR_SRV
.resrec
.name
->c
);
6175 if (s
->nta
) CancelGetZoneData(m
, s
->nta
);
6176 s
->nta
= StartGetZoneData(m
, s
->RR_SRV
.resrec
.name
, ZoneServiceUpdate
, ServiceRegistrationGotZoneData
, s
);
6180 return(mStatus_NoError
);
6183 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
6184 // the record list and/or question list.
6185 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6186 mDNSexport
void mDNS_DeregisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, mDNSBool flapping
)
6188 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6190 mDNSBool revalidate
= mDNSfalse
;
6191 // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
6192 // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
6193 // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
6194 if (m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) revalidate
= mDNStrue
;
6198 // Find this record in our list
6199 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
6200 if (!*p
) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m
); return; }
6202 // Unlink this record from our list
6204 set
->next
= mDNSNULL
;
6206 if (!set
->InterfaceActive
)
6208 // If this interface not the active member of its set, update the v4/v6Available flags for the active member
6209 NetworkInterfaceInfo
*intf
;
6210 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6211 if (intf
->InterfaceActive
&& intf
->InterfaceID
== set
->InterfaceID
)
6212 UpdateInterfaceProtocols(m
, intf
);
6216 NetworkInterfaceInfo
*intf
;
6217 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6218 if (intf
->InterfaceID
== set
->InterfaceID
)
6222 LogOperation("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;"
6223 " making it active", set
->InterfaceID
, set
->ifname
, &set
->ip
);
6224 intf
->InterfaceActive
= mDNStrue
;
6225 UpdateInterfaceProtocols(m
, intf
);
6227 // See if another representative *of the same type* exists. If not, we mave have gone from
6228 // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
6229 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6230 if (intf
->InterfaceID
== set
->InterfaceID
&& intf
->ip
.type
== set
->ip
.type
)
6232 if (!intf
) revalidate
= mDNStrue
;
6242 LogOperation("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;"
6243 " marking questions etc. dormant", set
->InterfaceID
, set
->ifname
, &set
->ip
);
6246 LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect",
6247 set
->ifname
, &set
->ip
);
6249 // 1. Deactivate any questions specific to this interface, and tag appropriate questions
6250 // so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them
6251 for (q
= m
->Questions
; q
; q
=q
->next
)
6253 if (q
->InterfaceID
== set
->InterfaceID
) q
->ThisQInterval
= 0;
6254 if (!q
->InterfaceID
|| q
->InterfaceID
== set
->InterfaceID
)
6256 q
->FlappingInterface2
= q
->FlappingInterface1
;
6257 q
->FlappingInterface1
= set
->InterfaceID
; // Keep history of the last two interfaces to go away
6261 // 2. Flush any cache records received on this interface
6262 revalidate
= mDNSfalse
; // Don't revalidate if we're flushing the records
6263 FORALL_CACHERECORDS(slot
, cg
, rr
)
6264 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6266 // If this interface is deemed flapping,
6267 // postpone deleting the cache records in case the interface comes back again
6268 if (!flapping
) mDNS_PurgeCacheResourceRecord(m
, rr
);
6271 // We want these record to go away in 30 seconds
6272 // We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them --
6273 // if the interface does come back, any relevant questions will be reactivated anyway
6274 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForFlappingInterface
);
6275 rr
->UnansweredQueries
= MaxUnansweredQueries
;
6279 // 3. Any DNS servers specific to this interface are now unusable
6280 for (s
= m
->DNSServers
; s
; s
= s
->next
)
6281 if (s
->interface
== set
->InterfaceID
)
6283 s
->interface
= mDNSInterface_Any
;
6284 s
->teststate
= DNSServer_Disabled
;
6289 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6290 if (set
->Advertise
) DeadvertiseInterface(m
, set
);
6292 // If we have any cache records received on this interface that went away, then re-verify them.
6293 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6294 // giving the false impression that there's an active representative of this interface when there really isn't.
6295 // Don't need to do this when shutting down, because *all* interfaces are about to go away
6296 if (revalidate
&& !m
->mDNS_shutdown
)
6301 m
->NextCacheCheck
= m
->timenow
;
6302 FORALL_CACHERECORDS(slot
, cg
, rr
)
6303 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6304 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForFlappingInterface
);
6310 mDNSlocal
void ServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6312 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6313 (void)m
; // Unused parameter
6317 char *msg
= "Unknown result";
6318 if (result
== mStatus_NoError
) msg
= "Name Registered";
6319 else if (result
== mStatus_NameConflict
) msg
= "Name Conflict";
6320 else if (result
== mStatus_MemFree
) msg
= "Memory Free";
6321 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6325 // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
6326 if (result
== mStatus_NoError
&& rr
!= &sr
->RR_SRV
) return;
6328 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6329 if (result
== mStatus_NameConflict
)
6331 sr
->Conflict
= mDNStrue
; // Record that this service set had a conflict
6332 mDNS_DeregisterService(m
, sr
); // Unlink the records from our list
6336 if (result
== mStatus_MemFree
)
6338 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
6339 // don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
6341 if (sr
->RR_PTR
.resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6342 for (i
=0; i
<sr
->NumSubTypes
; i
++) if (sr
->SubTypes
[i
].resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6344 // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
6345 // then we can now report the NameConflict to the client
6346 if (sr
->Conflict
) result
= mStatus_NameConflict
;
6349 // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
6350 // function is allowed to do anything, including deregistering this service and freeing its memory.
6351 if (sr
->ServiceCallback
)
6352 sr
->ServiceCallback(m
, sr
, result
);
6355 mDNSlocal
void NSSCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6357 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6358 if (sr
->ServiceCallback
)
6359 sr
->ServiceCallback(m
, sr
, result
);
6362 mDNSlocal mStatus
uDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*srs
)
6365 ServiceRecordSet
**p
= &m
->ServiceRegistrations
;
6366 while (*p
&& *p
!= srs
) p
=&(*p
)->uDNS_next
;
6367 if (*p
) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs
, srs
->RR_SRV
.resrec
.name
->c
); return(mStatus_AlreadyRegistered
); }
6369 srs
->uDNS_next
= mDNSNULL
;
6372 srs
->RR_SRV
.resrec
.rroriginalttl
= kHostNameTTL
;
6373 srs
->RR_TXT
.resrec
.rroriginalttl
= kStandardTTL
;
6374 srs
->RR_PTR
.resrec
.rroriginalttl
= kStandardTTL
;
6375 for (i
= 0; i
< srs
->NumSubTypes
;i
++) srs
->SubTypes
[i
].resrec
.rroriginalttl
= kStandardTTL
;
6377 srs
->srs_uselease
= mDNStrue
;
6379 if (srs
->RR_SRV
.AutoTarget
)
6381 // For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other
6382 // advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate,
6383 // with the port number in our advertised SRV record automatically tracking the external mapped port.
6384 DomainAuthInfo
*AuthInfo
= GetAuthInfoForName_internal(m
, srs
->RR_SRV
.resrec
.name
);
6385 if (!AuthInfo
|| !AuthInfo
->AutoTunnel
) srs
->RR_SRV
.AutoTarget
= Target_AutoHostAndNATMAP
;
6388 if (!GetServiceTarget(m
, srs
))
6390 // defer registration until we've got a target
6391 LogOperation("uDNS_RegisterService - no target for %##s", srs
->RR_SRV
.resrec
.name
->c
);
6392 srs
->state
= regState_NoTarget
;
6393 srs
->nta
= mDNSNULL
;
6394 return mStatus_NoError
;
6397 srs
->state
= regState_FetchingZoneData
;
6398 srs
->nta
= StartGetZoneData(m
, srs
->RR_SRV
.resrec
.name
, ZoneServiceUpdate
, ServiceRegistrationGotZoneData
, srs
);
6399 return srs
->nta
? mStatus_NoError
: mStatus_NoMemoryErr
;
6403 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
6404 // Type is service type (e.g. "_ipp._tcp.")
6405 // Domain is fully qualified domain name (i.e. ending with a null label)
6406 // We always register a TXT, even if it is empty (so that clients are not
6407 // left waiting forever looking for a nonexistent record.)
6408 // If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6409 // then the default host name (m->MulticastHostname) is automatically used
6410 // If the optional target host parameter is set, then the storage it points to must remain valid for the lifetime of the service registration
6411 mDNSexport mStatus
mDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*sr
,
6412 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6413 const domainname
*const host
, mDNSIPPort port
, const mDNSu8 txtinfo
[], mDNSu16 txtlen
,
6414 AuthRecord
*SubTypes
, mDNSu32 NumSubTypes
,
6415 const mDNSInterfaceID InterfaceID
, mDNSServiceCallback Callback
, void *Context
)
6420 sr
->state
= regState_Zero
;
6421 sr
->srs_uselease
= 0;
6422 sr
->TestForSelfConflict
= 0;
6426 sr
->SRSUpdateServer
= zeroAddr
;
6427 sr
->SRSUpdatePort
= zeroIPPort
;
6428 mDNSPlatformMemZero(&sr
->NATinfo
, sizeof(sr
->NATinfo
));
6429 sr
->ClientCallbackDeferred
= 0;
6430 sr
->DeferredStatus
= 0;
6431 sr
->SRVUpdateDeferred
= 0;
6435 sr
->ServiceCallback
= Callback
;
6436 sr
->ServiceContext
= Context
;
6437 sr
->Conflict
= mDNSfalse
;
6439 sr
->Extras
= mDNSNULL
;
6440 sr
->NumSubTypes
= NumSubTypes
;
6441 sr
->SubTypes
= SubTypes
;
6443 // Initialize the AuthRecord objects to sane values
6444 // Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out
6445 mDNS_SetupResourceRecord(&sr
->RR_ADV
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeAdvisory
, ServiceCallback
, sr
);
6446 mDNS_SetupResourceRecord(&sr
->RR_PTR
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6447 mDNS_SetupResourceRecord(&sr
->RR_SRV
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6448 mDNS_SetupResourceRecord(&sr
->RR_TXT
, mDNSNULL
, InterfaceID
, kDNSType_TXT
, kStandardTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6450 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6451 if (mDNSIPPortIsZero(port
))
6452 return(mDNS_RegisterNoSuchService(m
, &sr
->RR_SRV
, name
, type
, domain
, mDNSNULL
, mDNSInterface_Any
, NSSCallback
, sr
));
6454 // If the client is registering an oversized TXT record,
6455 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
6456 if (sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
< txtlen
)
6457 sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
= txtlen
;
6459 // Set up the record names
6460 // For now we only create an advisory record for the main type, not for subtypes
6461 // We need to gain some operational experience before we decide if there's a need to create them for subtypes too
6462 if (ConstructServiceName(&sr
->RR_ADV
.namestorage
, (const domainlabel
*)"\x09_services", (const domainname
*)"\x07_dns-sd\x04_udp", domain
) == mDNSNULL
)
6463 return(mStatus_BadParamErr
);
6464 if (ConstructServiceName(&sr
->RR_PTR
.namestorage
, mDNSNULL
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6465 if (ConstructServiceName(&sr
->RR_SRV
.namestorage
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6466 AssignDomainName(&sr
->RR_TXT
.namestorage
, sr
->RR_SRV
.resrec
.name
);
6468 // 1. Set up the ADV record rdata to advertise our service type
6469 AssignDomainName(&sr
->RR_ADV
.resrec
.rdata
->u
.name
, sr
->RR_PTR
.resrec
.name
);
6471 // 2. Set up the PTR record rdata to point to our service name
6472 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6473 AssignDomainName(&sr
->RR_PTR
.resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6474 sr
->RR_PTR
.Additional1
= &sr
->RR_SRV
;
6475 sr
->RR_PTR
.Additional2
= &sr
->RR_TXT
;
6477 // 2a. Set up any subtype PTRs to point to our service name
6478 // If the client is using subtypes, it is the client's responsibility to have
6479 // already set the first label of the record name to the subtype being registered
6480 for (i
=0; i
<NumSubTypes
; i
++)
6483 AssignDomainName(&st
, sr
->SubTypes
[i
].resrec
.name
);
6484 st
.c
[1+st
.c
[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
6485 AppendDomainName(&st
, type
);
6486 mDNS_SetupResourceRecord(&sr
->SubTypes
[i
], mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6487 if (ConstructServiceName(&sr
->SubTypes
[i
].namestorage
, mDNSNULL
, &st
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6488 AssignDomainName(&sr
->SubTypes
[i
].resrec
.rdata
->u
.name
, &sr
->RR_SRV
.namestorage
);
6489 sr
->SubTypes
[i
].Additional1
= &sr
->RR_SRV
;
6490 sr
->SubTypes
[i
].Additional2
= &sr
->RR_TXT
;
6493 // 3. Set up the SRV record rdata.
6494 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.priority
= 0;
6495 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.weight
= 0;
6496 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
= port
;
6498 // Setting AutoTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6499 if (host
&& host
->c
[0]) AssignDomainName(&sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
, host
);
6500 else { sr
->RR_SRV
.AutoTarget
= Target_AutoHost
; sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
.c
[0] = '\0'; }
6502 // 4. Set up the TXT record rdata,
6503 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6504 if (txtinfo
== mDNSNULL
) sr
->RR_TXT
.resrec
.rdlength
= 0;
6505 else if (txtinfo
!= sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
)
6507 sr
->RR_TXT
.resrec
.rdlength
= txtlen
;
6508 if (sr
->RR_TXT
.resrec
.rdlength
> sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
) return(mStatus_BadParamErr
);
6509 mDNSPlatformMemCopy(sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, txtinfo
, txtlen
);
6511 sr
->RR_TXT
.DependentOn
= &sr
->RR_SRV
;
6513 #ifndef UNICAST_DISABLED
6514 // If the client has specified an explicit InterfaceID,
6515 // then we do a multicast registration on that interface, even for unicast domains.
6516 if (!(InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(&sr
->RR_SRV
.namestorage
)))
6520 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6521 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6522 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6523 // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
6524 if (!sr
->RR_TXT
.resrec
.rdlength
) { sr
->RR_TXT
.resrec
.rdlength
= 1; sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6526 status
= uDNS_RegisterService(m
, sr
);
6532 err
= mDNS_Register_internal(m
, &sr
->RR_SRV
);
6533 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_TXT
);
6534 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
6535 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
6536 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
6537 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
6538 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
6539 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_ADV
);
6540 for (i
=0; i
<NumSubTypes
; i
++) if (!err
) err
= mDNS_Register_internal(m
, &sr
->SubTypes
[i
]);
6541 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_PTR
);
6545 if (err
) mDNS_DeregisterService(m
, sr
);
6549 mDNSlocal
void DummyCallback(mDNS
*const m
, AuthRecord
*rr
, mStatus result
)
6553 (void)result
; // Unused
6554 LogOperation("DummyCallback %d %s", result
, ARDisplayString(m
, rr
));
6557 mDNSexport mStatus
mDNS_AddRecordToService(mDNS
*const m
, ServiceRecordSet
*sr
,
6558 ExtraResourceRecord
*extra
, RData
*rdata
, mDNSu32 ttl
)
6560 ExtraResourceRecord
**e
;
6563 extra
->next
= mDNSNULL
;
6564 mDNS_SetupResourceRecord(&extra
->r
, rdata
, sr
->RR_PTR
.resrec
.InterfaceID
,
6565 extra
->r
.resrec
.rrtype
, ttl
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6566 AssignDomainName(&extra
->r
.namestorage
, sr
->RR_SRV
.resrec
.name
);
6570 while (*e
) e
= &(*e
)->next
;
6572 if (ttl
== 0) ttl
= kStandardTTL
;
6574 extra
->r
.DependentOn
= &sr
->RR_SRV
;
6576 debugf("mDNS_AddRecordToService adding record to %##s %s %d",
6577 extra
->r
.resrec
.name
->c
, DNSTypeName(extra
->r
.resrec
.rrtype
), extra
->r
.resrec
.rdlength
);
6579 status
= mDNS_Register_internal(m
, &extra
->r
);
6580 if (status
== mStatus_NoError
)
6583 #ifndef UNICAST_DISABLED
6584 if (AuthRecord_uDNS(&sr
->RR_SRV
))
6586 extra
->r
.resrec
.RecordType
= kDNSRecordTypeShared
; // don't want it to conflict with the service name (???)
6587 extra
->r
.RecordCallback
= DummyCallback
; // don't generate callbacks for extra RRs for unicast services (WHY NOT????)
6588 if (sr
->state
!= regState_Registered
&& sr
->state
!= regState_Refresh
) extra
->r
.state
= regState_ExtraQueued
;
6597 mDNSexport mStatus
mDNS_RemoveRecordFromService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
,
6598 mDNSRecordCallback MemFreeCallback
, void *Context
)
6600 ExtraResourceRecord
**e
;
6605 while (*e
&& *e
!= extra
) e
= &(*e
)->next
;
6608 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra
->r
.resrec
.name
->c
);
6609 status
= mStatus_BadReferenceErr
;
6613 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra
->r
.resrec
.name
->c
);
6614 extra
->r
.RecordCallback
= MemFreeCallback
;
6615 extra
->r
.RecordContext
= Context
;
6617 status
= mDNS_Deregister_internal(m
, &extra
->r
, mDNS_Dereg_normal
);
6623 mDNSexport mStatus
mDNS_RenameAndReregisterService(mDNS
*const m
, ServiceRecordSet
*const sr
, const domainlabel
*newname
)
6625 // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
6626 // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
6627 domainlabel name1
, name2
;
6628 domainname type
, domain
;
6629 const domainname
*host
= sr
->RR_SRV
.AutoTarget
? mDNSNULL
: &sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
;
6630 ExtraResourceRecord
*extras
= sr
->Extras
;
6633 DeconstructServiceName(sr
->RR_SRV
.resrec
.name
, &name1
, &type
, &domain
);
6637 IncrementLabelSuffix(&name2
, mDNStrue
);
6641 if (SameDomainName(&domain
, &localdomain
))
6642 LogMsg("%##s service renamed from \"%#s\" to \"%#s\"", type
.c
, name1
.c
, newname
->c
);
6643 else LogMsg("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type
.c
, domain
.c
, name1
.c
, newname
->c
);
6645 err
= mDNS_RegisterService(m
, sr
, newname
, &type
, &domain
,
6646 host
, sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, sr
->RR_TXT
.resrec
.rdlength
,
6647 sr
->SubTypes
, sr
->NumSubTypes
,
6648 sr
->RR_PTR
.resrec
.InterfaceID
, sr
->ServiceCallback
, sr
->ServiceContext
);
6650 // mDNS_RegisterService() just reset sr->Extras to NULL.
6651 // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
6652 // through the old list of extra records, and re-add them to our freshly created service registration
6653 while (!err
&& extras
)
6655 ExtraResourceRecord
*e
= extras
;
6656 extras
= extras
->next
;
6657 err
= mDNS_AddRecordToService(m
, sr
, e
, e
->r
.resrec
.rdata
, e
->r
.resrec
.rroriginalttl
);
6663 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
6664 // which may change the record list and/or question list.
6665 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6666 mDNSexport mStatus
mDNS_DeregisterService(mDNS
*const m
, ServiceRecordSet
*sr
)
6668 // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
6669 if (mDNSIPPortIsZero(sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
)) return(mDNS_DeregisterNoSuchService(m
, &sr
->RR_SRV
));
6671 #ifndef UNICAST_DISABLED
6672 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6676 status
= uDNS_DeregisterService(m
, sr
);
6681 if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeUnregistered
)
6683 debugf("Service set for %##s already deregistered", sr
->RR_SRV
.resrec
.name
->c
);
6684 return(mStatus_BadReferenceErr
);
6686 else if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeDeregistering
)
6688 debugf("Service set for %##s already in the process of deregistering", sr
->RR_SRV
.resrec
.name
->c
);
6689 // Avoid race condition:
6690 // If a service gets a conflict, then we set the Conflict flag to tell us to generate
6691 // an mStatus_NameConflict message when we get the mStatus_MemFree for our PTR record.
6692 // If the client happens to deregister the service in the middle of that process, then
6693 // we clear the flag back to the normal state, so that we deliver a plain mStatus_MemFree
6694 // instead of incorrectly promoting it to mStatus_NameConflict.
6695 // This race condition is exposed particularly when the conformance test generates
6696 // a whole batch of simultaneous conflicts across a range of services all advertised
6697 // using the same system default name, and if we don't take this precaution then
6698 // we end up incrementing m->nicelabel multiple times instead of just once.
6699 // <rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision
6700 sr
->Conflict
= mDNSfalse
;
6701 return(mStatus_NoError
);
6707 ExtraResourceRecord
*e
;
6711 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
6712 // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
6713 mDNS_Deregister_internal(m
, &sr
->RR_SRV
, mDNS_Dereg_repeat
);
6714 mDNS_Deregister_internal(m
, &sr
->RR_TXT
, mDNS_Dereg_repeat
);
6716 mDNS_Deregister_internal(m
, &sr
->RR_ADV
, mDNS_Dereg_normal
);
6718 // We deregister all of the extra records, but we leave the sr->Extras list intact
6719 // in case the client wants to do a RenameAndReregister and reinstate the registration
6722 mDNS_Deregister_internal(m
, &e
->r
, mDNS_Dereg_repeat
);
6726 for (i
=0; i
<sr
->NumSubTypes
; i
++)
6727 mDNS_Deregister_internal(m
, &sr
->SubTypes
[i
], mDNS_Dereg_normal
);
6729 // Be sure to deregister the PTR last!
6730 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
6731 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
6732 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
6733 // we've deregistered all our records and done any other necessary cleanup before that happens.
6734 status
= mDNS_Deregister_internal(m
, &sr
->RR_PTR
, mDNS_Dereg_normal
);
6740 // Create a registration that asserts that no such service exists with this name.
6741 // This can be useful where there is a given function is available through several protocols.
6742 // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
6743 // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
6744 // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
6745 // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
6746 mDNSexport mStatus
mDNS_RegisterNoSuchService(mDNS
*const m
, AuthRecord
*const rr
,
6747 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6748 const domainname
*const host
,
6749 const mDNSInterfaceID InterfaceID
, mDNSRecordCallback Callback
, void *Context
)
6751 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, Callback
, Context
);
6752 if (ConstructServiceName(&rr
->namestorage
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6753 rr
->resrec
.rdata
->u
.srv
.priority
= 0;
6754 rr
->resrec
.rdata
->u
.srv
.weight
= 0;
6755 rr
->resrec
.rdata
->u
.srv
.port
= zeroIPPort
;
6756 if (host
&& host
->c
[0]) AssignDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, host
);
6757 else rr
->AutoTarget
= Target_AutoHost
;
6758 return(mDNS_Register(m
, rr
));
6761 mDNSexport mStatus
mDNS_AdvertiseDomains(mDNS
*const m
, AuthRecord
*rr
,
6762 mDNS_DomainType DomainType
, const mDNSInterfaceID InterfaceID
, char *domname
)
6764 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, mDNSNULL
, mDNSNULL
);
6765 if (!MakeDomainNameFromDNSNameString(&rr
->namestorage
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
6766 if (!MakeDomainNameFromDNSNameString(&rr
->resrec
.rdata
->u
.name
, domname
)) return(mStatus_BadParamErr
);
6767 return(mDNS_Register(m
, rr
));
6770 mDNSOpaque16
mDNS_NewMessageID(mDNS
* const m
)
6772 static mDNSBool randomized
= mDNSfalse
;
6774 if (!randomized
) { m
->NextMessageID
= (mDNSu16
)mDNSRandom(0xFFFF); randomized
= mDNStrue
; }
6775 if (m
->NextMessageID
== 0) m
->NextMessageID
++;
6776 return mDNSOpaque16fromIntVal(m
->NextMessageID
++);
6779 // ***************************************************************************
6780 #if COMPILER_LIKES_PRAGMA_MARK
6782 #pragma mark - Startup and Shutdown
6785 mDNSlocal
void mDNS_GrowCache_internal(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
6787 if (storage
&& numrecords
)
6790 debugf("Adding cache storage for %d more records (%d bytes)", numrecords
, numrecords
*sizeof(CacheEntity
));
6791 for (i
=0; i
<numrecords
; i
++) storage
[i
].next
= &storage
[i
+1];
6792 storage
[numrecords
-1].next
= m
->rrcache_free
;
6793 m
->rrcache_free
= storage
;
6794 m
->rrcache_size
+= numrecords
;
6798 mDNSexport
void mDNS_GrowCache(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
6801 mDNS_GrowCache_internal(m
, storage
, numrecords
);
6805 mDNSexport mStatus
mDNS_Init(mDNS
*const m
, mDNS_PlatformSupport
*const p
,
6806 CacheEntity
*rrcachestorage
, mDNSu32 rrcachesize
,
6807 mDNSBool AdvertiseLocalAddresses
, mDNSCallback
*Callback
, void *Context
)
6813 if (!rrcachestorage
) rrcachesize
= 0;
6817 m
->CanReceiveUnicastOn5353
= mDNSfalse
; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
6818 m
->AdvertiseLocalAddresses
= AdvertiseLocalAddresses
;
6819 m
->mDNSPlatformStatus
= mStatus_Waiting
;
6820 m
->UnicastPort4
= zeroIPPort
;
6821 m
->UnicastPort6
= zeroIPPort
;
6822 m
->MainCallback
= Callback
;
6823 m
->MainContext
= Context
;
6824 m
->rec
.r
.resrec
.RecordType
= 0;
6826 // For debugging: To catch and report locking failures
6828 m
->mDNS_reentrancy
= 0;
6829 m
->mDNS_shutdown
= mDNSfalse
;
6830 m
->lock_rrcache
= 0;
6831 m
->lock_Questions
= 0;
6832 m
->lock_Records
= 0;
6834 // Task Scheduling variables
6835 result
= mDNSPlatformTimeInit();
6836 if (result
!= mStatus_NoError
) return(result
);
6837 m
->timenow_adjust
= (mDNSs32
)mDNSRandom(0xFFFFFFFF);
6838 timenow
= mDNS_TimeNow_NoLock(m
);
6840 m
->timenow
= 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
6841 m
->timenow_last
= timenow
;
6842 m
->NextScheduledEvent
= timenow
;
6843 m
->SuppressSending
= timenow
;
6844 m
->NextCacheCheck
= timenow
+ 0x78000000;
6845 m
->NextScheduledQuery
= timenow
+ 0x78000000;
6846 m
->NextScheduledProbe
= timenow
+ 0x78000000;
6847 m
->NextScheduledResponse
= timenow
+ 0x78000000;
6848 m
->NextScheduledNATOp
= timenow
+ 0x78000000;
6849 m
->RandomQueryDelay
= 0;
6850 m
->RandomReconfirmDelay
= 0;
6852 m
->SendDeregistrations
= mDNSfalse
;
6853 m
->SendImmediateAnswers
= mDNSfalse
;
6854 m
->SleepState
= mDNSfalse
;
6856 // These fields only required for mDNS Searcher...
6857 m
->Questions
= mDNSNULL
;
6858 m
->NewQuestions
= mDNSNULL
;
6859 m
->CurrentQuestion
= mDNSNULL
;
6860 m
->LocalOnlyQuestions
= mDNSNULL
;
6861 m
->NewLocalOnlyQuestions
= mDNSNULL
;
6862 m
->rrcache_size
= 0;
6863 m
->rrcache_totalused
= 0;
6864 m
->rrcache_active
= 0;
6865 m
->rrcache_report
= 10;
6866 m
->rrcache_free
= mDNSNULL
;
6868 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++) m
->rrcache_hash
[slot
] = mDNSNULL
;
6870 mDNS_GrowCache_internal(m
, rrcachestorage
, rrcachesize
);
6872 // Fields below only required for mDNS Responder...
6873 m
->hostlabel
.c
[0] = 0;
6874 m
->nicelabel
.c
[0] = 0;
6875 m
->MulticastHostname
.c
[0] = 0;
6876 m
->HIHardware
.c
[0] = 0;
6877 m
->HISoftware
.c
[0] = 0;
6878 m
->ResourceRecords
= mDNSNULL
;
6879 m
->DuplicateRecords
= mDNSNULL
;
6880 m
->NewLocalRecords
= mDNSNULL
;
6881 m
->CurrentRecord
= mDNSNULL
;
6882 m
->HostInterfaces
= mDNSNULL
;
6883 m
->ProbeFailTime
= 0;
6884 m
->NumFailedProbes
= 0;
6885 m
->SuppressProbes
= 0;
6887 #ifndef UNICAST_DISABLED
6888 m
->NextuDNSEvent
= timenow
+ 0x78000000;
6889 m
->NextSRVUpdate
= timenow
+ 0x78000000;
6890 m
->SuppressStdPort53Queries
= 0;
6892 m
->ServiceRegistrations
= mDNSNULL
;
6893 m
->NextMessageID
= 0;
6894 m
->DNSServers
= mDNSNULL
;
6896 m
->Router
= zeroAddr
;
6897 m
->AdvertisedV4
= zeroAddr
;
6898 m
->AdvertisedV6
= zeroAddr
;
6900 m
->AuthInfoList
= mDNSNULL
;
6902 m
->ReverseMap
.ThisQInterval
= -1;
6903 m
->StaticHostname
.c
[0] = 0;
6905 m
->Hostnames
= mDNSNULL
;
6906 m
->AutoTunnelHostAddr
.b
[0] = 0;
6907 m
->AutoTunnelHostAddrActive
= mDNSfalse
;
6908 m
->AutoTunnelLabel
.c
[0] = 0;
6910 m
->RegisterSearchDomains
= mDNSfalse
;
6912 // NAT traversal fields
6913 m
->NATTraversals
= mDNSNULL
;
6914 m
->CurrentNATTraversal
= mDNSNULL
;
6915 m
->retryIntervalGetAddr
= 0; // delta between time sent and retry
6916 m
->retryGetAddr
= timenow
+ 0x78000000; // absolute time when we retry
6917 m
->ExternalAddress
= zerov4Addr
;
6919 m
->NATMcastRecvskt
= mDNSNULL
;
6920 m
->NATMcastRecvsk2
= mDNSNULL
;
6921 m
->LastNATupseconds
= 0;
6922 m
->LastNATReplyLocalTime
= timenow
;
6924 m
->UPnPInterfaceID
= 0;
6925 m
->UPnPRouterPort
= zeroIPPort
;
6926 m
->UPnPSOAPPort
= zeroIPPort
;
6927 m
->UPnPRouterURL
= mDNSNULL
;
6928 m
->UPnPSOAPURL
= mDNSNULL
;
6929 m
->UPnPRouterAddressString
= mDNSNULL
;
6930 m
->UPnPSOAPAddressString
= mDNSNULL
;
6933 #if APPLE_OSX_mDNSResponder
6934 m
->TunnelClients
= mDNSNULL
;
6937 result
= mDNSPlatformInit(m
);
6939 #ifndef UNICAST_DISABLED
6940 // It's better to do this *after* the platform layer has set up the
6941 // interface list and security credentials
6942 uDNS_SetupDNSConfig(m
); // Get initial DNS configuration
6948 mDNSlocal
void DynDNSHostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6951 debugf("NameStatusCallback: result %d for registration of name %##s", result
, rr
->resrec
.name
->c
);
6952 mDNSPlatformDynDNSHostNameStatusChanged(rr
->resrec
.name
, result
);
6955 mDNSexport mStatus
uDNS_SetupDNSConfig(mDNS
*const m
)
6963 DNSServer
*ptr
, **p
= &m
->DNSServers
;
6964 const DNSServer
*oldServers
= m
->DNSServers
;
6967 if (m
->RegisterSearchDomains
) uDNS_RegisterSearchDomains(m
);
6971 // Let the platform layer get the current DNS information
6972 // The m->RegisterSearchDomains boolean is so that we lazily get the search domain list only on-demand
6973 // (no need to hit the network with domain enumeration queries until we actually need that information).
6974 for (ptr
= m
->DNSServers
; ptr
; ptr
= ptr
->next
) ptr
->del
= mDNStrue
;
6976 mDNSPlatformSetDNSConfig(m
, mDNStrue
, mDNSfalse
, &fqdn
, mDNSNULL
, mDNSNULL
);
6978 // Update our qDNSServer pointers before we go and free the DNSServer object memory
6979 for (q
= m
->Questions
; q
; q
=q
->next
)
6980 if (!mDNSOpaque16IsZero(q
->TargetQID
))
6982 DNSServer
*s
= GetServerForName(m
, &q
->qname
);
6983 DNSServer
*t
= q
->qDNSServer
;
6986 // If DNS Server for this question has changed, reactivate it
6987 LogOperation("Updating DNS Server from %p %#a:%d (%##s) to %p %#a:%d (%##s) for %##s (%s)",
6988 t
, t
? &t
->addr
: mDNSNULL
, mDNSVal16(t
? t
->port
: zeroIPPort
), t
? t
->domain
.c
: (mDNSu8
*)"",
6989 s
, s
? &s
->addr
: mDNSNULL
, mDNSVal16(s
? s
->port
: zeroIPPort
), s
? s
->domain
.c
: (mDNSu8
*)"",
6990 q
->qname
.c
, DNSTypeName(q
->qtype
));
6992 ActivateUnicastQuery(m
, q
);
7000 // Scan our cache, looking for uDNS records that we would have queried this server for.
7001 // We reconfirm any records that match, because in this world of split DNS, firewalls, etc.
7002 // different DNS servers can give different answers to the same question.
7004 ptr
->del
= mDNSfalse
; // Clear del so GetServerForName will (temporarily) find this server again before it's finally deleted
7005 FORALL_CACHERECORDS(slot
, cg
, cr
)
7006 if (!cr
->resrec
.InterfaceID
&& GetServerForName(m
, cr
->resrec
.name
) == ptr
)
7007 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
7009 mDNSPlatformMemFree(ptr
);
7015 // If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs).
7016 // This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless.
7017 // Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour.
7018 // Similarly, if we now have some DNS servers and we used to have none, we want to purge any fake negative results we may have generated.
7019 if ((m
->DNSServers
!= mDNSNULL
) != (oldServers
!= mDNSNULL
))
7022 FORALL_CACHERECORDS(slot
, cg
, cr
) if (!cr
->resrec
.InterfaceID
) { mDNS_PurgeCacheResourceRecord(m
, cr
); count
++; }
7023 LogOperation("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache",
7024 m
->DNSServers
? "DNS server became" : "No DNS servers", count
);
7027 // Did our FQDN change?
7028 if (!SameDomainName(&fqdn
, &m
->FQDN
))
7030 if (m
->FQDN
.c
[0]) mDNS_RemoveDynDNSHostName(m
, &m
->FQDN
);
7032 AssignDomainName(&m
->FQDN
, &fqdn
);
7036 mDNSPlatformDynDNSHostNameStatusChanged(&m
->FQDN
, 1);
7037 mDNS_AddDynDNSHostName(m
, &m
->FQDN
, DynDNSHostNameCallback
, mDNSNULL
);
7043 // handle router and primary interface changes
7044 v4
= v6
= r
= zeroAddr
;
7045 v4
.type
= r
.type
= mDNSAddrType_IPv4
;
7047 if (mDNSPlatformGetPrimaryInterface(m
, &v4
, &v6
, &r
) == mStatus_NoError
&& !mDNSv4AddressIsLinkLocal(&v4
.ip
.v4
))
7049 mDNS_SetPrimaryInterfaceInfo(m
,
7050 !mDNSIPv4AddressIsZero(v4
.ip
.v4
) ? &v4
: mDNSNULL
,
7051 !mDNSIPv6AddressIsZero(v6
.ip
.v6
) ? &v6
: mDNSNULL
,
7052 !mDNSIPv4AddressIsZero(r
.ip
.v4
) ? &r
: mDNSNULL
);
7056 mDNS_SetPrimaryInterfaceInfo(m
, mDNSNULL
, mDNSNULL
, mDNSNULL
);
7057 if (m
->FQDN
.c
[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m
->FQDN
, 1); // Set status to 1 to indicate temporary failure
7060 return mStatus_NoError
;
7063 mDNSexport
void mDNSCoreInitComplete(mDNS
*const m
, mStatus result
)
7065 m
->mDNSPlatformStatus
= result
;
7066 if (m
->MainCallback
)
7069 mDNS_DropLockBeforeCallback(); // Allow client to legally make mDNS API calls from the callback
7070 m
->MainCallback(m
, mStatus_NoError
);
7071 mDNS_ReclaimLockAfterCallback(); // Decrement mDNS_reentrancy to block mDNS API calls again
7076 mDNSexport
void mDNS_Close(mDNS
*const m
)
7078 mDNSu32 rrcache_active
= 0;
7079 mDNSu32 rrcache_totalused
= 0;
7081 NetworkInterfaceInfo
*intf
;
7085 m
->mDNS_shutdown
= mDNStrue
;
7087 #ifndef UNICAST_DISABLED
7089 SleepServiceRegistrations(m
);
7090 while (m
->Hostnames
) mDNS_RemoveDynDNSHostName(m
, &m
->Hostnames
->fqdn
);
7093 rrcache_totalused
= m
->rrcache_totalused
;
7094 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
7096 while (m
->rrcache_hash
[slot
])
7098 CacheGroup
*cg
= m
->rrcache_hash
[slot
];
7101 CacheRecord
*cr
= cg
->members
;
7102 cg
->members
= cg
->members
->next
;
7103 if (cr
->CRActiveQuestion
) rrcache_active
++;
7104 ReleaseCacheRecord(m
, cr
);
7106 cg
->rrcache_tail
= &cg
->members
;
7107 ReleaseCacheGroup(m
, &m
->rrcache_hash
[slot
]);
7110 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused
, rrcache_active
);
7111 if (rrcache_active
!= m
->rrcache_active
)
7112 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active
, m
->rrcache_active
);
7114 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
7115 if (intf
->Advertise
)
7116 DeadvertiseInterface(m
, intf
);
7118 // Shut down all our active NAT Traversals
7119 while (m
->NATTraversals
)
7121 NATTraversalInfo
*t
= m
->NATTraversals
;
7122 mDNS_StopNATOperation_internal(m
, t
); // This will cut 't' from the list, thereby advancing m->NATTraversals in the process
7124 // After stopping the NAT Traversal, we zero out the fields.
7125 // This has particularly important implications for our AutoTunnel records --
7126 // when we deregister our AutoTunnel records below, we don't want their mStatus_MemFree
7127 // handlers to just turn around and attempt to re-register those same records.
7128 // Clearing t->ExternalPort will cause the mStatus_MemFree callback handlers to not do this.
7129 t
->ExternalAddress
= zerov4Addr
;
7130 t
->ExternalPort
= zeroIPPort
;
7132 t
->Result
= mStatus_NoError
;
7135 // Make sure there are nothing but deregistering records remaining in the list
7136 if (m
->CurrentRecord
)
7137 LogMsg("mDNS_Close ERROR m->CurrentRecord already set %s", ARDisplayString(m
, m
->CurrentRecord
));
7139 // First we deregister any non-shared records. In particular, we want to make sure we deregister
7140 // any extra records added to a Service Record Set first, before we deregister its PTR record.
7141 m
->CurrentRecord
= m
->ResourceRecords
;
7142 while (m
->CurrentRecord
)
7144 rr
= m
->CurrentRecord
;
7145 m
->CurrentRecord
= rr
->next
;
7146 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
7147 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
7150 // Now deregister any remaining records we didn't get the first time through
7151 m
->CurrentRecord
= m
->ResourceRecords
;
7152 while (m
->CurrentRecord
)
7154 rr
= m
->CurrentRecord
;
7155 m
->CurrentRecord
= rr
->next
;
7156 if (rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
7157 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
7160 if (m
->ResourceRecords
) LogOperation("mDNS_Close: Sending final packets for deregistering records");
7161 else LogOperation("mDNS_Close: No deregistering records remain");
7163 // If any deregistering records remain, send their deregistration announcements before we exit
7164 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) DiscardDeregistrations(m
);
7165 else if (m
->ResourceRecords
) SendResponses(m
);
7167 for (rr
= m
->ResourceRecords
; rr
; rr
= rr
->next
)
7168 LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m
, rr
));
7171 debugf("mDNS_Close: mDNSPlatformClose");
7172 mDNSPlatformClose(m
);
7173 debugf("mDNS_Close: done");