1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
22 * @APPLE_LICENSE_HEADER_END@
24 * This code is completely 100% portable C. It does not depend on any external header files
25 * from outside the mDNS project -- all the types it expects to find are defined right here.
27 * The previous point is very important: This file does not depend on any external
28 * header files. It should complile on *any* platform that has a C compiler, without
29 * making *any* assumptions about availability of so-called "standard" C functions,
30 * routines, or types (which may or may not be present on any given platform).
33 * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
34 * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
35 * but for the sake of brevity here I will say just this: Curly braces are not syntactially
36 * part of an "if" statement; they are the beginning and ending markers of a compound statement;
37 * therefore common sense dictates that if they are part of a compound statement then they
38 * should be indented to the same level as everything else in that compound statement.
39 * Indenting curly braces at the same level as the "if" implies that curly braces are
40 * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
41 * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
42 * understand why variable y is not of type "char*" just proves the point that poor code
43 * layout leads people to unfortunate misunderstandings about how the C language really works.)
45 Change History (most recent first):
48 Revision 1.535.2.3 2006/11/10 19:36:42 cheshire
49 Further refinement: Only harmonize TTL if the value we're adjusting it to is at least 2 seconds
51 Revision 1.535.2.2 2006/10/31 02:11:26 cheshire
52 Compile error: Need to put back AllDNSLinkGroupv4 definition
54 Revision 1.535.2.1 2006/10/31 01:28:06 cheshire
55 <rdar://problem/4456945> After service restarts on different port, for a few seconds DNS-SD may return stale port number
57 Revision 1.535 2006/03/02 20:41:17 cheshire
58 <rdar://problem/4111464> After record update, old record sometimes remains in cache
59 Minor code tidying and comments to reduce the risk of similar programming errors in future
61 Revision 1.534 2006/03/02 03:25:46 cheshire
62 <rdar://problem/4111464> After record update, old record sometimes remains in cache
63 Code to harmonize RRSet TTLs was inadvertently rescuing expiring records
65 Revision 1.533 2006/02/26 00:54:41 cheshire
66 Fixes to avoid code generation warning/error on FreeBSD 7
68 Revision 1.532 2005/12/02 20:24:36 cheshire
69 <rdar://problem/4363209> Adjust cutoff time for KA list by one second
71 Revision 1.531 2005/12/02 19:05:42 cheshire
74 Revision 1.530 2005/11/07 01:49:48 cheshire
75 For consistency, use NonZeroTime() function instead of ?: expression
77 Revision 1.529 2005/10/25 23:42:24 cheshire
78 <rdar://problem/4316057> Error in ResolveSimultaneousProbe() when type or class don't match
79 Changed switch statement to an "if"
81 Revision 1.528 2005/10/25 23:34:22 cheshire
82 <rdar://problem/4316048> RequireGoodbye state not set/respected sometimes when machine going to sleep
84 Revision 1.527 2005/10/25 22:43:59 cheshire
85 Add clarifying comments
87 Revision 1.526 2005/10/20 00:10:33 cheshire
88 <rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
90 Revision 1.525 2005/09/24 00:47:17 cheshire
93 Revision 1.524 2005/09/16 21:06:49 cheshire
94 Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
96 Revision 1.523 2005/03/21 00:33:51 shersche
97 <rdar://problem/4021486> Fix build warnings on Win32 platform
99 Revision 1.522 2005/03/04 21:48:12 cheshire
100 <rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity
102 Revision 1.521 2005/02/25 04:21:00 cheshire
103 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
105 Revision 1.520 2005/02/16 01:14:11 cheshire
106 Convert RR Cache LogOperation() calls to debugf()
108 Revision 1.519 2005/02/15 01:57:20 cheshire
109 When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero
111 Revision 1.518 2005/02/10 22:35:17 cheshire
112 <rdar://problem/3727944> Update name
114 Revision 1.517 2005/02/03 00:21:21 cheshire
115 Update comments about BIND named and zero-length TXT records
117 Revision 1.516 2005/01/28 06:06:32 cheshire
120 Revision 1.515 2005/01/27 00:21:49 cheshire
121 <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
123 Revision 1.514 2005/01/21 01:33:45 cheshire
124 <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
126 Revision 1.513 2005/01/21 00:07:54 cheshire
127 <rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict
129 Revision 1.512 2005/01/20 00:37:45 cheshire
130 <rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse
131 Take care not to recycle records while they are on the CacheFlushRecords list
133 Revision 1.511 2005/01/19 22:48:53 cheshire
134 <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
136 Revision 1.510 2005/01/19 03:12:45 cheshire
137 Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
139 Revision 1.509 2005/01/19 03:08:49 cheshire
140 <rdar://problem/3961051> CPU Spin in mDNSResponder
141 Log messages to help catch and report CPU spins
143 Revision 1.508 2005/01/18 18:56:32 cheshire
144 <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
146 Revision 1.507 2005/01/18 01:12:07 cheshire
147 <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
149 Revision 1.506 2005/01/17 23:28:53 cheshire
152 Revision 1.505 2005/01/11 02:02:56 shersche
153 Move variable declaration to the beginning of statement block
155 Revision 1.504 2004/12/20 20:24:35 cheshire
156 <rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer
158 Revision 1.503 2004/12/20 18:41:47 cheshire
159 <rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space
161 Revision 1.502 2004/12/20 18:04:08 cheshire
162 <rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache
164 Revision 1.501 2004/12/19 23:50:18 cheshire
165 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
166 Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services
168 Revision 1.500 2004/12/18 03:13:46 cheshire
169 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
171 Revision 1.499 2004/12/17 23:37:45 cheshire
172 <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
173 (and other repetitive configuration changes)
175 Revision 1.498 2004/12/17 05:25:46 cheshire
176 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
178 Revision 1.497 2004/12/17 03:20:58 cheshire
179 <rdar://problem/3925168> Don't send unicast replies we know will be ignored
181 Revision 1.496 2004/12/16 22:18:26 cheshire
182 Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
184 Revision 1.495 2004/12/16 21:27:37 ksekar
185 Fixed build failures when compiled with verbose debugging messages
187 Revision 1.494 2004/12/16 20:46:56 cheshire
188 Fix compiler warnings
190 Revision 1.493 2004/12/16 20:13:00 cheshire
191 <rdar://problem/3324626> Cache memory management improvements
193 Revision 1.492 2004/12/16 08:03:24 shersche
194 Fix compilation error when UNICAST_DISABLED is set
196 Revision 1.491 2004/12/11 01:52:11 cheshire
197 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
199 Revision 1.490 2004/12/10 20:06:25 cheshire
200 <rdar://problem/3915074> Reduce egregious stack space usage
201 Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes
203 Revision 1.489 2004/12/10 20:03:43 cheshire
204 <rdar://problem/3915074> Reduce egregious stack space usage
205 Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes
207 Revision 1.488 2004/12/10 19:50:41 cheshire
208 <rdar://problem/3915074> Reduce egregious stack space usage
209 Reduced SendResponses() stack frame from 9K to 176 bytes
211 Revision 1.487 2004/12/10 19:39:13 cheshire
212 <rdar://problem/3915074> Reduce egregious stack space usage
213 Reduced SendQueries() stack frame from 18K to 112 bytes
215 Revision 1.486 2004/12/10 14:16:17 cheshire
216 <rdar://problem/3889788> Relax update rate limiting
217 We now allow an average rate of ten updates per minute.
218 Updates in excess of that are rate limited, but more gently than before.
220 Revision 1.485 2004/12/10 02:09:24 cheshire
221 <rdar://problem/3898376> Modify default TTLs
223 Revision 1.484 2004/12/09 03:15:40 ksekar
224 <rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains
226 Revision 1.483 2004/12/07 23:00:14 ksekar
227 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration:
228 Call RecordProbeFailure even if there is no record callback
230 Revision 1.482 2004/12/07 22:49:06 cheshire
231 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
233 Revision 1.481 2004/12/07 21:26:04 ksekar
234 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
236 Revision 1.480 2004/12/07 20:42:33 cheshire
237 Add explicit context parameter to mDNS_RemoveRecordFromService()
239 Revision 1.479 2004/12/07 17:50:49 ksekar
240 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
242 Revision 1.478 2004/12/06 21:15:22 ksekar
243 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
245 Revision 1.477 2004/12/04 02:12:45 cheshire
246 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
248 Revision 1.476 2004/11/29 23:34:31 cheshire
249 On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
250 is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
251 only nudges the time value to 1 if the interval calculation happens to result in the value zero.
253 Revision 1.475 2004/11/29 23:13:31 cheshire
254 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
255 Additional check: Make sure we don't unnecessarily send packets containing only additionals.
256 (This could occur with multi-packet KA lists, if the answer and additionals were marked
257 by the query packet, and then the answer were later suppressed in a subsequent KA packet.)
259 Revision 1.474 2004/11/29 17:18:12 cheshire
260 Remove "Unknown DNS packet type" message for update responses
262 Revision 1.473 2004/11/25 01:57:52 cheshire
263 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
265 Revision 1.472 2004/11/25 01:28:09 cheshire
266 <rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too)
268 Revision 1.471 2004/11/25 01:10:13 cheshire
269 Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
271 Revision 1.470 2004/11/24 21:54:44 cheshire
272 <rdar://problem/3894475> mDNSCore not receiving unicast responses properly
274 Revision 1.469 2004/11/24 04:50:39 cheshire
277 Revision 1.468 2004/11/24 01:47:07 cheshire
278 <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
280 Revision 1.467 2004/11/24 01:41:28 cheshire
281 Rename CompleteProbing() to AcknowledgeRecord()
283 Revision 1.466 2004/11/23 21:08:07 ksekar
284 Don't use ID to demux multicast/unicast now that unicast uses random IDs
286 Revision 1.465 2004/11/15 20:09:21 ksekar
287 <rdar://problem/3719050> Wide Area support for Add/Remove record
289 Revision 1.464 2004/11/03 01:44:36 cheshire
290 Update debugging messages
292 Revision 1.463 2004/10/29 02:38:48 cheshire
293 Fix Windows compile errors
295 Revision 1.462 2004/10/28 19:21:07 cheshire
296 Guard against registering interface with zero InterfaceID
298 Revision 1.461 2004/10/28 19:02:16 cheshire
299 Remove \n from LogMsg() call
301 Revision 1.460 2004/10/28 03:24:40 cheshire
302 Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
304 Revision 1.459 2004/10/26 22:34:37 cheshire
305 <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
307 Revision 1.458 2004/10/26 20:45:28 cheshire
308 Show mask in "invalid mask" message
310 Revision 1.457 2004/10/26 06:28:36 cheshire
311 Now that we don't check IP TTL any more, remove associated log message
313 Revision 1.456 2004/10/26 06:21:42 cheshire
314 Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address)
316 Revision 1.455 2004/10/26 06:11:40 cheshire
317 Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
319 Revision 1.454 2004/10/23 01:16:00 cheshire
320 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
322 Revision 1.453 2004/10/22 20:52:06 ksekar
323 <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
325 Revision 1.452 2004/10/20 01:50:40 cheshire
326 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
327 Implemented ForceMCast mode for AuthRecords as well as for Questions
329 Revision 1.451 2004/10/19 21:33:15 cheshire
330 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
331 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
332 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
334 Revision 1.450 2004/10/19 17:42:59 ksekar
335 Fixed compiler warnings for non-debug builds.
337 Revision 1.449 2004/10/18 22:57:07 cheshire
338 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
340 Revision 1.448 2004/10/16 00:16:59 cheshire
341 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
343 Revision 1.447 2004/10/15 00:51:21 cheshire
344 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
346 Revision 1.446 2004/10/14 00:43:34 cheshire
347 <rdar://problem/3815984> Services continue to announce SRV and HINFO
349 Revision 1.445 2004/10/12 21:07:09 cheshire
350 Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
352 Revision 1.444 2004/10/11 17:54:16 ksekar
353 Changed hashtable pointer output from debugf to verbosedebugf.
355 Revision 1.443 2004/10/10 07:05:45 cheshire
356 For consistency, use symbol "localdomain" instead of literal string
358 Revision 1.442 2004/10/08 20:25:10 cheshire
359 Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time
361 Revision 1.441 2004/10/08 03:25:01 ksekar
362 <rdar://problem/3831716> domain enumeration should use LLQs
364 Revision 1.440 2004/10/06 01:44:19 cheshire
365 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
367 Revision 1.439 2004/10/03 23:14:11 cheshire
368 Add "mDNSEthAddr" type and "zeroEthAddr" constant
370 Revision 1.438 2004/09/29 23:07:04 cheshire
371 Patch from Pavel Repin to fix compile error on Windows
373 Revision 1.437 2004/09/28 02:23:50 cheshire
374 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
375 Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot
376 For records with the cache flush bit set, defer the decision until the end of the packet
378 Revision 1.436 2004/09/28 01:27:04 cheshire
379 Update incorrect log message
381 Revision 1.435 2004/09/25 02:41:39 cheshire
382 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
384 Revision 1.434 2004/09/25 02:32:06 cheshire
387 Revision 1.433 2004/09/25 02:24:27 cheshire
388 Removed unused rr->UseCount
390 Revision 1.432 2004/09/24 21:35:17 cheshire
391 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
392 TargetPort and TargetQID are allowed to be undefined if no question->Target is set
394 Revision 1.431 2004/09/24 21:33:12 cheshire
397 Revision 1.430 2004/09/24 02:15:49 cheshire
398 <rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces
400 Revision 1.429 2004/09/24 00:20:21 cheshire
401 <rdar://problem/3483349> Any rrtype is a conflict for unique records
403 Revision 1.428 2004/09/24 00:12:25 cheshire
404 Get rid of unused RRUniqueOrKnownUnique(RR)
406 Revision 1.427 2004/09/23 20:44:11 cheshire
407 <rdar://problem/3813148> Reduce timeout before expiring records on failure
409 Revision 1.426 2004/09/23 20:21:07 cheshire
410 <rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
411 Associate a unique sequence number with each received packet, and only increment the count of recent answer
412 packets if the packet sequence number for this answer record is not one we've already seen and counted.
414 Revision 1.425 2004/09/23 20:14:38 cheshire
415 Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
417 Revision 1.424 2004/09/23 00:58:36 cheshire
418 <rdar://problem/3781269> Rate limiting interferes with updating TXT records
420 Revision 1.423 2004/09/23 00:50:53 cheshire
421 <rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep
423 Revision 1.422 2004/09/22 02:34:46 cheshire
424 Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
426 Revision 1.421 2004/09/21 23:29:49 cheshire
427 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
429 Revision 1.420 2004/09/21 23:01:42 cheshire
430 Update debugf messages
432 Revision 1.419 2004/09/21 19:51:14 cheshire
433 Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
435 Revision 1.418 2004/09/21 18:40:17 cheshire
436 <rdar://problem/3376752> Adjust default record TTLs
438 Revision 1.417 2004/09/21 17:32:16 cheshire
439 <rdar://problem/3809484> Rate limiting imposed too soon
441 Revision 1.416 2004/09/20 23:52:01 cheshire
442 CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
444 Revision 1.415 2004/09/18 01:14:09 cheshire
445 <rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces
447 Revision 1.414 2004/09/18 01:06:48 cheshire
450 Revision 1.413 2004/09/17 01:08:48 cheshire
451 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
452 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
453 declared in that file are ONLY appropriate to single-address-space embedded applications.
454 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
456 Revision 1.412 2004/09/17 00:46:33 cheshire
457 mDNS_TimeNow should take const mDNS parameter
459 Revision 1.411 2004/09/17 00:31:51 cheshire
460 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
462 Revision 1.410 2004/09/17 00:19:10 cheshire
463 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
465 Revision 1.409 2004/09/16 21:59:15 cheshire
466 For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
468 Revision 1.408 2004/09/16 21:36:36 cheshire
469 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
470 Changes to add necessary locking calls around unicast DNS operations
472 Revision 1.407 2004/09/16 02:29:39 cheshire
473 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
474 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
476 Revision 1.406 2004/09/16 01:58:14 cheshire
477 Fix compiler warnings
479 Revision 1.405 2004/09/16 00:24:48 cheshire
480 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
482 Revision 1.404 2004/09/15 21:44:11 cheshire
483 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
484 Show time value in log to help diagnose errors
486 Revision 1.403 2004/09/15 00:46:32 ksekar
487 Changed debugf to verbosedebugf in CheckCacheExpiration
489 Revision 1.402 2004/09/14 23:59:55 cheshire
490 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
492 Revision 1.401 2004/09/14 23:27:46 cheshire
495 Revision 1.400 2004/09/02 03:48:47 cheshire
496 <rdar://problem/3709039> Disable targeted unicast query support by default
497 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
498 2. New field AllowRemoteQuery in AuthRecord structure
499 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
500 4. mDNS.c only answers remote queries if AllowRemoteQuery is set
502 Revision 1.399 2004/09/02 01:39:40 cheshire
503 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
505 Revision 1.398 2004/09/01 03:59:29 ksekar
506 <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
508 Revision 1.397 2004/08/25 22:04:25 rpantos
509 Fix the standard Windows compile error.
511 Revision 1.396 2004/08/25 00:37:27 ksekar
512 <rdar://problem/3774635>: Cleanup DynDNS hostname registration code
514 Revision 1.395 2004/08/18 17:21:18 ksekar
515 Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
517 Revision 1.394 2004/08/14 03:22:41 cheshire
518 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
519 Add GetUserSpecifiedDDNSName() routine
520 Convert ServiceRegDomain to domainname instead of C string
521 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
523 Revision 1.393 2004/08/13 23:42:52 cheshire
524 Removed unused "zeroDomainNamePtr"
526 Revision 1.392 2004/08/13 23:37:02 cheshire
527 Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
528 "uDNS_info.UnicastHostname" for clarity
530 Revision 1.391 2004/08/13 23:25:00 cheshire
531 Now that we do both uDNS and mDNS, global replace "m->hostname" with
532 "m->MulticastHostname" for clarity
534 Revision 1.390 2004/08/11 02:17:01 cheshire
535 <rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record
537 Revision 1.389 2004/08/10 23:19:14 ksekar
538 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
539 Moved routines/constants to allow extern access for garbage collection daemon
541 Revision 1.388 2004/07/30 17:40:06 ksekar
542 <rdar://problem/3739115>: TXT Record updates not available for wide-area services
544 Revision 1.387 2004/07/26 22:49:30 ksekar
545 <rdar://problem/3651409>: Feature #9516: Need support for NAT-PMP in client
547 Revision 1.386 2004/07/13 21:24:24 rpantos
548 Fix for <rdar://problem/3701120>.
550 Revision 1.385 2004/06/18 19:09:59 cheshire
551 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
553 Revision 1.384 2004/06/15 04:31:23 cheshire
554 Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
556 Revision 1.383 2004/06/11 00:04:59 cheshire
557 <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
559 Revision 1.382 2004/06/08 04:59:40 cheshire
560 Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
562 Revision 1.381 2004/06/05 00:57:30 cheshire
563 Remove incorrect LogMsg()
565 Revision 1.380 2004/06/05 00:04:26 cheshire
566 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
568 Revision 1.379 2004/05/28 23:42:36 ksekar
569 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
571 Revision 1.378 2004/05/25 17:25:25 cheshire
572 Remove extraneous blank lines and white space
574 Revision 1.377 2004/05/18 23:51:25 cheshire
575 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
577 Revision 1.376 2004/05/05 18:30:44 ksekar
578 Restored surpressed Cache Tail debug messages.
580 Revision 1.375 2004/04/26 21:36:25 cheshire
581 Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive
582 is indicated as being available on that interface
584 Revision 1.374 2004/04/21 02:53:26 cheshire
585 Typo in debugf statement
587 Revision 1.373 2004/04/21 02:49:11 cheshire
588 To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
590 Revision 1.372 2004/04/21 02:38:51 cheshire
593 Revision 1.371 2004/04/14 23:09:28 ksekar
594 Support for TSIG signed dynamic updates.
596 Revision 1.370 2004/04/09 17:40:26 cheshire
597 Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
599 Revision 1.369 2004/04/09 16:34:00 cheshire
600 Debugging code for later; currently unused
602 Revision 1.368 2004/04/02 19:19:48 cheshire
603 Add code to do optional logging of multi-packet KA list time intervals
605 Revision 1.367 2004/03/20 03:16:10 cheshire
606 Minor refinement to "Excessive update rate" message
608 Revision 1.366 2004/03/20 03:12:57 cheshire
609 <rdar://problem/3587619>: UpdateCredits not granted promptly enough
611 Revision 1.365 2004/03/19 23:51:22 cheshire
612 Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
614 Revision 1.364 2004/03/13 01:57:33 ksekar
615 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
617 Revision 1.363 2004/03/12 21:00:51 cheshire
618 Also show port numbers when logging "apparent spoof mDNS Response" messages
620 Revision 1.362 2004/03/12 08:58:18 cheshire
621 Guard against empty TXT records
623 Revision 1.361 2004/03/09 03:00:46 cheshire
624 <rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good.
626 Revision 1.360 2004/03/08 02:52:41 cheshire
627 Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages
629 Revision 1.359 2004/03/02 03:21:56 cheshire
630 <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
632 Revision 1.358 2004/02/20 08:18:34 cheshire
633 <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
635 Revision 1.357 2004/02/18 01:47:41 cheshire
636 <rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms
638 Revision 1.356 2004/02/06 23:04:19 ksekar
639 Basic Dynamic Update support via mDNS_Register (dissabled via
640 UNICAST_REGISTRATION #define)
642 Revision 1.355 2004/02/05 09:32:33 cheshire
643 Fix from Bob Bradley: When using the "%.*s" string form,
644 guard against truncating in the middle of a multi-byte UTF-8 character.
646 Revision 1.354 2004/02/05 09:30:22 cheshire
649 Revision 1.353 2004/01/28 03:41:00 cheshire
650 <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
652 Revision 1.352 2004/01/28 02:30:07 ksekar
653 Added default Search Domains to unicast browsing, controlled via
654 Networking sharing prefs pane. Stopped sending unicast messages on
655 every interface. Fixed unicast resolving via mach-port API.
657 Revision 1.351 2004/01/27 20:15:22 cheshire
658 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
660 Revision 1.350 2004/01/24 23:38:16 cheshire
661 Use mDNSVal16() instead of shifting and ORing operations
663 Revision 1.349 2004/01/23 23:23:14 ksekar
664 Added TCP support for truncated unicast messages.
666 Revision 1.348 2004/01/22 03:54:11 cheshire
667 Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
668 which means "do this query via multicast, even if it's apparently a unicast domain"
670 Revision 1.347 2004/01/22 03:50:49 cheshire
671 If the client has specified an explicit InterfaceID, then do query by multicast, not unicast
673 Revision 1.346 2004/01/22 03:48:41 cheshire
674 Make sure uDNS client doesn't accidentally use query ID zero
676 Revision 1.345 2004/01/22 03:43:08 cheshire
677 Export constants like mDNSInterface_LocalOnly so that the client layers can use them
679 Revision 1.344 2004/01/21 21:53:18 cheshire
680 <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
682 Revision 1.343 2003/12/23 00:07:47 cheshire
683 Make port number in debug message be five-character field, left justified
685 Revision 1.342 2003/12/20 01:34:28 cheshire
686 <rdar://problem/3515876>: Error putting additional records into packets
687 Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
688 after every record, not once at the end.
690 Revision 1.341 2003/12/18 22:56:12 cheshire
691 <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
693 Revision 1.340 2003/12/16 02:31:37 cheshire
694 Minor update to comments
696 Revision 1.339 2003/12/13 05:50:33 bradley
697 Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform
698 layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to
699 fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf.
701 Revision 1.338 2003/12/13 03:05:27 ksekar
702 <rdar://problem/3192548>: DynDNS: Unicast query of service records
704 Revision 1.337 2003/12/01 21:46:05 cheshire
705 mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
707 Revision 1.336 2003/12/01 21:26:19 cheshire
708 Guard against zero-length sbuffer in mDNS_vsnprintf()
710 Revision 1.335 2003/12/01 20:27:48 cheshire
711 Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors
713 Revision 1.334 2003/11/20 22:59:53 cheshire
714 Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
715 Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
717 Revision 1.333 2003/11/20 20:49:53 cheshire
718 Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
720 Revision 1.332 2003/11/20 05:47:37 cheshire
721 <rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query
722 Now that we only include answers in the known answer list if they are less than
723 halfway to expiry, the check to also see if we have another query scheduled
724 before the record expires is no longer necessary (and in fact, not correct).
726 Revision 1.331 2003/11/19 22:31:48 cheshire
727 When automatically adding A records to SRVs, add them as additionals, not answers
729 Revision 1.330 2003/11/19 22:28:50 cheshire
730 Increment/Decrement mDNS_reentrancy around calls to m->MainCallback()
731 to allow client to make mDNS calls (specifically the call to mDNS_GrowCache())
733 Revision 1.329 2003/11/19 22:19:24 cheshire
734 Show log message when ignoring packets with bad TTL.
735 This is to help diagnose problems on Linux versions that may not report the TTL reliably.
737 Revision 1.328 2003/11/19 22:06:38 cheshire
738 Show log messages when a service or hostname is renamed
740 Revision 1.327 2003/11/19 22:03:44 cheshire
741 Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
743 Revision 1.326 2003/11/17 22:27:02 cheshire
744 Another fix from ramaprasad.kr@hp.com: Improve reply delay computation
745 on platforms that have native clock rates below fifty ticks per second.
747 Revision 1.325 2003/11/17 20:41:44 cheshire
748 Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
750 Revision 1.324 2003/11/17 20:36:32 cheshire
751 Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and
752 DeadvertiseInterface() -- they're internal private routines, not API routines.
754 Revision 1.323 2003/11/14 20:59:08 cheshire
755 Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
756 Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
758 Revision 1.322 2003/11/14 19:47:52 cheshire
759 Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
761 Revision 1.321 2003/11/14 19:18:34 cheshire
762 Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
764 Revision 1.320 2003/11/13 06:45:04 cheshire
765 Fix compiler warning on certain compilers
767 Revision 1.319 2003/11/13 00:47:40 cheshire
768 <rdar://problem/3437556> We should delay AAAA record query if A record already in cache.
770 Revision 1.318 2003/11/13 00:33:26 cheshire
771 Change macro "RRIsAddressType" to "RRTypeIsAddressType"
773 Revision 1.317 2003/11/13 00:10:49 cheshire
774 <rdar://problem/3436412>: Verify that rr data is different before updating.
776 Revision 1.316 2003/11/08 23:37:54 cheshire
777 Give explicit zero initializers to blank static structure, required by certain compilers.
778 (Thanks to ramaprasad.kr@hp.com for reporting this.)
780 Revision 1.315 2003/11/07 03:32:56 cheshire
781 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
782 This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
783 purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
784 to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
785 can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
787 Revision 1.314 2003/11/07 03:19:49 cheshire
788 Minor variable renaming for clarity
790 Revision 1.313 2003/11/07 03:14:49 cheshire
791 Previous checkin proved to be overly simplistic; reversing
793 Revision 1.312 2003/11/03 23:45:15 cheshire
794 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
795 Build cache lists in FIFO order, not customary C LIFO order
796 (Append new elements to tail of cache list, instead of prepending at the head.)
798 Revision 1.311 2003/10/09 18:00:11 cheshire
799 Another compiler warning fix.
801 Revision 1.310 2003/10/07 20:27:05 cheshire
802 Patch from Bob Bradley, to fix warning and compile error on Windows
804 Revision 1.309 2003/09/26 01:06:36 cheshire
805 <rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too
806 Made new routine HaveSentEntireRRSet() to check if flag should be set
808 Revision 1.308 2003/09/23 01:05:01 cheshire
809 Minor changes to comments and debugf() message
811 Revision 1.307 2003/09/09 20:13:30 cheshire
812 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
813 Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
814 rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
816 Revision 1.306 2003/09/09 03:00:03 cheshire
817 <rdar://problem/3413099> Services take a long time to disappear when switching networks.
818 Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
820 Revision 1.305 2003/09/09 02:49:31 cheshire
821 <rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
823 Revision 1.304 2003/09/09 02:41:19 cheshire
824 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
826 Revision 1.303 2003/09/05 19:55:02 cheshire
827 <rdar://problem/3409533> Include address records when announcing SRV records
829 Revision 1.302 2003/09/05 00:01:36 cheshire
830 <rdar://problem/3407549> Don't accelerate queries that have large KA lists
832 Revision 1.301 2003/09/04 22:51:13 cheshire
833 <rdar://problem/3398213> Group probes and goodbyes better
835 Revision 1.300 2003/09/03 02:40:37 cheshire
836 <rdar://problem/3404842> mDNSResponder complains about '_'s
837 Underscores are not supposed to be legal in standard DNS names, but IANA appears
838 to have allowed them in previous service name registrations, so we should too.
840 Revision 1.299 2003/09/03 02:33:09 cheshire
841 <rdar://problem/3404795> CacheRecordRmv ERROR
842 Don't update m->NewQuestions until *after* CheckCacheExpiration();
844 Revision 1.298 2003/09/03 01:47:01 cheshire
845 <rdar://problem/3319418> Services always in a state of flux
846 Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds
848 Revision 1.297 2003/08/29 19:44:15 cheshire
849 <rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
850 1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries
851 that already have at least one unique answer in the cache
852 2. For these queries, go straight to QM, skipping QU
854 Revision 1.296 2003/08/29 19:08:21 cheshire
855 <rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
856 Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
858 Revision 1.295 2003/08/28 01:10:59 cheshire
859 <rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
861 Revision 1.294 2003/08/27 02:30:22 cheshire
862 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
863 One more change: "query->GotTXT" is now a straightforward bi-state boolean again
865 Revision 1.293 2003/08/27 02:25:31 cheshire
866 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
868 Revision 1.292 2003/08/21 19:27:36 cheshire
869 <rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
871 Revision 1.291 2003/08/21 18:57:44 cheshire
872 <rdar://problem/3387140> Synchronized queries on the network
874 Revision 1.290 2003/08/21 02:25:23 cheshire
875 Minor changes to comments and debugf() messages
877 Revision 1.289 2003/08/21 02:21:50 cheshire
878 <rdar://problem/3386473> Efficiency: Reduce repeated queries
880 Revision 1.288 2003/08/20 23:39:30 cheshire
881 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
883 Revision 1.287 2003/08/20 20:47:18 cheshire
886 Revision 1.286 2003/08/20 02:18:51 cheshire
887 <rdar://problem/3344098> Cleanup: Review syslog messages
889 Revision 1.285 2003/08/20 01:59:06 cheshire
890 <rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
891 Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
893 Revision 1.284 2003/08/19 22:20:00 cheshire
894 <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
895 More minor refinements
897 Revision 1.283 2003/08/19 22:16:27 cheshire
898 Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
900 Revision 1.282 2003/08/19 06:48:25 cheshire
901 <rdar://problem/3376552> Guard against excessive record updates
902 Each record starts with 10 UpdateCredits.
903 Every update consumes one UpdateCredit.
904 UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
905 As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
906 When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
908 Revision 1.281 2003/08/19 04:49:28 cheshire
909 <rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right
910 1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6.
911 2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
912 3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
914 Revision 1.280 2003/08/19 02:33:36 cheshire
917 Revision 1.279 2003/08/19 02:31:11 cheshire
918 <rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
919 Final expiration queries now only mark the question for sending on the particular interface
920 pertaining to the record that's expiring.
922 Revision 1.278 2003/08/18 22:53:37 cheshire
923 <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
925 Revision 1.277 2003/08/18 19:05:44 cheshire
926 <rdar://problem/3382423> UpdateRecord not working right
927 Added "newrdlength" field to hold new length of updated rdata
929 Revision 1.276 2003/08/16 03:39:00 cheshire
930 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
932 Revision 1.275 2003/08/16 02:51:27 cheshire
933 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
934 Don't try to compute namehash etc, until *after* validating the name
936 Revision 1.274 2003/08/16 01:12:40 cheshire
937 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
938 Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
939 simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
940 and in the process of copying it, the C compiler may run off the end of the rdata object into
941 unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
942 call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
944 Revision 1.273 2003/08/15 20:16:02 cheshire
945 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
946 We want to avoid touching the rdata pages, so we don't page them in.
947 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
948 Moved this from the RData to the ResourceRecord object.
949 2. To avoid unnecessarily touching the rdata just to compare it,
950 compute a hash of the rdata and store the hash in the ResourceRecord object.
952 Revision 1.272 2003/08/14 19:29:04 cheshire
953 <rdar://problem/3378473> Include cache records in SIGINFO output
954 Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
956 Revision 1.271 2003/08/14 02:17:05 cheshire
957 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
959 Revision 1.270 2003/08/13 17:07:28 ksekar
960 <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
961 Added check to result of mDNS_Register() before linking extra record into list.
963 Revision 1.269 2003/08/12 19:56:23 cheshire
966 Revision 1.268 2003/08/12 15:01:10 cheshire
969 Revision 1.267 2003/08/12 14:59:27 cheshire
970 <rdar://problem/3374490> Rate-limiting blocks some legitimate responses
971 When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
972 whether to suppress the response, also check LastMCInterface to see if it matches.
974 Revision 1.266 2003/08/12 12:47:16 cheshire
975 In mDNSCoreMachineSleep debugf message, display value of m->timenow
977 Revision 1.265 2003/08/11 20:04:28 cheshire
978 <rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
980 Revision 1.264 2003/08/09 00:55:02 cheshire
981 <rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
982 Don't scan the whole cache after every packet.
984 Revision 1.263 2003/08/09 00:35:29 cheshire
985 Moved AnswerNewQuestion() later in the file, in preparation for next checkin
987 Revision 1.262 2003/08/08 19:50:33 cheshire
988 <rdar://problem/3370332> Remove "Cache size now xxx" messages
990 Revision 1.261 2003/08/08 19:18:45 cheshire
991 <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
993 Revision 1.260 2003/08/08 18:55:48 cheshire
994 <rdar://problem/3370365> Guard against time going backwards
996 Revision 1.259 2003/08/08 18:36:04 cheshire
997 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
999 Revision 1.258 2003/08/08 16:22:05 cheshire
1000 <rdar://problem/3335473> Need to check validity of TXT (and other) records
1001 Remove unneeded LogMsg
1003 Revision 1.257 2003/08/07 01:41:08 cheshire
1004 <rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
1006 Revision 1.256 2003/08/06 23:25:51 cheshire
1007 <rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four
1009 Revision 1.255 2003/08/06 23:22:50 cheshire
1010 Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
1012 Revision 1.254 2003/08/06 21:33:39 cheshire
1013 Fix compiler warnings on PocketPC 2003 (Windows CE)
1015 Revision 1.253 2003/08/06 20:43:57 cheshire
1016 <rdar://problem/3335473> Need to check validity of TXT (and other) records
1017 Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
1019 Revision 1.252 2003/08/06 20:35:47 cheshire
1020 Enhance debugging routine GetRRDisplayString() so it can also be used to display
1021 other RDataBody objects, not just the one currently attached the given ResourceRecord
1023 Revision 1.251 2003/08/06 19:07:34 cheshire
1024 <rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
1025 Was checking LastAPTime instead of LastMCTime
1027 Revision 1.250 2003/08/06 19:01:55 cheshire
1030 Revision 1.249 2003/08/06 00:13:28 cheshire
1031 Tidy up debugf messages
1033 Revision 1.248 2003/08/05 22:20:15 cheshire
1034 <rdar://problem/3330324> Need to check IP TTL on responses
1036 Revision 1.247 2003/08/05 00:56:39 cheshire
1037 <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
1039 Revision 1.246 2003/08/04 19:20:49 cheshire
1040 Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
1042 Revision 1.245 2003/08/02 01:56:29 cheshire
1043 For debugging: log message if we ever get more than one question in a truncated packet
1045 Revision 1.244 2003/08/01 23:55:32 cheshire
1046 Fix for compiler warnings on Windows, submitted by Bob Bradley
1048 Revision 1.243 2003/07/25 02:26:09 cheshire
1049 Typo: FIxed missing semicolon
1051 Revision 1.242 2003/07/25 01:18:41 cheshire
1052 Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
1054 Revision 1.241 2003/07/23 21:03:42 cheshire
1055 Only show "Found record..." debugf message in verbose mode
1057 Revision 1.240 2003/07/23 21:01:11 cheshire
1058 <rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
1059 After sending a packet, suppress further sending for the next 100ms.
1061 Revision 1.239 2003/07/22 01:30:05 cheshire
1062 <rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once
1064 Revision 1.238 2003/07/22 00:10:20 cheshire
1065 <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
1067 Revision 1.237 2003/07/19 03:23:13 cheshire
1068 <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
1070 Revision 1.236 2003/07/19 03:04:55 cheshire
1071 Fix warnings; some debugf message improvements
1073 Revision 1.235 2003/07/19 00:03:32 cheshire
1074 <rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received
1075 ScheduleNextTask is quite an expensive operation.
1076 We don't need to do all that work after receiving a no-op packet that didn't change our state.
1078 Revision 1.234 2003/07/18 23:52:11 cheshire
1079 To improve consistency of field naming, global search-and-replace:
1080 NextProbeTime -> NextScheduledProbe
1081 NextResponseTime -> NextScheduledResponse
1083 Revision 1.233 2003/07/18 00:29:59 cheshire
1084 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
1086 Revision 1.232 2003/07/18 00:11:38 cheshire
1087 Add extra case to switch statements to handle HINFO data for Get, Put and Display
1088 (In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT)
1090 Revision 1.231 2003/07/18 00:06:37 cheshire
1091 To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->"
1093 Revision 1.230 2003/07/17 18:16:54 cheshire
1094 <rdar://problem/3319418> Services always in a state of flux
1095 In preparation for working on this, made some debugf messages a little more selective
1097 Revision 1.229 2003/07/17 17:35:04 cheshire
1098 <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
1100 Revision 1.228 2003/07/16 20:50:27 cheshire
1101 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1103 Revision 1.227 2003/07/16 05:01:36 cheshire
1104 Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
1105 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1107 Revision 1.226 2003/07/16 04:51:44 cheshire
1108 Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
1110 Revision 1.225 2003/07/16 04:46:41 cheshire
1111 Minor wording cleanup: The correct DNS term is "response", not "reply"
1113 Revision 1.224 2003/07/16 04:39:02 cheshire
1114 Textual cleanup (no change to functionality):
1115 Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
1117 Revision 1.223 2003/07/16 00:09:22 cheshire
1118 Textual cleanup (no change to functionality):
1119 Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
1120 replace with macro "TicksTTL(rr)"
1121 Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
1122 replaced with macro "RRExpireTime(rr)"
1124 Revision 1.222 2003/07/15 23:40:46 cheshire
1125 Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
1127 Revision 1.221 2003/07/15 22:17:56 cheshire
1128 <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
1130 Revision 1.220 2003/07/15 02:12:51 cheshire
1131 Slight tidy-up of debugf messages and comments
1133 Revision 1.219 2003/07/15 01:55:12 cheshire
1134 <rdar://problem/3315777> Need to implement service registration with subtypes
1136 Revision 1.218 2003/07/14 16:26:06 cheshire
1137 <rdar://problem/3324795> Duplicate query suppression not working right
1138 Refinement: Don't record DS information for a question in the first quarter second
1139 right after we send it -- in the case where a question happens to be accelerated by
1140 the maximum allowed amount, we don't want it to then be suppressed because the previous
1141 time *we* sent that question falls (just) within the valid duplicate suppression window.
1143 Revision 1.217 2003/07/13 04:43:53 cheshire
1144 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1145 Minor refinement: No need to make address query broader than the original SRV query that provoked it
1147 Revision 1.216 2003/07/13 03:13:17 cheshire
1148 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1149 If we get an identical SRV on a second interface, convert address queries to non-specific
1151 Revision 1.215 2003/07/13 02:28:00 cheshire
1152 <rdar://problem/3325166> SendResponses didn't all its responses
1153 Delete all references to RRInterfaceActive -- it's now superfluous
1155 Revision 1.214 2003/07/13 01:47:53 cheshire
1156 Fix one error and one warning in the Windows build
1158 Revision 1.213 2003/07/12 04:25:48 cheshire
1159 Fix minor signed/unsigned warnings
1161 Revision 1.212 2003/07/12 01:59:11 cheshire
1162 Minor changes to debugf messages
1164 Revision 1.211 2003/07/12 01:47:01 cheshire
1165 <rdar://problem/3324495> After name conflict, appended number should be higher than previous number
1167 Revision 1.210 2003/07/12 01:43:28 cheshire
1168 <rdar://problem/3324795> Duplicate query suppression not working right
1169 The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
1170 The code was incorrectly using the last query time plus one-half the query interval.
1171 This was only correct in the case where query acceleration was not in effect.
1173 Revision 1.209 2003/07/12 01:27:50 cheshire
1174 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1175 Fix missing "-1" in RemoveLabelSuffix()
1177 Revision 1.208 2003/07/11 01:32:38 cheshire
1178 Syntactic cleanup (no change to funcationality): Now that we only have one host name,
1179 rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
1181 Revision 1.207 2003/07/11 01:28:00 cheshire
1182 <rdar://problem/3161289> No more local.arpa
1184 Revision 1.206 2003/07/11 00:45:02 cheshire
1185 <rdar://problem/3321909> Client should get callback confirming successful host name registration
1187 Revision 1.205 2003/07/11 00:40:18 cheshire
1188 Tidy up debug message in HostNameCallback()
1190 Revision 1.204 2003/07/11 00:20:32 cheshire
1191 <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
1193 Revision 1.203 2003/07/10 23:53:41 cheshire
1194 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1196 Revision 1.202 2003/07/04 02:23:20 cheshire
1197 <rdar://problem/3311955> Responder too aggressive at flushing stale data
1198 Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
1200 Revision 1.201 2003/07/04 01:09:41 cheshire
1201 <rdar://problem/3315775> Need to implement subtype queries
1202 Modified ConstructServiceName() to allow three-part service types
1204 Revision 1.200 2003/07/03 23:55:26 cheshire
1205 Minor change to wording of syslog warning messages
1207 Revision 1.199 2003/07/03 23:51:13 cheshire
1208 <rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings
1209 Added more detailed debugging information
1211 Revision 1.198 2003/07/03 22:19:30 cheshire
1212 <rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
1213 Make exception to allow _tivo_servemedia._tcp.
1215 Revision 1.197 2003/07/02 22:33:05 cheshire
1216 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1218 When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
1219 Allow cache to grow to 512 records before considering it a potential denial-of-service attack
1221 Revision 1.196 2003/07/02 21:19:45 cheshire
1222 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
1224 Revision 1.195 2003/07/02 19:56:58 cheshire
1225 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1226 Minor refinement: m->rrcache_active was not being decremented when
1227 an active record was deleted because its TTL expired
1229 Revision 1.194 2003/07/02 18:47:40 cheshire
1230 Minor wording change to log messages
1232 Revision 1.193 2003/07/02 02:44:13 cheshire
1233 Fix warning in non-debug build
1235 Revision 1.192 2003/07/02 02:41:23 cheshire
1236 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1238 Revision 1.191 2003/07/02 02:30:51 cheshire
1239 HashSlot() returns an array index. It can't be negative; hence it should not be signed.
1241 Revision 1.190 2003/06/27 00:03:05 vlubet
1242 <rdar://problem/3304625> Merge of build failure fix for gcc 3.3
1244 Revision 1.189 2003/06/11 19:24:03 cheshire
1245 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1246 Slight refinement to previous checkin
1248 Revision 1.188 2003/06/10 20:33:28 cheshire
1249 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1251 Revision 1.187 2003/06/10 04:30:44 cheshire
1252 <rdar://problem/3286234> Need to re-probe/re-announce on configuration change
1253 Only interface-specific records were re-probing and re-announcing, not non-specific records.
1255 Revision 1.186 2003/06/10 04:24:39 cheshire
1256 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1257 Some additional refinements:
1258 Don't try to do this for unicast-response queries
1259 better tracking of Qs and KAs in multi-packet KA lists
1261 Revision 1.185 2003/06/10 03:52:49 cheshire
1262 Update comments and debug messages
1264 Revision 1.184 2003/06/10 02:26:39 cheshire
1265 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1266 Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
1268 Revision 1.183 2003/06/09 18:53:13 cheshire
1269 Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
1271 Revision 1.182 2003/06/09 18:38:42 cheshire
1272 <rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network
1273 Only issue a correction if the TTL in the proxy packet is less than half the correct value.
1275 Revision 1.181 2003/06/07 06:45:05 cheshire
1276 <rdar://problem/3283666> No need for multiple machines to all be sending the same queries
1278 Revision 1.180 2003/06/07 06:31:07 cheshire
1279 Create little four-line helper function "FindIdenticalRecordInCache()"
1281 Revision 1.179 2003/06/07 06:28:13 cheshire
1282 For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
1284 Revision 1.178 2003/06/07 06:25:12 cheshire
1285 Update some comments
1287 Revision 1.177 2003/06/07 04:50:53 cheshire
1288 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1290 Revision 1.176 2003/06/07 04:33:26 cheshire
1291 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1292 Minor change: Increment/decrement logic for q->CurrentAnswers should be in
1293 CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
1295 Revision 1.175 2003/06/07 04:11:52 cheshire
1296 Minor changes to comments and debug messages
1298 Revision 1.174 2003/06/07 01:46:38 cheshire
1299 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1301 Revision 1.173 2003/06/07 01:22:13 cheshire
1302 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1304 Revision 1.172 2003/06/07 00:59:42 cheshire
1305 <rdar://problem/3283454> Need some randomness to spread queries on the network
1307 Revision 1.171 2003/06/06 21:41:10 cheshire
1308 For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
1310 Revision 1.170 2003/06/06 21:38:55 cheshire
1311 Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
1312 already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
1314 Revision 1.169 2003/06/06 21:35:55 cheshire
1315 Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
1316 (the target is a domain name, but not necessarily a host name)
1318 Revision 1.168 2003/06/06 21:33:31 cheshire
1319 Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
1321 Revision 1.167 2003/06/06 21:30:42 cheshire
1322 <rdar://problem/3282962> Don't delay queries for shared record types
1324 Revision 1.166 2003/06/06 17:20:14 cheshire
1325 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
1326 (Global search-and-replace; no functional change to code execution.)
1328 Revision 1.165 2003/06/04 02:53:21 cheshire
1329 Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
1331 Revision 1.164 2003/06/04 01:25:33 cheshire
1332 <rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
1333 Display time interval between first and subsequent queries
1335 Revision 1.163 2003/06/03 19:58:14 cheshire
1336 <rdar://problem/3277665> mDNS_DeregisterService() fixes:
1337 When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
1338 Guard against a couple of possible mDNS_DeregisterService() race conditions.
1340 Revision 1.162 2003/06/03 19:30:39 cheshire
1341 Minor addition refinements for
1342 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1344 Revision 1.161 2003/06/03 18:29:03 cheshire
1345 Minor changes to comments and debugf() messages
1347 Revision 1.160 2003/06/03 05:02:16 cheshire
1348 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1350 Revision 1.159 2003/06/03 03:31:57 cheshire
1351 <rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine
1353 Revision 1.158 2003/06/02 22:57:09 cheshire
1354 Minor clarifying changes to comments and log messages;
1355 IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
1357 Revision 1.157 2003/05/31 00:09:49 cheshire
1358 <rdar://problem/3274862> Add ability to discover what services are on a network
1360 Revision 1.156 2003/05/30 23:56:49 cheshire
1361 <rdar://problem/3274847> Crash after error in mDNS_RegisterService()
1362 Need to set "sr->Extras = mDNSNULL" before returning
1364 Revision 1.155 2003/05/30 23:48:00 cheshire
1365 <rdar://problem/3274832> Announcements not properly grouped
1366 Due to inconsistent setting of rr->LastAPTime at different places in the
1367 code, announcements were not properly grouped into a single packet.
1368 Fixed by creating a single routine called InitializeLastAPTime().
1370 Revision 1.154 2003/05/30 23:38:14 cheshire
1371 <rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
1372 Wrote buffer[32] where it should have said buffer[64]
1374 Revision 1.153 2003/05/30 19:10:56 cheshire
1375 <rdar://problem/3274153> ConstructServiceName needs to be more restrictive
1377 Revision 1.152 2003/05/29 22:39:16 cheshire
1378 <rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
1380 Revision 1.151 2003/05/29 06:35:42 cheshire
1381 <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
1383 Revision 1.150 2003/05/29 06:25:45 cheshire
1384 <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
1386 Revision 1.149 2003/05/29 06:18:39 cheshire
1387 <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
1389 Revision 1.148 2003/05/29 06:11:34 cheshire
1390 <rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
1392 Revision 1.147 2003/05/29 06:01:18 cheshire
1393 Change some debugf() calls to LogMsg() calls to help with debugging
1395 Revision 1.146 2003/05/28 21:00:44 cheshire
1396 Re-enable "immediate answer burst" debugf message
1398 Revision 1.145 2003/05/28 20:57:44 cheshire
1399 <rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
1400 known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
1401 version of mDNSResponder, so for now we should suppress this warning message.
1403 Revision 1.144 2003/05/28 18:05:12 cheshire
1404 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1405 Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
1407 Revision 1.143 2003/05/28 04:31:29 cheshire
1408 <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
1410 Revision 1.142 2003/05/28 03:13:07 cheshire
1411 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1412 Require that the transport protocol be _udp or _tcp
1414 Revision 1.141 2003/05/28 02:19:12 cheshire
1415 <rdar://problem/3270634> Misleading messages generated by iChat
1416 Better fix: Only generate the log message for queries where the TC bit is set.
1418 Revision 1.140 2003/05/28 01:55:24 cheshire
1419 Minor change to log messages
1421 Revision 1.139 2003/05/28 01:52:51 cheshire
1422 <rdar://problem/3270634> Misleading messages generated by iChat
1424 Revision 1.138 2003/05/27 22:35:00 cheshire
1425 <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
1427 Revision 1.137 2003/05/27 20:04:33 cheshire
1428 <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
1430 Revision 1.136 2003/05/27 18:50:07 cheshire
1431 <rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
1433 Revision 1.135 2003/05/26 04:57:28 cheshire
1434 <rdar://problem/3268953> Delay queries when there are already answers in the cache
1436 Revision 1.134 2003/05/26 04:54:54 cheshire
1437 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1438 Accidentally deleted '%' case from the switch statement
1440 Revision 1.133 2003/05/26 03:21:27 cheshire
1441 Tidy up address structure naming:
1442 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
1443 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
1444 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
1446 Revision 1.132 2003/05/26 03:01:26 cheshire
1447 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1449 Revision 1.131 2003/05/26 00:42:05 cheshire
1450 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
1452 Revision 1.130 2003/05/24 16:39:48 cheshire
1453 <rdar://problem/3268631> SendResponses also needs to handle multihoming better
1455 Revision 1.129 2003/05/23 02:15:37 cheshire
1456 Fixed misleading use of the term "duplicate suppression" where it should have
1457 said "known answer suppression". (Duplicate answer suppression is something
1458 different, and duplicate question suppression is yet another thing, so the use
1459 of the completely vague term "duplicate suppression" was particularly bad.)
1461 Revision 1.128 2003/05/23 01:55:13 cheshire
1462 <rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
1464 Revision 1.127 2003/05/23 01:02:15 ksekar
1465 <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
1467 Revision 1.126 2003/05/22 02:29:22 cheshire
1468 <rdar://problem/2984918> SendQueries needs to handle multihoming better
1469 Complete rewrite of SendQueries. Works much better now :-)
1471 Revision 1.125 2003/05/22 01:50:45 cheshire
1472 Fix warnings, and improve log messages
1474 Revision 1.124 2003/05/22 01:41:50 cheshire
1475 DiscardDeregistrations doesn't need InterfaceID parameter
1477 Revision 1.123 2003/05/22 01:38:55 cheshire
1478 Change bracketing of #pragma mark
1480 Revision 1.122 2003/05/21 19:59:04 cheshire
1481 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1482 Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
1484 Revision 1.121 2003/05/21 17:54:07 ksekar
1485 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1486 New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
1488 Revision 1.120 2003/05/19 22:14:14 ksekar
1489 <rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type
1491 Revision 1.119 2003/05/16 01:34:10 cheshire
1494 Revision 1.118 2003/05/14 18:48:40 cheshire
1495 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1496 More minor refinements:
1497 mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
1498 mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
1500 Revision 1.117 2003/05/14 07:08:36 cheshire
1501 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1502 Previously, when there was any network configuration change, mDNSResponder
1503 would tear down the entire list of active interfaces and start again.
1504 That was very disruptive, and caused the entire cache to be flushed,
1505 and caused lots of extra network traffic. Now it only removes interfaces
1506 that have really gone, and only adds new ones that weren't there before.
1508 Revision 1.116 2003/05/14 06:51:56 cheshire
1509 <rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
1511 Revision 1.115 2003/05/14 06:44:31 cheshire
1512 Improve debugging message
1514 Revision 1.114 2003/05/07 01:47:03 cheshire
1515 <rdar://problem/3250330> Also protect against NULL domainlabels
1517 Revision 1.113 2003/05/07 00:28:18 cheshire
1518 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
1520 Revision 1.112 2003/05/06 00:00:46 cheshire
1521 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
1523 Revision 1.111 2003/05/05 23:42:08 cheshire
1524 <rdar://problem/3245631> Resolves never succeed
1525 Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
1526 instead of "rr->LastAPTime = timenow - rr->ThisAPInterval"
1528 Revision 1.110 2003/04/30 21:09:59 cheshire
1529 <rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
1531 Revision 1.109 2003/04/26 02:41:56 cheshire
1532 <rdar://problem/3241281> Change timenow from a local variable to a structure member
1534 Revision 1.108 2003/04/25 01:45:56 cheshire
1535 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
1537 Revision 1.107 2003/04/25 00:41:31 cheshire
1538 <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
1540 Revision 1.106 2003/04/22 03:14:45 cheshire
1541 <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
1543 Revision 1.105 2003/04/22 01:07:43 cheshire
1544 <rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
1545 If TTL parameter is zero, leave record TTL unchanged
1547 Revision 1.104 2003/04/21 19:15:52 cheshire
1548 Fix some compiler warnings
1550 Revision 1.103 2003/04/19 02:26:35 cheshire
1551 <rdar://problem/3233804> Incorrect goodbye packet after conflict
1553 Revision 1.102 2003/04/17 03:06:28 cheshire
1554 <rdar://problem/3231321> No need to query again when a service goes away
1555 Set UnansweredQueries to 2 when receiving a "goodbye" packet
1557 Revision 1.101 2003/04/15 20:58:31 jgraessl
1558 <rdar://problem/3229014> Added a hash to lookup records in the cache.
1560 Revision 1.100 2003/04/15 18:53:14 cheshire
1561 <rdar://problem/3229064> Bug in ScheduleNextTask
1562 mDNS.c 1.94 incorrectly combined two "if" statements into one.
1564 Revision 1.99 2003/04/15 18:09:13 jgraessl
1565 <rdar://problem/3228892>
1566 Reviewed by: Stuart Cheshire
1567 Added code to keep track of when the next cache item will expire so we can
1568 call TidyRRCache only when necessary.
1570 Revision 1.98 2003/04/03 03:43:55 cheshire
1571 <rdar://problem/3216837> Off-by-one error in probe rate limiting
1573 Revision 1.97 2003/04/02 01:48:17 cheshire
1574 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1575 Additional fix pointed out by Josh:
1576 Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
1578 Revision 1.96 2003/04/01 23:58:55 cheshire
1579 Minor comment changes
1581 Revision 1.95 2003/04/01 23:46:05 cheshire
1582 <rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
1583 mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
1584 to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
1585 window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
1586 FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
1588 Revision 1.94 2003/03/29 01:55:19 cheshire
1589 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1590 Solution: Major cleanup of packet timing and conflict handling rules
1592 Revision 1.93 2003/03/28 01:54:36 cheshire
1593 Minor tidyup of IPv6 (AAAA) code
1595 Revision 1.92 2003/03/27 03:30:55 cheshire
1596 <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
1597 Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
1599 1. Make mDNS_DeregisterInterface() safe to call from a callback
1600 2. Make HostNameCallback() use DeadvertiseInterface() instead
1601 (it never really needed to deregister the interface at all)
1603 Revision 1.91 2003/03/15 04:40:36 cheshire
1604 Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
1606 Revision 1.90 2003/03/14 20:26:37 cheshire
1607 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1609 Revision 1.89 2003/03/12 19:57:50 cheshire
1610 Fixed typo in debug message
1612 Revision 1.88 2003/03/12 00:17:44 cheshire
1613 <rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records
1615 Revision 1.87 2003/03/11 01:27:20 cheshire
1616 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1618 Revision 1.86 2003/03/06 20:44:33 cheshire
1621 Revision 1.85 2003/03/05 03:38:35 cheshire
1622 <rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
1623 Fixed by leaving client in list after conflict, until client explicitly deallocates
1625 Revision 1.84 2003/03/05 01:27:30 cheshire
1626 <rdar://problem/3185482> Different TTL for multicast versus unicast responses
1627 When building unicast responses, record TTLs are capped to 10 seconds
1629 Revision 1.83 2003/03/04 23:48:52 cheshire
1630 <rdar://problem/3188865> Double probes after wake from sleep
1631 Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
1633 Revision 1.82 2003/03/04 23:38:29 cheshire
1634 <rdar://problem/3099194> mDNSResponder needs performance improvements
1635 Only set rr->CRActiveQuestion to point to the
1636 currently active representative of a question set
1638 Revision 1.81 2003/02/21 03:35:34 cheshire
1639 <rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
1641 Revision 1.80 2003/02/21 02:47:53 cheshire
1642 <rdar://problem/3099194> mDNSResponder needs performance improvements
1643 Several places in the code were calling CacheRRActive(), which searched the entire
1644 question list every time, to see if this cache resource record answers any question.
1645 Instead, we now have a field "CRActiveQuestion" in the resource record structure
1647 Revision 1.79 2003/02/21 01:54:07 cheshire
1648 <rdar://problem/3099194> mDNSResponder needs performance improvements
1649 Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
1651 Revision 1.78 2003/02/20 06:48:32 cheshire
1652 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
1653 Reviewed by: Josh Graessley, Bob Bradley
1655 Revision 1.77 2003/01/31 03:35:59 cheshire
1656 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1657 When there were *two* active questions in the list, they were incorrectly
1658 finding *each other* and *both* being marked as duplicates of another question
1660 Revision 1.76 2003/01/29 02:46:37 cheshire
1662 A physical interface is identified solely by its InterfaceID (not by IP and type).
1663 On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
1664 In cases where the requested outbound protocol (v4 or v6) is not supported on
1665 that InterfaceID, the platform support layer should simply discard that packet.
1667 Revision 1.75 2003/01/29 01:47:40 cheshire
1668 Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
1670 Revision 1.74 2003/01/28 05:26:25 cheshire
1671 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1672 Add 'Active' flag for interfaces
1674 Revision 1.73 2003/01/28 03:45:12 cheshire
1675 Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
1677 Revision 1.72 2003/01/28 01:49:48 cheshire
1678 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1679 FindDuplicateQuestion() was incorrectly finding the question itself in the list,
1680 and incorrectly marking it as a duplicate (of itself), so that it became inactive.
1682 Revision 1.71 2003/01/28 01:41:44 cheshire
1683 <rdar://problem/3153091> Race condition when network change causes bad stuff
1684 When an interface goes away, interface-specific questions on that interface become orphaned.
1685 Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
1686 Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
1688 Revision 1.70 2003/01/23 19:00:20 cheshire
1689 Protect against infinite loops in mDNS_Execute
1691 Revision 1.69 2003/01/21 22:56:32 jgraessl
1692 <rdar://problem/3124348> service name changes are not properly handled
1693 Submitted by: Stuart Cheshire
1694 Reviewed by: Joshua Graessley
1695 Applying changes for 3124348 to main branch. 3124348 changes went in to a
1698 Revision 1.68 2003/01/17 04:09:27 cheshire
1699 <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
1701 Revision 1.67 2003/01/17 03:56:45 cheshire
1702 Default 24-hour TTL is far too long. Changing to two hours.
1704 Revision 1.66 2003/01/13 23:49:41 jgraessl
1705 Merged changes for the following fixes in to top of tree:
1706 <rdar://problem/3086540> computer name changes not handled properly
1707 <rdar://problem/3124348> service name changes are not properly handled
1708 <rdar://problem/3124352> announcements sent in pairs, failing chattiness test
1710 Revision 1.65 2002/12/23 22:13:28 jgraessl
1711 Reviewed by: Stuart Cheshire
1712 Initial IPv6 support for mDNSResponder.
1714 Revision 1.64 2002/11/26 20:49:06 cheshire
1715 <rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
1717 Revision 1.63 2002/09/21 20:44:49 zarzycki
1720 Revision 1.62 2002/09/20 03:25:37 cheshire
1721 Fix some compiler warnings
1723 Revision 1.61 2002/09/20 01:05:24 cheshire
1724 Don't kill the Extras list in mDNS_DeregisterService()
1726 Revision 1.60 2002/09/19 23:47:35 cheshire
1727 Added mDNS_RegisterNoSuchService() function for assertion of non-existence
1728 of a particular named service
1730 Revision 1.59 2002/09/19 21:25:34 cheshire
1731 mDNS_snprintf() doesn't need to be in a separate file
1733 Revision 1.58 2002/09/19 04:20:43 cheshire
1734 Remove high-ascii characters that confuse some systems
1736 Revision 1.57 2002/09/17 01:07:08 cheshire
1737 Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
1739 Revision 1.56 2002/09/16 19:44:17 cheshire
1740 Merge in license terms from Quinn's copy, in preparation for Darwin release
1743 #include "DNSCommon.h" // Defines general DNS untility routines
1744 #include "uDNS.h" // Defines entry points into unicast-specific routines
1745 // Disable certain benign warnings with Microsoft compilers
1746 #if(defined(_MSC_VER))
1747 // Disable "conditional expression is constant" warning for debug macros.
1748 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
1749 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
1750 #pragma warning(disable:4127)
1752 // Disable "assignment within conditional expression".
1753 // Other compilers understand the convention that if you place the assignment expression within an extra pair
1754 // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
1755 // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
1756 // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
1757 #pragma warning(disable:4706)
1760 // ***************************************************************************
1761 #if COMPILER_LIKES_PRAGMA_MARK
1763 #pragma mark - Program Constants
1766 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
1767 mDNSexport
const mDNSv4Addr zerov4Addr
= { { 0 } };
1768 mDNSexport
const mDNSv6Addr zerov6Addr
= { { 0 } };
1769 mDNSexport
const mDNSEthAddr zeroEthAddr
= { { 0 } };
1770 mDNSexport
const mDNSv4Addr onesIPv4Addr
= { { 255, 255, 255, 255 } };
1771 mDNSexport
const mDNSv6Addr onesIPv6Addr
= { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
1772 mDNSexport
const mDNSAddr zeroAddr
= { mDNSAddrType_None
, {{{ 0 }}} };
1774 mDNSexport
const mDNSInterfaceID mDNSInterface_Any
= 0;
1775 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)1;
1777 mDNSlocal
const mDNSInterfaceID mDNSInterfaceMark
= (mDNSInterfaceID
)~0;
1779 #define UnicastDNSPortAsNumber 53
1780 #define NATPMPPortAsNumber 5351
1781 #define DNSEXTPortAsNumber 5352 // Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
1782 #define MulticastDNSPortAsNumber 5353
1783 #define LoopbackIPCPortAsNumber 5354
1785 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
1786 mDNSexport
const mDNSIPPort NATPMPPort
= { { NATPMPPortAsNumber
>> 8, NATPMPPortAsNumber
& 0xFF } };
1787 mDNSexport
const mDNSIPPort DNSEXTPort
= { { DNSEXTPortAsNumber
>> 8, DNSEXTPortAsNumber
& 0xFF } };
1788 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
1789 mDNSexport
const mDNSIPPort LoopbackIPCPort
= { { LoopbackIPCPortAsNumber
>> 8, LoopbackIPCPortAsNumber
& 0xFF } };
1791 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
1792 mDNSexport
const mDNSv4Addr AllDNSLinkGroupv4
= { { 224, 0, 0, 251 } };
1793 mDNSexport
const mDNSv6Addr AllDNSLinkGroupv6
= { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } };
1794 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
1795 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
1797 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
1798 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
1799 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
1800 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
1801 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
1802 mDNSexport
const mDNSOpaque16 UpdateRespFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
1804 // Any records bigger than this are considered 'large' records
1805 #define SmallRecordLimit 1024
1807 #define kMaxUpdateCredits 10
1808 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1810 mDNSexport
const char *const mDNS_DomainTypeNames
[] =
1812 "b._dns-sd._udp.", // Browse
1813 "db._dns-sd._udp.", // Default Browse
1814 "lb._dns-sd._udp.", // Legacy Browse
1815 "r._dns-sd._udp.", // Registration
1816 "dr._dns-sd._udp." // Default Registration
1819 #ifdef UNICAST_DISABLED
1820 #define uDNS_IsActiveQuery(q, u) mDNSfalse
1823 // ***************************************************************************
1824 #if COMPILER_LIKES_PRAGMA_MARK
1826 #pragma mark - Specialized mDNS version of vsnprintf
1829 static const struct mDNSprintf_format
1831 unsigned leftJustify
: 1;
1832 unsigned forceSign
: 1;
1833 unsigned zeroPad
: 1;
1834 unsigned havePrecision
: 1;
1838 char sign
; // +, - or space
1839 unsigned int fieldWidth
;
1840 unsigned int precision
;
1841 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1843 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
1845 mDNSu32 nwritten
= 0;
1847 if (buflen
== 0) return(0);
1848 buflen
--; // Pre-reserve one space in the buffer for the terminating null
1849 if (buflen
== 0) goto exit
;
1851 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
1855 *sbuffer
++ = (char)c
;
1856 if (++nwritten
>= buflen
) goto exit
;
1860 unsigned int i
=0, j
;
1861 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
1862 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
1863 // The size needs to be enough for a 256-byte domain name plus some error text.
1864 #define mDNS_VACB_Size 300
1865 char mDNS_VACB
[mDNS_VACB_Size
];
1866 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
1867 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
1868 char *s
= mDNS_VACB_Lim
, *digits
;
1869 struct mDNSprintf_format F
= mDNSprintf_format_default
;
1871 while (1) // decode flags
1874 if (c
== '-') F
.leftJustify
= 1;
1875 else if (c
== '+') F
.forceSign
= 1;
1876 else if (c
== ' ') F
.sign
= ' ';
1877 else if (c
== '#') F
.altForm
++;
1878 else if (c
== '0') F
.zeroPad
= 1;
1882 if (c
== '*') // decode field width
1884 int f
= va_arg(arg
, int);
1885 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
1886 F
.fieldWidth
= (unsigned int)f
;
1891 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
1892 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
1895 if (c
== '.') // decode precision
1897 if ((c
= *++fmt
) == '*')
1898 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
1899 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
1900 F
.precision
= (10 * F
.precision
) + (c
- '0');
1901 F
.havePrecision
= 1;
1904 if (F
.leftJustify
) F
.zeroPad
= 0;
1907 switch (c
) // perform appropriate conversion
1910 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
1911 case 'l' : // fall through
1912 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
1914 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
1915 else n
= (unsigned long)va_arg(arg
, int);
1916 if (F
.hSize
) n
= (short) n
;
1917 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
1918 else if (F
.forceSign
) F
.sign
= '+';
1920 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1921 else n
= va_arg(arg
, unsigned int);
1922 if (F
.hSize
) n
= (unsigned short) n
;
1925 decimal
: if (!F
.havePrecision
)
1929 F
.precision
= F
.fieldWidth
;
1930 if (F
.sign
) --F
.precision
;
1932 if (F
.precision
< 1) F
.precision
= 1;
1934 if (F
.precision
> mDNS_VACB_Size
- 1)
1935 F
.precision
= mDNS_VACB_Size
- 1;
1936 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
1937 for (; i
< F
.precision
; i
++) *--s
= '0';
1938 if (F
.sign
) { *--s
= F
.sign
; i
++; }
1941 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1942 else n
= va_arg(arg
, unsigned int);
1943 if (F
.hSize
) n
= (unsigned short) n
;
1944 if (!F
.havePrecision
)
1946 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
1947 if (F
.precision
< 1) F
.precision
= 1;
1949 if (F
.precision
> mDNS_VACB_Size
- 1)
1950 F
.precision
= mDNS_VACB_Size
- 1;
1951 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
1952 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
1953 for (; i
< F
.precision
; i
++) *--s
= '0';
1957 unsigned char *a
= va_arg(arg
, unsigned char *);
1958 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
1961 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
1964 mDNSAddr
*ip
= (mDNSAddr
*)a
;
1967 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
1968 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
1969 default: F
.precision
= 0; break;
1972 switch (F
.precision
)
1974 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
1975 a
[0], a
[1], a
[2], a
[3]); break;
1976 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
1977 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
1978 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
1979 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
1980 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
1981 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
1982 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify address size "
1983 "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
1989 case 'p' : F
.havePrecision
= F
.lSize
= 1;
1991 case 'X' : digits
= "0123456789ABCDEF";
1993 case 'x' : digits
= "0123456789abcdef";
1994 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1995 else n
= va_arg(arg
, unsigned int);
1996 if (F
.hSize
) n
= (unsigned short) n
;
1997 if (!F
.havePrecision
)
2001 F
.precision
= F
.fieldWidth
;
2002 if (F
.altForm
) F
.precision
-= 2;
2004 if (F
.precision
< 1) F
.precision
= 1;
2006 if (F
.precision
> mDNS_VACB_Size
- 1)
2007 F
.precision
= mDNS_VACB_Size
- 1;
2008 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
2009 for (; i
< F
.precision
; i
++) *--s
= '0';
2010 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
2013 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
2015 case 's' : s
= va_arg(arg
, char *);
2016 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
2017 else switch (F
.altForm
)
2020 if (!F
.havePrecision
) // C string
2024 while ((i
< F
.precision
) && s
[i
]) i
++;
2025 // Make sure we don't truncate in the middle of a UTF-8 character
2026 // If last character we got was any kind of UTF-8 multi-byte character,
2027 // then see if we have to back up.
2028 // This is not as easy as the similar checks below, because
2029 // here we can't assume it's safe to examine the *next* byte, so we
2030 // have to confine ourselves to working only backwards in the string.
2031 j
= i
; // Record where we got to
2032 // Now, back up until we find first non-continuation-char
2033 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
2034 // Now s[i-1] is the first non-continuation-char
2035 // and (j-i) is the number of continuation-chars we found
2036 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
2038 i
--; // Tentatively eliminate this start-char as well
2039 // Now (j-i) is the number of characters we're considering eliminating.
2040 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
2041 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
2042 // (with sign extension) then the result has to be 0xFE.
2043 // If this is right, then we reinstate the tentatively eliminated bytes.
2044 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
2048 case 1: i
= (unsigned char) *s
++; break; // Pascal string
2049 case 2: { // DNS label-sequence name
2050 unsigned char *a
= (unsigned char *)s
;
2051 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
2052 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
2055 if (*a
> 63) { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
2056 if (s
+ *a
>= &mDNS_VACB
[254]) { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
2057 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%#s.", a
);
2060 i
= (mDNSu32
)(s
- mDNS_VACB
);
2061 s
= mDNS_VACB
; // Reset s back to the start of the buffer
2065 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
2066 if (F
.havePrecision
&& i
> F
.precision
)
2067 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2070 case 'n' : s
= va_arg(arg
, char *);
2071 if (F
.hSize
) * (short *) s
= (short)nwritten
;
2072 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
2073 else * (int *) s
= (int)nwritten
;
2076 default: s
= mDNS_VACB
;
2077 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
2079 case '%' : *sbuffer
++ = (char)c
;
2080 if (++nwritten
>= buflen
) goto exit
;
2084 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
2087 if (++nwritten
>= buflen
) goto exit
;
2088 } while (i
< --F
.fieldWidth
);
2090 // Make sure we don't truncate in the middle of a UTF-8 character.
2091 // Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the allowed output. If s[i] is a
2092 // UTF-8 continuation character, then we've cut a unicode character in half, so back up 'i' until s[i] is no longer a UTF-8 continuation
2093 // character. (if the input was proprly formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
2094 if (i
> buflen
- nwritten
)
2095 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2096 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
2098 if (nwritten
>= buflen
) goto exit
;
2100 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
2103 if (++nwritten
>= buflen
) goto exit
;
2112 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
2118 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);
2124 // ***************************************************************************
2125 #if COMPILER_LIKES_PRAGMA_MARK
2127 #pragma mark - General Utility Functions
2130 #define InitialQuestionInterval (mDNSPlatformOneSecond/2)
2131 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
2132 #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
2134 mDNSlocal
void SetNextQueryTime(mDNS
*const m
, const DNSQuestion
*const q
)
2136 if (ActiveQuestion(q
))
2137 if (m
->NextScheduledQuery
- (q
->LastQTime
+ q
->ThisQInterval
) > 0)
2138 m
->NextScheduledQuery
= (q
->LastQTime
+ q
->ThisQInterval
);
2141 mDNSlocal CacheGroup
*CacheGroupForName(const mDNS
*const m
, const mDNSu32 slot
, const mDNSu32 namehash
, const domainname
*const name
)
2144 for (cg
= m
->rrcache_hash
[slot
]; cg
; cg
=cg
->next
)
2145 if (cg
->namehash
== namehash
&& SameDomainName(cg
->name
, name
))
2150 mDNSlocal CacheGroup
*CacheGroupForRecord(const mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
2152 return(CacheGroupForName(m
, slot
, rr
->namehash
, rr
->name
));
2155 mDNSlocal mDNSBool
AddressIsLocalSubnet(mDNS
*const m
, const mDNSInterfaceID InterfaceID
, const mDNSAddr
*addr
)
2157 NetworkInterfaceInfo
*intf
;
2159 if (addr
->type
== mDNSAddrType_IPv4
)
2161 if (addr
->ip
.v4
.b
[0] == 169 && addr
->ip
.v4
.b
[1] == 254) return(mDNStrue
);
2162 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2163 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
2164 if (((intf
->ip
.ip
.v4
.NotAnInteger
^ addr
->ip
.v4
.NotAnInteger
) & intf
->mask
.ip
.v4
.NotAnInteger
) == 0)
2168 if (addr
->type
== mDNSAddrType_IPv6
)
2170 if (addr
->ip
.v6
.b
[0] == 0xFE && addr
->ip
.v6
.b
[1] == 0x80) return(mDNStrue
);
2171 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2172 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
2173 if ((((intf
->ip
.ip
.v6
.l
[0] ^ addr
->ip
.v6
.l
[0]) & intf
->mask
.ip
.v6
.l
[0]) == 0) &&
2174 (((intf
->ip
.ip
.v6
.l
[1] ^ addr
->ip
.v6
.l
[1]) & intf
->mask
.ip
.v6
.l
[1]) == 0) &&
2175 (((intf
->ip
.ip
.v6
.l
[2] ^ addr
->ip
.v6
.l
[2]) & intf
->mask
.ip
.v6
.l
[2]) == 0) &&
2176 (((intf
->ip
.ip
.v6
.l
[3] ^ addr
->ip
.v6
.l
[3]) & intf
->mask
.ip
.v6
.l
[3]) == 0))
2183 // Set up a AuthRecord with sensible default values.
2184 // These defaults may be overwritten with new values before mDNS_Register is called
2185 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
2186 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
2188 mDNSPlatformMemZero(&rr
->uDNS_info
, sizeof(uDNS_RegInfo
));
2189 // Don't try to store a TTL bigger than we can represent in platform time units
2190 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
2191 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
2192 else if (ttl
== 0) // And Zero TTL is illegal
2193 ttl
= DefaultTTLforRRType(rrtype
);
2195 // Field Group 1: The actual information pertaining to this resource record
2196 rr
->resrec
.RecordType
= RecordType
;
2197 rr
->resrec
.InterfaceID
= InterfaceID
;
2198 rr
->resrec
.name
= &rr
->namestorage
;
2199 rr
->resrec
.rrtype
= rrtype
;
2200 rr
->resrec
.rrclass
= kDNSClass_IN
;
2201 rr
->resrec
.rroriginalttl
= ttl
;
2202 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
2203 // rr->resrec.rdestimate = set in mDNS_Register_internal
2204 // rr->resrec.rdata = MUST be set by client
2207 rr
->resrec
.rdata
= RDataStorage
;
2210 rr
->resrec
.rdata
= &rr
->rdatastorage
;
2211 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
2214 // Field Group 2: Persistent metadata for Authoritative Records
2215 rr
->Additional1
= mDNSNULL
;
2216 rr
->Additional2
= mDNSNULL
;
2217 rr
->DependentOn
= mDNSNULL
;
2218 rr
->RRSet
= mDNSNULL
;
2219 rr
->RecordCallback
= Callback
;
2220 rr
->RecordContext
= Context
;
2222 rr
->HostTarget
= mDNSfalse
;
2223 rr
->AllowRemoteQuery
= mDNSfalse
;
2224 rr
->ForceMCast
= mDNSfalse
;
2226 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
2228 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
2231 // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
2232 // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
2233 mDNSlocal
void AnswerLocalOnlyQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, AuthRecord
*rr
, mDNSBool AddRecord
)
2235 // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
2236 if (AddRecord
) rr
->LocalAnswer
= mDNStrue
;
2237 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2238 if (q
->QuestionCallback
)
2239 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
2240 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2243 // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers to each,
2244 // stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
2245 // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
2246 // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
2247 mDNSlocal
void AnswerLocalQuestions(mDNS
*const m
, AuthRecord
*rr
, mDNSBool AddRecord
)
2249 if (m
->CurrentQuestion
) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2251 m
->CurrentQuestion
= m
->LocalOnlyQuestions
;
2252 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewLocalOnlyQuestions
)
2254 DNSQuestion
*q
= m
->CurrentQuestion
;
2255 m
->CurrentQuestion
= q
->next
;
2256 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2257 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
2260 // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
2261 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
2263 m
->CurrentQuestion
= m
->Questions
;
2264 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2266 DNSQuestion
*q
= m
->CurrentQuestion
;
2267 m
->CurrentQuestion
= q
->next
;
2268 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2269 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
2273 m
->CurrentQuestion
= mDNSNULL
;
2276 // ***************************************************************************
2277 #if COMPILER_LIKES_PRAGMA_MARK
2279 #pragma mark - Resource Record Utility Functions
2282 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
2284 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
2285 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2286 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2287 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
2289 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2290 (ResourceRecordIsValidAnswer(RR) && \
2291 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
2293 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2294 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
2296 #define InitialAnnounceCount ((mDNSu8)10)
2298 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
2299 // This means that because the announce interval is doubled after sending the first packet, the first
2300 // observed on-the-wire inter-packet interval between announcements is actually one second.
2301 // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
2302 #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
2303 #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
2304 #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
2306 #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
2307 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
2308 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
2310 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
2311 #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
2312 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
2313 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
2315 #define MaxUnansweredQueries 4
2317 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
2318 // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
2319 // TTL and rdata may differ.
2320 // This is used for cache flush management:
2321 // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
2322 // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
2323 mDNSlocal mDNSBool
SameResourceRecordSignature(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
2325 if (!r1
) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse
); }
2326 if (!r2
) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse
); }
2327 if (r1
->InterfaceID
&&
2329 r1
->InterfaceID
!= r2
->InterfaceID
) return(mDNSfalse
);
2330 return(mDNSBool
)(r1
->rrtype
== r2
->rrtype
&& r1
->rrclass
== r2
->rrclass
&& r1
->namehash
== r2
->namehash
&& SameDomainName(r1
->name
, r2
->name
));
2333 // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
2334 // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
2335 // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
2336 // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
2337 // so a response of any type should match, even if it is not actually the type the client plans to use.
2338 mDNSlocal mDNSBool
PacketRRMatchesSignature(const CacheRecord
*const pktrr
, const AuthRecord
*const authrr
)
2340 if (!pktrr
) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse
); }
2341 if (!authrr
) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse
); }
2342 if (pktrr
->resrec
.InterfaceID
&&
2343 authrr
->resrec
.InterfaceID
&&
2344 pktrr
->resrec
.InterfaceID
!= authrr
->resrec
.InterfaceID
) return(mDNSfalse
);
2345 if (!(authrr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) && pktrr
->resrec
.rrtype
!= authrr
->resrec
.rrtype
) return(mDNSfalse
);
2346 return(mDNSBool
)(pktrr
->resrec
.rrclass
== authrr
->resrec
.rrclass
&& pktrr
->resrec
.namehash
== authrr
->resrec
.namehash
&& SameDomainName(pktrr
->resrec
.name
, authrr
->resrec
.name
));
2349 // IdenticalResourceRecord returns true if two resources records have
2350 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
2351 mDNSlocal mDNSBool
IdenticalResourceRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
2353 if (!r1
) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse
); }
2354 if (!r2
) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse
); }
2355 if (r1
->rrtype
!= r2
->rrtype
|| r1
->rrclass
!= r2
->rrclass
|| r1
->namehash
!= r2
->namehash
|| !SameDomainName(r1
->name
, r2
->name
)) return(mDNSfalse
);
2356 return(SameRData(r1
, r2
));
2359 // CacheRecord *ks is the CacheRecord from the known answer list in the query.
2360 // This is the information that the requester believes to be correct.
2361 // AuthRecord *rr is the answer we are proposing to give, if not suppressed.
2362 // This is the information that we believe to be correct.
2363 // We've already determined that we plan to give this answer on this interface
2364 // (either the record is non-specific, or it is specific to this interface)
2365 // so now we just need to check the name, type, class, rdata and TTL.
2366 mDNSlocal mDNSBool
ShouldSuppressKnownAnswer(const CacheRecord
*const ka
, const AuthRecord
*const rr
)
2368 // If RR signature is different, or data is different, then don't suppress our answer
2369 if (!IdenticalResourceRecord(&ka
->resrec
, &rr
->resrec
)) return(mDNSfalse
);
2371 // If the requester's indicated TTL is less than half the real TTL,
2372 // we need to give our answer before the requester's copy expires.
2373 // If the requester's indicated TTL is at least half the real TTL,
2374 // then we can suppress our answer this time.
2375 // If the requester's indicated TTL is greater than the TTL we believe,
2376 // then that's okay, and we don't need to do anything about it.
2377 // (If two responders on the network are offering the same information,
2378 // that's okay, and if they are offering the information with different TTLs,
2379 // the one offering the lower TTL should defer to the one offering the higher TTL.)
2380 return(mDNSBool
)(ka
->resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/ 2);
2383 mDNSlocal
void SetNextAnnounceProbeTime(mDNS
*const m
, const AuthRecord
*const rr
)
2385 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
2387 //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
2388 if (m
->NextScheduledProbe
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
2389 m
->NextScheduledProbe
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
2391 else if (rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
))
2393 if (m
->NextScheduledResponse
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
2394 m
->NextScheduledResponse
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
2398 mDNSlocal
void InitializeLastAPTime(mDNS
*const m
, AuthRecord
*const rr
)
2400 // To allow us to aggregate probes when a group of services are registered together,
2401 // the first probe is delayed 1/4 second. This means the common-case behaviour is:
2402 // 1/4 second wait; probe
2403 // 1/4 second wait; probe
2404 // 1/4 second wait; probe
2405 // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
2407 // If we have no probe suppression time set, or it is in the past, set it now
2408 if (m
->SuppressProbes
== 0 || m
->SuppressProbes
- m
->timenow
< 0)
2410 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ DefaultProbeIntervalForTypeUnique
);
2411 // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
2412 if (m
->SuppressProbes
- m
->NextScheduledProbe
>= 0)
2413 m
->SuppressProbes
= m
->NextScheduledProbe
;
2414 // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
2415 if (m
->SuppressProbes
- m
->NextScheduledQuery
>= 0)
2416 m
->SuppressProbes
= m
->NextScheduledQuery
;
2419 // We announce to flush stale data from other caches. It is a reasonable assumption that any
2420 // old stale copies will probably have the same TTL we're using, so announcing longer than
2421 // this serves no purpose -- any stale copies of that record will have expired by then anyway.
2422 rr
->AnnounceUntil
= m
->timenow
+ TicksTTL(rr
);
2423 rr
->LastAPTime
= m
->SuppressProbes
- rr
->ThisAPInterval
;
2424 // Set LastMCTime to now, to inhibit multicast responses
2425 // (no need to send additional multicast responses when we're announcing anyway)
2426 rr
->LastMCTime
= m
->timenow
;
2427 rr
->LastMCInterface
= mDNSInterfaceMark
;
2429 // If this is a record type that's not going to probe, then delay its first announcement so that
2430 // it will go out synchronized with the first announcement for the other records that *are* probing.
2431 // This is a minor performance tweak that helps keep groups of related records synchronized together.
2432 // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
2433 // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
2434 // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
2435 // because they will meet the criterion of being at least half-way to their scheduled announcement time.
2436 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
)
2437 rr
->LastAPTime
+= DefaultProbeIntervalForTypeUnique
* DefaultProbeCountForTypeUnique
+ rr
->ThisAPInterval
/ 2;
2439 SetNextAnnounceProbeTime(m
, rr
);
2442 #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
2444 mDNSlocal
void SetTargetToHostName(mDNS
*const m
, AuthRecord
*const rr
)
2446 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
2448 if (!target
) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr
->resrec
.rrtype
);
2450 if (target
&& SameDomainName(target
, &m
->MulticastHostname
))
2451 debugf("SetTargetToHostName: Target of %##s is already %##s", rr
->resrec
.name
->c
, target
->c
);
2453 if (target
&& !SameDomainName(target
, &m
->MulticastHostname
))
2455 AssignDomainName(target
, &m
->MulticastHostname
);
2456 SetNewRData(&rr
->resrec
, mDNSNULL
, 0);
2458 // If we're in the middle of probing this record, we need to start again,
2459 // because changing its rdata may change the outcome of the tie-breaker.
2460 // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
2461 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
2463 // If we've announced this record, we really should send a goodbye packet for the old rdata before
2464 // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
2465 // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
2466 if (rr
->RequireGoodbye
&& rr
->resrec
.RecordType
== kDNSRecordTypeShared
)
2467 debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2469 rr
->AnnounceCount
= InitialAnnounceCount
;
2470 rr
->RequireGoodbye
= mDNSfalse
;
2471 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
2472 InitializeLastAPTime(m
,rr
);
2476 mDNSlocal
void AcknowledgeRecord(mDNS
*const m
, AuthRecord
*const rr
)
2478 if (!rr
->Acknowledged
&& rr
->RecordCallback
)
2480 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2481 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2482 rr
->Acknowledged
= mDNStrue
;
2483 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2484 rr
->RecordCallback(m
, rr
, mStatus_NoError
);
2485 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2489 // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
2490 #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
2491 #define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
2493 mDNSlocal mStatus
mDNS_Register_internal(mDNS
*const m
, AuthRecord
*const rr
)
2495 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
2497 AuthRecord
**p
= &m
->ResourceRecords
;
2498 AuthRecord
**d
= &m
->DuplicateRecords
;
2500 mDNSPlatformMemZero(&rr
->uDNS_info
, sizeof(uDNS_RegInfo
));
2502 if ((mDNSs32
)rr
->resrec
.rroriginalttl
<= 0)
2503 { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m
, rr
)); return(mStatus_BadParamErr
); }
2505 #ifndef UNICAST_DISABLED
2506 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| rr
->ForceMCast
|| IsLocalDomain(rr
->resrec
.name
))
2507 rr
->uDNS_info
.id
= zeroID
;
2508 else return uDNS_RegisterRecord(m
, rr
);
2511 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2512 while (*d
&& *d
!= rr
) d
=&(*d
)->next
;
2515 LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the list", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2516 return(mStatus_AlreadyRegistered
);
2519 if (rr
->DependentOn
)
2521 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
2522 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
2525 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
2526 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2527 return(mStatus_Invalid
);
2529 if (!(rr
->DependentOn
->resrec
.RecordType
& (kDNSRecordTypeUnique
| kDNSRecordTypeVerified
)))
2531 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
2532 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->DependentOn
->resrec
.RecordType
);
2533 return(mStatus_Invalid
);
2537 // If this resource record is referencing a specific interface, make sure it exists
2538 if (rr
->resrec
.InterfaceID
&& rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
2540 NetworkInterfaceInfo
*intf
;
2541 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2542 if (intf
->InterfaceID
== rr
->resrec
.InterfaceID
) break;
2545 debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr
->resrec
.InterfaceID
);
2546 return(mStatus_BadReferenceErr
);
2550 rr
->next
= mDNSNULL
;
2552 // Field Group 1: Persistent metadata for Authoritative Records
2553 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2554 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2555 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2556 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2557 // rr->Callback = already set in mDNS_SetupResourceRecord
2558 // rr->Context = already set in mDNS_SetupResourceRecord
2559 // rr->RecordType = already set in mDNS_SetupResourceRecord
2560 // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2561 // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2562 // Make sure target is not uninitialized data, or we may crash writing debugging log messages
2563 if (rr
->HostTarget
&& target
) target
->c
[0] = 0;
2565 // Field Group 2: Transient state for Authoritative Records
2566 rr
->Acknowledged
= mDNSfalse
;
2567 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
2568 rr
->AnnounceCount
= InitialAnnounceCount
;
2569 rr
->RequireGoodbye
= mDNSfalse
;
2570 rr
->LocalAnswer
= mDNSfalse
;
2571 rr
->IncludeInProbe
= mDNSfalse
;
2572 rr
->ImmedAnswer
= mDNSNULL
;
2573 rr
->ImmedUnicast
= mDNSfalse
;
2574 rr
->ImmedAdditional
= mDNSNULL
;
2575 rr
->SendRNow
= mDNSNULL
;
2576 rr
->v4Requester
= zerov4Addr
;
2577 rr
->v6Requester
= zerov6Addr
;
2578 rr
->NextResponse
= mDNSNULL
;
2579 rr
->NR_AnswerTo
= mDNSNULL
;
2580 rr
->NR_AdditionalTo
= mDNSNULL
;
2581 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
2582 if (!rr
->HostTarget
) InitializeLastAPTime(m
, rr
);
2583 // rr->AnnounceUntil = Set for us in InitializeLastAPTime()
2584 // rr->LastAPTime = Set for us in InitializeLastAPTime()
2585 // rr->LastMCTime = Set for us in InitializeLastAPTime()
2586 // rr->LastMCInterface = Set for us in InitializeLastAPTime()
2587 rr
->NewRData
= mDNSNULL
;
2588 rr
->newrdlength
= 0;
2589 rr
->UpdateCallback
= mDNSNULL
;
2590 rr
->UpdateCredits
= kMaxUpdateCredits
;
2591 rr
->NextUpdateCredit
= 0;
2592 rr
->UpdateBlocked
= 0;
2594 // rr->resrec.interface = already set in mDNS_SetupResourceRecord
2595 // rr->resrec.name->c = MUST be set by client
2596 // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
2597 // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
2598 // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
2599 // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
2602 SetTargetToHostName(m
, rr
); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
2605 rr
->resrec
.rdlength
= GetRDLength(&rr
->resrec
, mDNSfalse
);
2606 rr
->resrec
.rdestimate
= GetRDLength(&rr
->resrec
, mDNStrue
);
2609 if (!ValidateDomainName(rr
->resrec
.name
))
2610 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
2612 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
2613 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
2614 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
2615 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& rr
->resrec
.rdlength
== 0) { rr
->resrec
.rdlength
= 1; rr
->resrec
.rdata
->u
.txt
.c
[0] = 0; }
2617 // Don't do this until *after* we've set rr->resrec.rdlength
2618 if (!ValidateRData(rr
->resrec
.rrtype
, rr
->resrec
.rdlength
, rr
->resrec
.rdata
))
2619 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
2621 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2622 rr
->resrec
.rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->resrec
.rdlength
, &rr
->resrec
.rdata
->u
);
2624 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
2626 // If this is supposed to be unique, make sure we don't have any name conflicts
2627 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2629 const AuthRecord
*s1
= rr
->RRSet
? rr
->RRSet
: rr
;
2630 for (r
= m
->ResourceRecords
; r
; r
=r
->next
)
2632 const AuthRecord
*s2
= r
->RRSet
? r
->RRSet
: r
;
2633 if (s1
!= s2
&& SameResourceRecordSignature(&r
->resrec
, &rr
->resrec
) && !SameRData(&r
->resrec
, &rr
->resrec
))
2636 if (r
) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
2638 debugf("Name conflict %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2639 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
2640 rr
->resrec
.rroriginalttl
= 0;
2641 rr
->ImmedAnswer
= mDNSInterfaceMark
;
2642 m
->NextScheduledResponse
= m
->timenow
;
2647 // Now that we've finished building our new record, make sure it's not identical to one we already have
2648 for (r
= m
->ResourceRecords
; r
; r
=r
->next
) if (RecordIsLocalDuplicate(r
, rr
)) break;
2652 debugf("Adding to duplicate list %p %s", rr
, ARDisplayString(m
,rr
));
2654 // If the previous copy of this record is already verified unique,
2655 // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
2656 // Setting ProbeCount to zero will cause SendQueries() to advance this record to
2657 // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
2658 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& r
->resrec
.RecordType
== kDNSRecordTypeVerified
)
2663 debugf("Adding to active record list %p %s", rr
, ARDisplayString(m
,rr
));
2664 if (!m
->NewLocalRecords
) m
->NewLocalRecords
= rr
;
2668 // For records that are not going to probe, acknowledge them right away
2669 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
&& rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
2670 AcknowledgeRecord(m
, rr
);
2672 return(mStatus_NoError
);
2675 mDNSlocal
void RecordProbeFailure(mDNS
*const m
, const AuthRecord
*const rr
)
2677 m
->ProbeFailTime
= m
->timenow
;
2678 m
->NumFailedProbes
++;
2679 // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
2680 // If a bunch of hosts have all been configured with the same name, then they'll all
2681 // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
2682 // up to name-10. After that they'll start adding random increments in the range 1-100,
2683 // so they're more likely to branch out in the available namespace and settle on a set of
2684 // unique names quickly. If after five more tries the host is still conflicting, then we
2685 // may have a serious problem, so we start rate-limiting so we don't melt down the network.
2686 if (m
->NumFailedProbes
>= 15)
2688 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
* 5);
2689 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
2690 m
->NumFailedProbes
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2694 mDNSlocal
void CompleteRDataUpdate(mDNS
*const m
, AuthRecord
*const rr
)
2696 RData
*OldRData
= rr
->resrec
.rdata
;
2697 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
); // Update our rdata
2698 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
2699 if (rr
->UpdateCallback
)
2700 rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
2703 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
2704 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
2705 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
2706 typedef enum { mDNS_Dereg_normal
, mDNS_Dereg_conflict
, mDNS_Dereg_repeat
} mDNS_Dereg_type
;
2708 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
2709 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2710 mDNSlocal mStatus
mDNS_Deregister_internal(mDNS
*const m
, AuthRecord
*const rr
, mDNS_Dereg_type drt
)
2713 mDNSu8 RecordType
= rr
->resrec
.RecordType
;
2714 AuthRecord
**p
= &m
->ResourceRecords
; // Find this record in our list of active records
2716 #ifndef UNICAST_DISABLED
2717 if (!(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| rr
->ForceMCast
|| IsLocalDomain(rr
->resrec
.name
)))
2718 return uDNS_DeregisterRecord(m
, rr
);
2721 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2725 // We found our record on the main list. See if there are any duplicates that need special handling.
2726 if (drt
== mDNS_Dereg_conflict
) // If this was a conflict, see that all duplicates get the same treatment
2728 // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
2729 // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
2730 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
) if (RecordIsLocalDuplicate(r2
, rr
)) r2
->ProbeCount
= 0xFF;
2734 // Before we delete the record (and potentially send a goodbye packet)
2735 // first see if we have a record on the duplicate list ready to take over from it.
2736 AuthRecord
**d
= &m
->DuplicateRecords
;
2737 while (*d
&& !RecordIsLocalDuplicate(*d
, rr
)) d
=&(*d
)->next
;
2740 AuthRecord
*dup
= *d
;
2741 debugf("Duplicate record %p taking over from %p %##s (%s)", dup
, rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2742 *d
= dup
->next
; // Cut replacement record from DuplicateRecords list
2743 dup
->next
= rr
->next
; // And then...
2744 rr
->next
= dup
; // ... splice it in right after the record we're about to delete
2745 dup
->resrec
.RecordType
= rr
->resrec
.RecordType
;
2746 dup
->ProbeCount
= rr
->ProbeCount
;
2747 dup
->AnnounceCount
= rr
->AnnounceCount
;
2748 dup
->RequireGoodbye
= rr
->RequireGoodbye
;
2749 dup
->ImmedAnswer
= rr
->ImmedAnswer
;
2750 dup
->ImmedUnicast
= rr
->ImmedUnicast
;
2751 dup
->ImmedAdditional
= rr
->ImmedAdditional
;
2752 dup
->v4Requester
= rr
->v4Requester
;
2753 dup
->v6Requester
= rr
->v6Requester
;
2754 dup
->ThisAPInterval
= rr
->ThisAPInterval
;
2755 dup
->AnnounceUntil
= rr
->AnnounceUntil
;
2756 dup
->LastAPTime
= rr
->LastAPTime
;
2757 dup
->LastMCTime
= rr
->LastMCTime
;
2758 dup
->LastMCInterface
= rr
->LastMCInterface
;
2759 rr
->RequireGoodbye
= mDNSfalse
;
2765 // We didn't find our record on the main list; try the DuplicateRecords list instead.
2766 p
= &m
->DuplicateRecords
;
2767 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2768 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
2769 if (*p
) rr
->RequireGoodbye
= mDNSfalse
;
2770 if (*p
) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2775 // No need to log an error message if we already know this is a potentially repeated deregistration
2776 if (drt
!= mDNS_Dereg_repeat
)
2777 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2778 return(mStatus_BadReferenceErr
);
2781 // If this is a shared record and we've announced it at least once,
2782 // we need to retract that announcement before we delete the record
2783 if (RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
2785 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2786 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
2787 rr
->resrec
.rroriginalttl
= 0;
2788 rr
->ImmedAnswer
= mDNSInterfaceMark
;
2789 if (m
->NextScheduledResponse
- (m
->timenow
+ mDNSPlatformOneSecond
/10) >= 0)
2790 m
->NextScheduledResponse
= (m
->timenow
+ mDNSPlatformOneSecond
/10);
2794 *p
= rr
->next
; // Cut this record from the list
2795 // If someone is about to look at this, bump the pointer forward
2796 if (m
->CurrentRecord
== rr
) m
->CurrentRecord
= rr
->next
;
2797 if (m
->NewLocalRecords
== rr
) m
->NewLocalRecords
= rr
->next
;
2798 rr
->next
= mDNSNULL
;
2800 if (RecordType
== kDNSRecordTypeUnregistered
)
2801 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
2802 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2803 else if (RecordType
== kDNSRecordTypeDeregistering
)
2804 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
2805 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2808 verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2809 rr
->resrec
.RecordType
= kDNSRecordTypeUnregistered
;
2812 if ((drt
== mDNS_Dereg_conflict
|| drt
== mDNS_Dereg_repeat
) && RecordType
== kDNSRecordTypeShared
)
2813 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
2814 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2816 // If we have an update queued up which never executed, give the client a chance to free that memory
2817 if (rr
->NewRData
) CompleteRDataUpdate(m
, rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
2819 if (rr
->LocalAnswer
) AnswerLocalQuestions(m
, rr
, mDNSfalse
);
2821 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2822 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2823 // In this case the likely client action to the mStatus_MemFree message is to free the memory,
2824 // so any attempt to touch rr after this is likely to lead to a crash.
2825 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2826 if (drt
!= mDNS_Dereg_conflict
)
2828 if (rr
->RecordCallback
) rr
->RecordCallback(m
, rr
, mStatus_MemFree
); // MUST NOT touch rr after this
2832 RecordProbeFailure(m
, rr
);
2833 if (rr
->RecordCallback
) rr
->RecordCallback(m
, rr
, mStatus_NameConflict
); // MUST NOT touch rr after this
2834 // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
2835 // Note that with all the client callbacks going on, by the time we get here all the
2836 // records we marked may have been explicitly deregistered by the client anyway.
2837 r2
= m
->DuplicateRecords
;
2840 if (r2
->ProbeCount
!= 0xFF) r2
= r2
->next
;
2841 else { mDNS_Deregister_internal(m
, r2
, mDNS_Dereg_conflict
); r2
= m
->DuplicateRecords
; }
2844 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2846 return(mStatus_NoError
);
2849 // ***************************************************************************
2850 #if COMPILER_LIKES_PRAGMA_MARK
2853 #pragma mark - Packet Sending Functions
2856 mDNSlocal
void AddRecordToResponseList(AuthRecord
***nrpp
, AuthRecord
*rr
, AuthRecord
*add
)
2858 if (rr
->NextResponse
== mDNSNULL
&& *nrpp
!= &rr
->NextResponse
)
2861 // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
2862 // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
2863 // The referenced record will definitely be acceptable (by recursive application of this rule)
2864 if (add
&& add
->NR_AdditionalTo
) add
= add
->NR_AdditionalTo
;
2865 rr
->NR_AdditionalTo
= add
;
2866 *nrpp
= &rr
->NextResponse
;
2868 debugf("AddRecordToResponseList: %##s (%s) already in list", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2871 mDNSlocal
void AddAdditionalsToResponseList(mDNS
*const m
, AuthRecord
*ResponseRecords
, AuthRecord
***nrpp
, const mDNSInterfaceID InterfaceID
)
2873 AuthRecord
*rr
, *rr2
;
2874 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // For each record we plan to put
2876 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
2877 // later in the "for" loop, and we will follow further "additional" links then.)
2878 if (rr
->Additional1
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional1
, InterfaceID
))
2879 AddRecordToResponseList(nrpp
, rr
->Additional1
, rr
);
2881 if (rr
->Additional2
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional2
, InterfaceID
))
2882 AddRecordToResponseList(nrpp
, rr
->Additional2
, rr
);
2884 // For SRV records, automatically add the Address record(s) for the target host
2885 if (rr
->resrec
.rrtype
== kDNSType_SRV
)
2886 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
2887 if (RRTypeIsAddressType(rr2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
2888 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceID
) && // ... which are valid for answer ...
2889 rr
->resrec
.rdatahash
== rr2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
2890 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, rr2
->resrec
.name
))
2891 AddRecordToResponseList(nrpp
, rr2
, rr
);
2895 mDNSlocal
void SendDelayedUnicastResponse(mDNS
*const m
, const mDNSAddr
*const dest
, const mDNSInterfaceID InterfaceID
)
2898 AuthRecord
*ResponseRecords
= mDNSNULL
;
2899 AuthRecord
**nrp
= &ResponseRecords
;
2901 // Make a list of all our records that need to be unicast to this destination
2902 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2904 // If we find we can no longer unicast this answer, clear ImmedUnicast
2905 if (rr
->ImmedAnswer
== mDNSInterfaceMark
||
2906 mDNSSameIPv4Address(rr
->v4Requester
, onesIPv4Addr
) ||
2907 mDNSSameIPv6Address(rr
->v6Requester
, onesIPv6Addr
) )
2908 rr
->ImmedUnicast
= mDNSfalse
;
2910 if (rr
->ImmedUnicast
&& rr
->ImmedAnswer
== InterfaceID
)
2911 if ((dest
->type
== mDNSAddrType_IPv4
&& mDNSSameIPv4Address(rr
->v4Requester
, dest
->ip
.v4
)) ||
2912 (dest
->type
== mDNSAddrType_IPv6
&& mDNSSameIPv6Address(rr
->v6Requester
, dest
->ip
.v6
)))
2914 rr
->ImmedAnswer
= mDNSNULL
; // Clear the state fields
2915 rr
->ImmedUnicast
= mDNSfalse
;
2916 rr
->v4Requester
= zerov4Addr
;
2917 rr
->v6Requester
= zerov6Addr
;
2918 if (rr
->NextResponse
== mDNSNULL
&& nrp
!= &rr
->NextResponse
) // rr->NR_AnswerTo
2919 { rr
->NR_AnswerTo
= (mDNSu8
*)~0; *nrp
= rr
; nrp
= &rr
->NextResponse
; }
2923 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
2925 while (ResponseRecords
)
2927 mDNSu8
*responseptr
= m
->omsg
.data
;
2929 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
2931 // Put answers in the packet
2932 while (ResponseRecords
&& ResponseRecords
->NR_AnswerTo
)
2934 rr
= ResponseRecords
;
2935 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2936 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
2937 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
2938 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
2939 if (!newptr
&& m
->omsg
.h
.numAnswers
) break; // If packet full, send it now
2940 if (newptr
) responseptr
= newptr
;
2941 ResponseRecords
= rr
->NextResponse
;
2942 rr
->NextResponse
= mDNSNULL
;
2943 rr
->NR_AnswerTo
= mDNSNULL
;
2944 rr
->NR_AdditionalTo
= mDNSNULL
;
2945 rr
->RequireGoodbye
= mDNStrue
;
2948 // Add additionals, if there's space
2949 while (ResponseRecords
&& !ResponseRecords
->NR_AnswerTo
)
2951 rr
= ResponseRecords
;
2952 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2953 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
2954 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
2955 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
2957 if (newptr
) responseptr
= newptr
;
2958 if (newptr
&& m
->omsg
.h
.numAnswers
) rr
->RequireGoodbye
= mDNStrue
;
2959 else if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) rr
->ImmedAnswer
= mDNSInterfaceMark
;
2960 ResponseRecords
= rr
->NextResponse
;
2961 rr
->NextResponse
= mDNSNULL
;
2962 rr
->NR_AnswerTo
= mDNSNULL
;
2963 rr
->NR_AdditionalTo
= mDNSNULL
;
2966 if (m
->omsg
.h
.numAnswers
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, mDNSInterface_Any
, dest
, MulticastDNSPort
, -1, mDNSNULL
);
2970 mDNSlocal
void CompleteDeregistration(mDNS
*const m
, AuthRecord
*rr
)
2972 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal()
2973 // that it should go ahead and immediately dispose of this registration
2974 rr
->resrec
.RecordType
= kDNSRecordTypeShared
;
2975 rr
->RequireGoodbye
= mDNSfalse
;
2976 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
); // Don't touch rr after this
2979 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
2980 // the record list and/or question list.
2981 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2982 mDNSlocal
void DiscardDeregistrations(mDNS
*const m
)
2984 if (m
->CurrentRecord
) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
2985 m
->CurrentRecord
= m
->ResourceRecords
;
2987 while (m
->CurrentRecord
)
2989 AuthRecord
*rr
= m
->CurrentRecord
;
2990 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
2991 CompleteDeregistration(m
, rr
); // Don't touch rr after this
2993 m
->CurrentRecord
= rr
->next
;
2997 mDNSlocal
void GrantUpdateCredit(AuthRecord
*rr
)
2999 if (++rr
->UpdateCredits
>= kMaxUpdateCredits
) rr
->NextUpdateCredit
= 0;
3000 else rr
->NextUpdateCredit
= NonZeroTime(rr
->NextUpdateCredit
+ kUpdateCreditRefreshInterval
);
3003 // Note about acceleration of announcements to facilitate automatic coalescing of
3004 // multiple independent threads of announcements into a single synchronized thread:
3005 // The announcements in the packet may be at different stages of maturity;
3006 // One-second interval, two-second interval, four-second interval, and so on.
3007 // After we've put in all the announcements that are due, we then consider
3008 // whether there are other nearly-due announcements that are worth accelerating.
3009 // To be eligible for acceleration, a record MUST NOT be older (further along
3010 // its timeline) than the most mature record we've already put in the packet.
3011 // In other words, younger records can have their timelines accelerated to catch up
3012 // with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
3013 // Older records cannot have their timelines accelerated; this would just widen
3014 // the gap between them and their younger bretheren and get them even more out of sync.
3016 // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
3017 // the record list and/or question list.
3018 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3019 mDNSlocal
void SendResponses(mDNS
*const m
)
3022 AuthRecord
*rr
, *r2
;
3023 mDNSs32 maxExistingAnnounceInterval
= 0;
3024 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
3026 m
->NextScheduledResponse
= m
->timenow
+ 0x78000000;
3028 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3029 if (rr
->ImmedUnicast
)
3031 mDNSAddr v4
= { mDNSAddrType_IPv4
, {{{0}}} };
3032 mDNSAddr v6
= { mDNSAddrType_IPv6
, {{{0}}} };
3033 v4
.ip
.v4
= rr
->v4Requester
;
3034 v6
.ip
.v6
= rr
->v6Requester
;
3035 if (!mDNSIPv4AddressIsZero(rr
->v4Requester
)) SendDelayedUnicastResponse(m
, &v4
, rr
->ImmedAnswer
);
3036 if (!mDNSIPv6AddressIsZero(rr
->v6Requester
)) SendDelayedUnicastResponse(m
, &v6
, rr
->ImmedAnswer
);
3037 if (rr
->ImmedUnicast
)
3039 LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m
, rr
));
3040 rr
->ImmedUnicast
= mDNSfalse
;
3045 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
3048 // Run through our list of records, and decide which ones we're going to announce on all interfaces
3049 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3051 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
3052 if (TimeToAnnounceThisRecord(rr
, m
->timenow
) && ResourceRecordIsValidAnswer(rr
))
3054 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
3055 if (maxExistingAnnounceInterval
< rr
->ThisAPInterval
)
3056 maxExistingAnnounceInterval
= rr
->ThisAPInterval
;
3057 if (rr
->UpdateBlocked
) rr
->UpdateBlocked
= 0;
3061 // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
3062 // Eligible records that are more than half-way to their announcement time are accelerated
3063 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3064 if ((rr
->resrec
.InterfaceID
&& rr
->ImmedAnswer
) ||
3065 (rr
->ThisAPInterval
<= maxExistingAnnounceInterval
&&
3066 TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2) &&
3067 ResourceRecordIsValidAnswer(rr
)))
3068 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
3070 // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
3071 // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
3072 // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
3073 // then all that means is that it won't get sent -- which would not be the end of the world.
3074 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3075 if (rr
->ImmedAnswer
&& rr
->resrec
.rrtype
== kDNSType_SRV
)
3076 for (r2
=m
->ResourceRecords
; r2
; r2
=r2
->next
) // Scan list of resource records
3077 if (RRTypeIsAddressType(r2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
3078 ResourceRecordIsValidAnswer(r2
) && // ... which are valid for answer ...
3079 rr
->LastMCTime
- r2
->LastMCTime
>= 0 && // ... which we have not sent recently ...
3080 rr
->resrec
.rdatahash
== r2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
3081 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, r2
->resrec
.name
) &&
3082 (rr
->ImmedAnswer
== mDNSInterfaceMark
|| rr
->ImmedAnswer
== r2
->resrec
.InterfaceID
))
3083 r2
->ImmedAdditional
= r2
->resrec
.InterfaceID
; // ... then mark this address record for sending too
3085 // If there's a record which is supposed to be unique that we're going to send, then make sure that we give
3086 // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
3087 // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
3088 // record, then other RRSet members that have not been sent recently will get flushed out of client caches.
3089 // -- 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
3090 // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
3091 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3092 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3094 if (rr
->ImmedAnswer
) // If we're sending this as answer, see that its whole RRSet is similarly marked
3096 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
3097 if (ResourceRecordIsValidAnswer(r2
))
3098 if (r2
->ImmedAnswer
!= mDNSInterfaceMark
&& r2
->ImmedAnswer
!= rr
->ImmedAnswer
&& SameResourceRecordSignature(&r2
->resrec
, &rr
->resrec
))
3099 r2
->ImmedAnswer
= rr
->ImmedAnswer
;
3101 else if (rr
->ImmedAdditional
) // If we're sending this as additional, see that its whole RRSet is similarly marked
3103 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
3104 if (ResourceRecordIsValidAnswer(r2
))
3105 if (r2
->ImmedAdditional
!= rr
->ImmedAdditional
&& SameResourceRecordSignature(&r2
->resrec
, &rr
->resrec
))
3106 r2
->ImmedAdditional
= rr
->ImmedAdditional
;
3110 // Now set SendRNow state appropriately
3111 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3113 if (rr
->ImmedAnswer
== mDNSInterfaceMark
) // Sending this record on all appropriate interfaces
3115 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
3116 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional if sending as answer
3117 rr
->LastMCTime
= m
->timenow
;
3118 rr
->LastMCInterface
= rr
->ImmedAnswer
;
3119 // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
3120 if (TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2))
3122 rr
->AnnounceCount
--;
3123 rr
->ThisAPInterval
*= 2;
3124 rr
->LastAPTime
= m
->timenow
;
3125 if (rr
->LastAPTime
+ rr
->ThisAPInterval
- rr
->AnnounceUntil
>= 0) rr
->AnnounceCount
= 0;
3126 debugf("Announcing %##s (%s) %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->AnnounceCount
);
3129 else if (rr
->ImmedAnswer
) // Else, just respond to a single query on single interface:
3131 rr
->SendRNow
= rr
->ImmedAnswer
; // Just respond on that interface
3132 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional too
3133 rr
->LastMCTime
= m
->timenow
;
3134 rr
->LastMCInterface
= rr
->ImmedAnswer
;
3136 SetNextAnnounceProbeTime(m
, rr
);
3137 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
3141 // *** 2. Loop through interface list, sending records as appropriate
3147 int numAnnounce
= 0;
3149 mDNSu8
*responseptr
= m
->omsg
.data
;
3151 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
3153 // First Pass. Look for:
3154 // 1. Deregistering records that need to send their goodbye packet
3155 // 2. Updated records that need to retract their old data
3156 // 3. Answers and announcements we need to send
3157 // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
3158 // send this packet and then try again.
3159 // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
3160 // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
3161 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3162 if (rr
->SendRNow
== intf
->InterfaceID
)
3164 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
3166 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
3167 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3169 responseptr
= newptr
;
3171 else if (rr
->NewRData
&& !m
->SleepState
) // If we have new data for this record
3173 RData
*OldRData
= rr
->resrec
.rdata
;
3174 mDNSu16 oldrdlength
= rr
->resrec
.rdlength
;
3175 // See if we should send a courtesy "goodbye" for the old data before we replace it.
3176 if (ResourceRecordIsValidAnswer(rr
) && rr
->RequireGoodbye
)
3178 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
3179 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3181 responseptr
= newptr
;
3182 rr
->RequireGoodbye
= mDNSfalse
;
3184 // Now try to see if we can fit the update in the same packet (not fatal if we can't)
3185 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
);
3186 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3187 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3188 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
3189 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3190 if (newptr
) { responseptr
= newptr
; rr
->RequireGoodbye
= mDNStrue
; }
3191 SetNewRData(&rr
->resrec
, OldRData
, oldrdlength
);
3195 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3196 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3197 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, m
->SleepState
? 0 : rr
->resrec
.rroriginalttl
);
3198 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3199 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3200 rr
->RequireGoodbye
= (mDNSu8
) (!m
->SleepState
);
3201 if (rr
->LastAPTime
== m
->timenow
) numAnnounce
++; else numAnswer
++;
3202 responseptr
= newptr
;
3204 // If sending on all interfaces, go to next interface; else we're finished now
3205 if (rr
->ImmedAnswer
== mDNSInterfaceMark
&& rr
->resrec
.InterfaceID
== mDNSInterface_Any
)
3206 rr
->SendRNow
= GetNextActiveInterfaceID(intf
);
3208 rr
->SendRNow
= mDNSNULL
;
3211 // Second Pass. Add additional records, if there's space.
3212 newptr
= responseptr
;
3213 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3214 if (rr
->ImmedAdditional
== intf
->InterfaceID
)
3215 if (ResourceRecordIsValidAnswer(rr
))
3217 // If we have at least one answer already in the packet, then plan to add additionals too
3218 mDNSBool SendAdditional
= (m
->omsg
.h
.numAnswers
> 0);
3220 // If we're not planning to send any additionals, but this record is a unique one, then
3221 // make sure we haven't already sent any other members of its RRSet -- if we have, then they
3222 // will have had the cache flush bit set, so now we need to finish the job and send the rest.
3223 if (!SendAdditional
&& (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
))
3225 const AuthRecord
*a
;
3226 for (a
= m
->ResourceRecords
; a
; a
=a
->next
)
3227 if (a
->LastMCTime
== m
->timenow
&&
3228 a
->LastMCInterface
== intf
->InterfaceID
&&
3229 SameResourceRecordSignature(&a
->resrec
, &rr
->resrec
)) { SendAdditional
= mDNStrue
; break; }
3231 if (!SendAdditional
) // If we don't want to send this after all,
3232 rr
->ImmedAdditional
= mDNSNULL
; // then cancel its ImmedAdditional field
3233 else if (newptr
) // Else, try to add it if we can
3235 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3236 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3237 newptr
= PutResourceRecord(&m
->omsg
, newptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
3238 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3241 responseptr
= newptr
;
3242 rr
->ImmedAdditional
= mDNSNULL
;
3243 rr
->RequireGoodbye
= mDNStrue
;
3244 // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
3245 // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
3246 // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
3247 // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
3248 rr
->LastMCTime
= m
->timenow
;
3249 rr
->LastMCInterface
= intf
->InterfaceID
;
3254 if (m
->omsg
.h
.numAnswers
> 0 || m
->omsg
.h
.numAdditionals
)
3256 debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
3257 numDereg
, numDereg
== 1 ? "" : "s",
3258 numAnnounce
, numAnnounce
== 1 ? "" : "s",
3259 numAnswer
, numAnswer
== 1 ? "" : "s",
3260 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s", intf
->InterfaceID
);
3261 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, -1, mDNSNULL
);
3262 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, -1, mDNSNULL
);
3263 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
3264 if (++pktcount
>= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount
); break; }
3265 // There might be more things to send on this interface, so go around one more time and try again.
3267 else // Nothing more to send on this interface; go to next
3269 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
3270 #if MDNS_DEBUGMSGS && 0
3271 const char *const msg
= next
? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
3272 debugf(msg
, intf
, next
);
3279 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3282 if (m
->CurrentRecord
) LogMsg("SendResponses: ERROR m->CurrentRecord already set");
3283 m
->CurrentRecord
= m
->ResourceRecords
;
3284 while (m
->CurrentRecord
)
3286 rr
= m
->CurrentRecord
;
3287 m
->CurrentRecord
= rr
->next
;
3291 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
3292 LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m
, rr
));
3293 rr
->SendRNow
= mDNSNULL
;
3296 if (rr
->ImmedAnswer
)
3298 if (rr
->NewRData
) CompleteRDataUpdate(m
,rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
3300 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
3301 CompleteDeregistration(m
, rr
); // Don't touch rr after this
3304 rr
->ImmedAnswer
= mDNSNULL
;
3305 rr
->ImmedUnicast
= mDNSfalse
;
3306 rr
->v4Requester
= zerov4Addr
;
3307 rr
->v6Requester
= zerov6Addr
;
3311 verbosedebugf("SendResponses: Next in %ld ticks", m
->NextScheduledResponse
- m
->timenow
);
3314 // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
3315 // so we want to be lazy about how frequently we do it.
3316 // 1. If a cache record is currently referenced by *no* active questions,
3317 // then we don't mind expiring it up to a minute late (who will know?)
3318 // 2. Else, if a cache record is due for some of its final expiration queries,
3319 // we'll allow them to be late by up to 2% of the TTL
3320 // 3. Else, if a cache record has completed all its final expiration queries without success,
3321 // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
3322 // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
3323 // so allow at most 1/10 second lateness
3324 #define CacheCheckGracePeriod(RR) ( \
3325 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
3326 ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \
3327 ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \
3328 ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10))
3330 // Note: MUST call SetNextCacheCheckTime any time we change:
3332 // rr->resrec.rroriginalttl
3333 // rr->UnansweredQueries
3334 // rr->CRActiveQuestion
3335 // Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary
3336 // Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime
3337 mDNSlocal
void SetNextCacheCheckTime(mDNS
*const m
, CacheRecord
*const rr
)
3339 rr
->NextRequiredQuery
= RRExpireTime(rr
);
3341 // If we have an active question, then see if we want to schedule a refresher query for this record.
3342 // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
3343 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
3345 rr
->NextRequiredQuery
-= TicksTTL(rr
)/20 * (MaxUnansweredQueries
- rr
->UnansweredQueries
);
3346 rr
->NextRequiredQuery
+= mDNSRandom((mDNSu32
)TicksTTL(rr
)/50);
3347 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec",
3348 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), (rr
->NextRequiredQuery
- m
->timenow
) / mDNSPlatformOneSecond
);
3351 if (m
->NextCacheCheck
- (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
)) > 0)
3352 m
->NextCacheCheck
= (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
));
3354 if (rr
->DelayDelivery
)
3355 if (m
->NextCacheCheck
- rr
->DelayDelivery
> 0)
3356 m
->NextCacheCheck
= rr
->DelayDelivery
;
3359 #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
3360 #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
3361 #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
3363 mDNSlocal mStatus
mDNS_Reconfirm_internal(mDNS
*const m
, CacheRecord
*const rr
, mDNSu32 interval
)
3365 if (interval
< kMinimumReconfirmTime
)
3366 interval
= kMinimumReconfirmTime
;
3367 if (interval
> 0x10000000) // Make sure interval doesn't overflow when we multiply by four below
3368 interval
= 0x10000000;
3370 // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
3371 if (RRExpireTime(rr
) - m
->timenow
> (mDNSs32
)((interval
* 4) / 3))
3373 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
3374 interval
+= mDNSRandom(interval
/3);
3375 rr
->TimeRcvd
= m
->timenow
- (mDNSs32
)interval
* 3;
3376 rr
->resrec
.rroriginalttl
= interval
* 4 / mDNSPlatformOneSecond
;
3377 SetNextCacheCheckTime(m
, rr
);
3379 debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr
) - m
->timenow
, CRDisplayString(m
, rr
));
3380 return(mStatus_NoError
);
3383 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
3385 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
3386 // It also appends to the list of known answer records that need to be included,
3387 // and updates the forcast for the size of the known answer section.
3388 mDNSlocal mDNSBool
BuildQuestion(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
, DNSQuestion
*q
,
3389 CacheRecord
***kalistptrptr
, mDNSu32
*answerforecast
)
3391 mDNSBool ucast
= (q
->LargeAnswers
|| q
->RequestUnicast
) && m
->CanReceiveUnicastOn5353
;
3392 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
3393 const mDNSu8
*const limit
= query
->data
+ NormalMaxDNSMessageData
;
3394 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &q
->qname
, q
->qtype
, (mDNSu16
)(q
->qclass
| ucbit
));
3397 debugf("BuildQuestion: No more space in this packet for question %##s", q
->qname
.c
);
3400 else if (newptr
+ *answerforecast
>= limit
)
3402 verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q
->qname
.c
, newptr
+ *answerforecast
- query
->data
);
3403 query
->h
.numQuestions
--;
3408 mDNSu32 forecast
= *answerforecast
;
3409 const mDNSu32 slot
= HashSlot(&q
->qname
);
3410 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
3412 CacheRecord
**ka
= *kalistptrptr
; // Make a working copy of the pointer we're going to update
3414 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
3415 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
3416 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not already in the known answer list
3417 rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
3418 ResourceRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
3419 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
> // and its half-way-to-expiry time is at least 1 second away
3420 mDNSPlatformOneSecond
) // (also ensures we never include goodbye records with TTL=1)
3422 *ka
= rr
; // Link this record into our known answer chain
3423 ka
= &rr
->NextInKAList
;
3424 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3425 forecast
+= 12 + rr
->resrec
.rdestimate
;
3426 // If we're trying to put more than one question in this packet, and it doesn't fit
3427 // then undo that last question and try again next time
3428 if (query
->h
.numQuestions
> 1 && newptr
+ forecast
>= limit
)
3430 debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
3431 q
->qname
.c
, DNSTypeName(q
->qtype
), newptr
+ forecast
- query
->data
);
3432 query
->h
.numQuestions
--;
3433 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
3434 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
3435 return(mDNSfalse
); // Return false, so we'll try again in the next packet
3439 // Traffic reduction:
3440 // If we already have at least one unique answer in the cache,
3441 // OR we have so many shared answers that the KA list is too big to fit in one packet
3442 // The we suppress queries number 3 and 5:
3443 // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies)
3444 // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally)
3445 // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress)
3446 // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally)
3447 // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress)
3448 // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally)
3449 if (q
->UniqueAnswers
|| newptr
+ forecast
>= limit
)
3450 if (q
->ThisQInterval
== InitialQuestionInterval
* 8 || q
->ThisQInterval
== InitialQuestionInterval
* 32)
3452 query
->h
.numQuestions
--;
3453 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
3454 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
3455 return(mDNStrue
); // Return true: pretend we succeeded, even though we actually suppressed this question
3458 // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
3459 *queryptr
= newptr
; // Update the packet pointer
3460 *answerforecast
= forecast
; // Update the forecast
3461 *kalistptrptr
= ka
; // Update the known answer list pointer
3462 if (ucast
) m
->ExpectUnicastResponse
= m
->timenow
;
3464 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // For every resource record in our cache,
3465 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
3466 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not in the known answer list
3467 ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) // which answers our question
3469 rr
->UnansweredQueries
++; // indicate that we're expecting a response
3470 rr
->LastUnansweredTime
= m
->timenow
;
3471 SetNextCacheCheckTime(m
, rr
);
3478 mDNSlocal
void ReconfirmAntecedents(mDNS
*const m
, DNSQuestion
*q
)
3484 FORALL_CACHERECORDS(slot
, cg
, rr
)
3485 if ((target
= GetRRDomainNameTarget(&rr
->resrec
)) && rr
->resrec
.rdatahash
== q
->qnamehash
&& SameDomainName(target
, &q
->qname
))
3486 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
3489 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
3490 mDNSlocal
void ExpireDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
)
3493 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
3496 mDNSlocal
void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
, mDNSInterfaceID InterfaceID
)
3499 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
3502 mDNSlocal mDNSBool
SuppressOnThisInterface(const DupSuppressInfo ds
[DupSuppressInfoSize
], const NetworkInterfaceInfo
* const intf
)
3505 mDNSBool v4
= !intf
->IPv4Available
; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
3506 mDNSBool v6
= !intf
->IPv6Available
; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
3507 for (i
=0; i
<DupSuppressInfoSize
; i
++)
3508 if (ds
[i
].InterfaceID
== intf
->InterfaceID
)
3510 if (ds
[i
].Type
== mDNSAddrType_IPv4
) v4
= mDNStrue
;
3511 else if (ds
[i
].Type
== mDNSAddrType_IPv6
) v6
= mDNStrue
;
3512 if (v4
&& v6
) return(mDNStrue
);
3517 mDNSlocal
int RecordDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 Time
, mDNSInterfaceID InterfaceID
, mDNSs32 Type
)
3521 // See if we have this one in our list somewhere already
3522 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Type
== Type
) break;
3524 // If not, find a slot we can re-use
3525 if (i
>= DupSuppressInfoSize
)
3528 for (j
=1; j
<DupSuppressInfoSize
&& ds
[i
].InterfaceID
; j
++)
3529 if (!ds
[j
].InterfaceID
|| ds
[j
].Time
- ds
[i
].Time
< 0)
3533 // Record the info about this query we saw
3535 ds
[i
].InterfaceID
= InterfaceID
;
3541 mDNSlocal mDNSBool
AccelerateThisQuery(mDNS
*const m
, DNSQuestion
*q
)
3543 // If more than 90% of the way to the query time, we should unconditionally accelerate it
3544 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/10))
3547 // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
3548 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/2))
3550 // We forecast: qname (n) type (2) class (2)
3551 mDNSu32 forecast
= (mDNSu32
)DomainNameLength(&q
->qname
) + 4;
3552 const mDNSu32 slot
= HashSlot(&q
->qname
);
3553 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
3555 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
3556 if (rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
3557 ResourceRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
3558 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
>= 0 && // and it is less than half-way to expiry
3559 rr
->NextRequiredQuery
- (m
->timenow
+ q
->ThisQInterval
) > 0)// and we'll ask at least once again before NextRequiredQuery
3561 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3562 forecast
+= 12 + rr
->resrec
.rdestimate
;
3563 if (forecast
>= 512) return(mDNSfalse
); // If this would add 512 bytes or more to the packet, don't accelerate
3571 // How Standard Queries are generated:
3572 // 1. The Question Section contains the question
3573 // 2. The Additional Section contains answers we already know, to suppress duplicate responses
3575 // How Probe Queries are generated:
3576 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
3577 // if some other host is already using *any* records with this name, we want to know about it.
3578 // 2. The Authority Section contains the proposed values we intend to use for one or more
3579 // of our records with that name (analogous to the Update section of DNS Update packets)
3580 // because if some other host is probing at the same time, we each want to know what the other is
3581 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
3583 mDNSlocal
void SendQueries(mDNS
*const m
)
3587 // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
3588 mDNSs32 maxExistingQuestionInterval
= 0;
3589 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
3590 CacheRecord
*KnownAnswerList
= mDNSNULL
;
3592 // 1. If time for a query, work out what we need to do
3593 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3598 m
->NextScheduledQuery
= m
->timenow
+ 0x78000000;
3600 // We're expecting to send a query anyway, so see if any expiring cache records are close enough
3601 // to their NextRequiredQuery to be worth batching them together with this one
3602 FORALL_CACHERECORDS(slot
, cg
, rr
)
3603 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
3604 if (m
->timenow
+ TicksTTL(rr
)/50 - rr
->NextRequiredQuery
>= 0)
3606 q
= rr
->CRActiveQuestion
;
3607 ExpireDupSuppressInfoOnInterface(q
->DupSuppress
, m
->timenow
- TicksTTL(rr
)/20, rr
->resrec
.InterfaceID
);
3608 if (q
->Target
.type
) q
->SendQNow
= mDNSInterfaceMark
; // If unicast query, mark it
3609 else if (q
->SendQNow
== mDNSNULL
) q
->SendQNow
= rr
->resrec
.InterfaceID
;
3610 else if (q
->SendQNow
!= rr
->resrec
.InterfaceID
) q
->SendQNow
= mDNSInterfaceMark
;
3613 // Scan our list of questions to see which *unicast* queries need to be sent
3614 for (q
= m
->Questions
; q
; q
=q
->next
)
3615 if (q
->Target
.type
&& (q
->SendQNow
|| TimeToSendThisQuestion(q
, m
->timenow
)))
3617 mDNSu8
*qptr
= m
->omsg
.data
;
3618 const mDNSu8
*const limit
= m
->omsg
.data
+ sizeof(m
->omsg
.data
);
3619 InitializeDNSMessage(&m
->omsg
.h
, q
->TargetQID
, QueryFlags
);
3620 qptr
= putQuestion(&m
->omsg
, qptr
, limit
, &q
->qname
, q
->qtype
, q
->qclass
);
3621 mDNSSendDNSMessage(m
, &m
->omsg
, qptr
, mDNSInterface_Any
, &q
->Target
, q
->TargetPort
, -1, mDNSNULL
);
3622 q
->ThisQInterval
*= 2;
3623 if (q
->ThisQInterval
> MaxQuestionInterval
)
3624 q
->ThisQInterval
= MaxQuestionInterval
;
3625 q
->LastQTime
= m
->timenow
;
3626 q
->LastQTxTime
= m
->timenow
;
3627 q
->RecentAnswerPkts
= 0;
3628 q
->SendQNow
= mDNSNULL
;
3629 m
->ExpectUnicastResponse
= m
->timenow
;
3632 // Scan our list of questions to see which *multicast* queries we're definitely going to send
3633 for (q
= m
->Questions
; q
; q
=q
->next
)
3634 if (!q
->Target
.type
&& TimeToSendThisQuestion(q
, m
->timenow
))
3636 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
3637 if (maxExistingQuestionInterval
< q
->ThisQInterval
)
3638 maxExistingQuestionInterval
= q
->ThisQInterval
;
3641 // Scan our list of questions
3642 // (a) to see if there are any more that are worth accelerating, and
3643 // (b) to update the state variables for *all* the questions we're going to send
3644 for (q
= m
->Questions
; q
; q
=q
->next
)
3647 (!q
->Target
.type
&& ActiveQuestion(q
) && q
->ThisQInterval
<= maxExistingQuestionInterval
&& AccelerateThisQuery(m
,q
)))
3649 // If at least halfway to next query time, advance to next interval
3650 // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval
3651 if (m
->timenow
- (q
->LastQTime
+ q
->ThisQInterval
/2) >= 0)
3653 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
3654 q
->ThisQInterval
*= 2;
3655 if (q
->ThisQInterval
> MaxQuestionInterval
)
3656 q
->ThisQInterval
= MaxQuestionInterval
;
3657 else if (q
->CurrentAnswers
== 0 && q
->ThisQInterval
== InitialQuestionInterval
* 8)
3659 debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q
->qname
.c
, DNSTypeName(q
->qtype
));
3660 ReconfirmAntecedents(m
, q
); // If sending third query, and no answers yet, time to begin doubting the source
3664 // Mark for sending. (If no active interfaces, then don't even try.)
3665 q
->SendOnAll
= (q
->SendQNow
== mDNSInterfaceMark
);
3668 q
->SendQNow
= !intf
? mDNSNULL
: (q
->InterfaceID
) ? q
->InterfaceID
: intf
->InterfaceID
;
3669 q
->LastQTime
= m
->timenow
;
3672 // If we recorded a duplicate suppression for this question less than half an interval ago,
3673 // then we consider it recent enough that we don't need to do an identical query ourselves.
3674 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
- q
->ThisQInterval
/2);
3676 q
->LastQTxTime
= m
->timenow
;
3677 q
->RecentAnswerPkts
= 0;
3678 if (q
->RequestUnicast
) q
->RequestUnicast
--;
3680 // For all questions (not just the ones we're sending) check what the next scheduled event will be
3681 SetNextQueryTime(m
,q
);
3685 // 2. Scan our authoritative RR list to see what probes we might need to send
3686 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3688 m
->NextScheduledProbe
= m
->timenow
+ 0x78000000;
3690 if (m
->CurrentRecord
) LogMsg("SendQueries: ERROR m->CurrentRecord already set");
3691 m
->CurrentRecord
= m
->ResourceRecords
;
3692 while (m
->CurrentRecord
)
3694 AuthRecord
*rr
= m
->CurrentRecord
;
3695 m
->CurrentRecord
= rr
->next
;
3696 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
) // For all records that are still probing...
3698 // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
3699 if (m
->timenow
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) < 0)
3701 SetNextAnnounceProbeTime(m
, rr
);
3703 // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
3704 else if (rr
->ProbeCount
)
3706 // Mark for sending. (If no active interfaces, then don't even try.)
3707 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
3708 rr
->LastAPTime
= m
->timenow
;
3710 SetNextAnnounceProbeTime(m
, rr
);
3712 // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
3716 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
3717 rr
->ThisAPInterval
= DefaultAnnounceIntervalForTypeUnique
;
3718 rr
->LastAPTime
= m
->timenow
- DefaultAnnounceIntervalForTypeUnique
;
3719 SetNextAnnounceProbeTime(m
, rr
);
3720 // If we have any records on our duplicate list that match this one, they have now also completed probing
3721 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
)
3722 if (r2
->resrec
.RecordType
== kDNSRecordTypeUnique
&& RecordIsLocalDuplicate(r2
, rr
))
3724 AcknowledgeRecord(m
, rr
);
3728 m
->CurrentRecord
= m
->DuplicateRecords
;
3729 while (m
->CurrentRecord
)
3731 AuthRecord
*rr
= m
->CurrentRecord
;
3732 m
->CurrentRecord
= rr
->next
;
3733 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& rr
->ProbeCount
== 0)
3734 AcknowledgeRecord(m
, rr
);
3738 // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
3742 mDNSu8
*queryptr
= m
->omsg
.data
;
3743 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, QueryFlags
);
3744 if (KnownAnswerList
) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet");
3745 if (!KnownAnswerList
)
3747 // Start a new known-answer list
3748 CacheRecord
**kalistptr
= &KnownAnswerList
;
3749 mDNSu32 answerforecast
= 0;
3751 // Put query questions in this packet
3752 for (q
= m
->Questions
; q
; q
=q
->next
)
3753 if (q
->SendQNow
== intf
->InterfaceID
)
3755 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
3756 SuppressOnThisInterface(q
->DupSuppress
, intf
) ? "Suppressing" : "Putting ",
3757 q
->qname
.c
, DNSTypeName(q
->qtype
), queryptr
- m
->omsg
.data
, queryptr
+ answerforecast
- m
->omsg
.data
);
3758 // If we're suppressing this question, or we successfully put it, update its SendQNow state
3759 if (SuppressOnThisInterface(q
->DupSuppress
, intf
) ||
3760 BuildQuestion(m
, &m
->omsg
, &queryptr
, q
, &kalistptr
, &answerforecast
))
3761 q
->SendQNow
= (q
->InterfaceID
|| !q
->SendOnAll
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
3764 // Put probe questions in this packet
3765 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3766 if (rr
->SendRNow
== intf
->InterfaceID
)
3768 mDNSBool ucast
= (rr
->ProbeCount
>= DefaultProbeCountForTypeUnique
-1) && m
->CanReceiveUnicastOn5353
;
3769 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
3770 const mDNSu8
*const limit
= m
->omsg
.data
+ ((m
->omsg
.h
.numQuestions
) ? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
3771 mDNSu8
*newptr
= putQuestion(&m
->omsg
, queryptr
, limit
, rr
->resrec
.name
, kDNSQType_ANY
, (mDNSu16
)(rr
->resrec
.rrclass
| ucbit
));
3772 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3773 mDNSu32 forecast
= answerforecast
+ 12 + rr
->resrec
.rdestimate
;
3774 if (newptr
&& newptr
+ forecast
< limit
)
3777 answerforecast
= forecast
;
3778 rr
->SendRNow
= (rr
->resrec
.InterfaceID
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
3779 rr
->IncludeInProbe
= mDNStrue
;
3780 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->ProbeCount
);
3784 verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3785 m
->omsg
.h
.numQuestions
--;
3790 // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
3791 while (KnownAnswerList
)
3793 CacheRecord
*rr
= KnownAnswerList
;
3794 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
3795 mDNSu8
*newptr
= PutResourceRecordTTL(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, rr
->resrec
.rroriginalttl
- SecsSinceRcvd
);
3798 verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), queryptr
- m
->omsg
.data
, newptr
- m
->omsg
.data
);
3800 KnownAnswerList
= rr
->NextInKAList
;
3801 rr
->NextInKAList
= mDNSNULL
;
3805 // If we ran out of space and we have more than one question in the packet, that's an error --
3806 // we shouldn't have put more than one question if there was a risk of us running out of space.
3807 if (m
->omsg
.h
.numQuestions
> 1)
3808 LogMsg("SendQueries: Put %d answers; No more space for known answers", m
->omsg
.h
.numAnswers
);
3809 m
->omsg
.h
.flags
.b
[0] |= kDNSFlag0_TC
;
3814 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3815 if (rr
->IncludeInProbe
)
3817 mDNSu8
*newptr
= PutResourceRecord(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAuthorities
, &rr
->resrec
);
3818 rr
->IncludeInProbe
= mDNSfalse
;
3819 if (newptr
) queryptr
= newptr
;
3820 else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?",
3821 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3824 if (queryptr
> m
->omsg
.data
)
3826 if ((m
->omsg
.h
.flags
.b
[0] & kDNSFlag0_TC
) && m
->omsg
.h
.numQuestions
> 1)
3827 LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m
->omsg
.h
.numQuestions
);
3828 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
3829 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
3830 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
3831 m
->omsg
.h
.numAuthorities
, m
->omsg
.h
.numAuthorities
== 1 ? "" : "s", intf
->InterfaceID
);
3832 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, -1, mDNSNULL
);
3833 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, -1, mDNSNULL
);
3834 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
3835 if (++pktcount
>= 1000)
3836 { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount
); break; }
3837 // There might be more records left in the known answer list, or more questions to send
3838 // on this interface, so go around one more time and try again.
3840 else // Nothing more to send on this interface; go to next
3842 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
3843 #if MDNS_DEBUGMSGS && 0
3844 const char *const msg
= next
? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p";
3845 debugf(msg
, intf
, next
);
3851 // Final sanity check for debugging purposes
3854 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3857 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
3858 LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m
, rr
));
3859 rr
->SendRNow
= mDNSNULL
;
3864 // ***************************************************************************
3865 #if COMPILER_LIKES_PRAGMA_MARK
3867 #pragma mark - RR List Management & Task Management
3870 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
3871 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3872 mDNSlocal
void AnswerQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, CacheRecord
*rr
, mDNSBool AddRecord
)
3874 verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
3875 q
->CurrentAnswers
, AddRecord
? "Add" : "Rmv", rr
->resrec
.rroriginalttl
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3877 // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue)
3878 // may be called twice, once when the record is received, and again when it's time to notify local clients.
3879 // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
3881 rr
->LastUsed
= m
->timenow
;
3882 if (ActiveQuestion(q
) && rr
->CRActiveQuestion
!= q
)
3884 if (!rr
->CRActiveQuestion
) m
->rrcache_active
++; // If not previously active, increment rrcache_active count
3885 rr
->CRActiveQuestion
= q
; // We know q is non-null
3886 SetNextCacheCheckTime(m
, rr
);
3890 // (a) a no-cache add, where we've already done at least one 'QM' query, or
3891 // (b) a normal add, where we have at least one unique-type answer,
3892 // then there's no need to keep polling the network.
3893 // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
3894 if ((AddRecord
== 2 && !q
->RequestUnicast
) ||
3895 (AddRecord
== 1 && (q
->ExpectUnique
|| (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))))
3896 if (ActiveQuestion(q
))
3898 q
->LastQTime
= m
->timenow
;
3899 q
->LastQTxTime
= m
->timenow
;
3900 q
->RecentAnswerPkts
= 0;
3901 q
->ThisQInterval
= MaxQuestionInterval
;
3902 q
->RequestUnicast
= mDNSfalse
;
3905 if (rr
->DelayDelivery
) return; // We'll come back later when CacheRecordDeferredAdd() calls us
3907 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
3908 if (q
->QuestionCallback
)
3909 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
3910 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
3911 // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function
3912 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
3913 // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv()
3914 // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions
3915 // being deleted out from under them.
3918 mDNSlocal
void CacheRecordDeferredAdd(mDNS
*const m
, CacheRecord
*rr
)
3920 rr
->DelayDelivery
= 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
3921 if (m
->CurrentQuestion
) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set");
3922 m
->CurrentQuestion
= m
->Questions
;
3923 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3925 DNSQuestion
*q
= m
->CurrentQuestion
;
3926 m
->CurrentQuestion
= q
->next
;
3927 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3928 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3930 m
->CurrentQuestion
= mDNSNULL
;
3933 mDNSlocal mDNSs32
CheckForSoonToExpireRecords(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const mDNSu32 slot
)
3935 const mDNSs32 threshhold
= m
->timenow
+ mDNSPlatformOneSecond
; // See if there are any records expiring within one second
3936 const mDNSs32 start
= m
->timenow
- 0x10000000;
3937 mDNSs32 delay
= start
;
3938 CacheGroup
*cg
= CacheGroupForName(m
, slot
, namehash
, name
);
3940 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3941 if (rr
->resrec
.namehash
== namehash
&& SameDomainName(rr
->resrec
.name
, name
))
3942 if (threshhold
- RRExpireTime(rr
) >= 0) // If we have records about to expire within a second
3943 if (delay
- RRExpireTime(rr
) < 0) // then delay until after they've been deleted
3944 delay
= RRExpireTime(rr
);
3945 if (delay
- start
> 0) return(NonZeroTime(delay
));
3949 // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
3950 // If new questions are created as a result of invoking client callbacks, they will be added to
3951 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3952 // rr is a new CacheRecord just received into our cache
3953 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
3954 // NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback,
3955 // which may change the record list and/or question list.
3956 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3957 mDNSlocal
void CacheRecordAdd(mDNS
*const m
, CacheRecord
*rr
)
3959 if (m
->CurrentQuestion
) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3960 m
->CurrentQuestion
= m
->Questions
;
3961 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3963 DNSQuestion
*q
= m
->CurrentQuestion
;
3964 m
->CurrentQuestion
= q
->next
;
3965 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3967 // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last
3968 // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start.
3969 // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less,
3970 // there's not much benefit accelerating because we will anyway send another query within a few seconds.
3971 // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines.
3972 if (q
->LastAnswerPktNum
!= m
->PktNum
)
3974 q
->LastAnswerPktNum
= m
->PktNum
;
3975 if (ActiveQuestion(q
) && ++q
->RecentAnswerPkts
>= 10 &&
3976 q
->ThisQInterval
> InitialQuestionInterval
*32 && m
->timenow
- q
->LastQTxTime
< mDNSPlatformOneSecond
)
3978 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
3979 q
->qname
.c
, DNSTypeName(q
->qtype
));
3980 q
->LastQTime
= m
->timenow
- InitialQuestionInterval
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*4);
3981 q
->ThisQInterval
= InitialQuestionInterval
;
3982 SetNextQueryTime(m
,q
);
3985 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->resrec
.rroriginalttl
);
3986 q
->CurrentAnswers
++;
3987 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
3988 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
3989 if (q
->CurrentAnswers
> 4000)
3991 static int msgcount
= 0;
3992 if (msgcount
++ < 10)
3993 LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
3994 q
->qname
.c
, DNSTypeName(q
->qtype
), q
->CurrentAnswers
);
3995 rr
->resrec
.rroriginalttl
= 1;
3996 rr
->UnansweredQueries
= MaxUnansweredQueries
;
3998 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3999 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4002 m
->CurrentQuestion
= mDNSNULL
;
4003 SetNextCacheCheckTime(m
, rr
);
4006 // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
4007 // If new questions are created as a result of invoking client callbacks, they will be added to
4008 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
4009 // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
4010 // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
4011 // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
4012 // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
4013 // NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback,
4014 // which may change the record list and/or question list.
4015 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4016 mDNSlocal
void NoCacheAnswer(mDNS
*const m
, CacheRecord
*rr
)
4018 LogMsg("No cache space: Delivering non-cached result for %##s", m
->rec
.r
.resrec
.name
->c
);
4019 if (m
->CurrentQuestion
) LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set");
4020 m
->CurrentQuestion
= m
->Questions
;
4021 while (m
->CurrentQuestion
)
4023 DNSQuestion
*q
= m
->CurrentQuestion
;
4024 m
->CurrentQuestion
= q
->next
;
4025 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4026 AnswerQuestionWithResourceRecord(m
, q
, rr
, 2); // Value '2' indicates "don't expect 'remove' events for this"
4027 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4029 m
->CurrentQuestion
= mDNSNULL
;
4032 // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
4033 // If new questions are created as a result of invoking client callbacks, they will be added to
4034 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
4035 // rr is an existing cache CacheRecord that just expired and is being deleted
4036 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
4037 // NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback,
4038 // which may change the record list and/or question list.
4039 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4040 mDNSlocal
void CacheRecordRmv(mDNS
*const m
, CacheRecord
*rr
)
4042 if (m
->CurrentQuestion
) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set");
4043 m
->CurrentQuestion
= m
->Questions
;
4044 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
4046 DNSQuestion
*q
= m
->CurrentQuestion
;
4047 m
->CurrentQuestion
= q
->next
;
4048 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4050 verbosedebugf("CacheRecordRmv %p %s", rr
, CRDisplayString(m
, rr
));
4051 if (q
->CurrentAnswers
== 0)
4052 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q
, q
->qname
.c
, DNSTypeName(q
->qtype
));
4055 q
->CurrentAnswers
--;
4056 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
--;
4057 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
--;
4059 if (q
->CurrentAnswers
== 0)
4061 debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q
->qname
.c
, DNSTypeName(q
->qtype
));
4062 ReconfirmAntecedents(m
, q
);
4064 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNSfalse
);
4065 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4068 m
->CurrentQuestion
= mDNSNULL
;
4071 mDNSlocal
void ReleaseCacheEntity(mDNS
*const m
, CacheEntity
*e
)
4073 #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
4075 for (i
=0; i
<sizeof(*e
); i
++) ((char*)e
)[i
] = 0xFF;
4077 e
->next
= m
->rrcache_free
;
4078 m
->rrcache_free
= e
;
4079 m
->rrcache_totalused
--;
4082 mDNSlocal
void ReleaseCacheGroup(mDNS
*const m
, CacheGroup
**cp
)
4084 CacheEntity
*e
= (CacheEntity
*)(*cp
);
4085 //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
4086 if ((*cp
)->rrcache_tail
!= &(*cp
)->members
) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
4087 //if ((*cp)->name != (domainname*)((*cp)->namestorage)) LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
4088 if ((*cp
)->name
!= (domainname
*)((*cp
)->namestorage
)) mDNSPlatformMemFree((*cp
)->name
);
4089 (*cp
)->name
= mDNSNULL
;
4090 *cp
= (*cp
)->next
; // Cut record from list
4091 ReleaseCacheEntity(m
, e
);
4094 mDNSlocal
void ReleaseCacheRecord(mDNS
*const m
, CacheRecord
*r
)
4096 if (r
->resrec
.rdata
&& r
->resrec
.rdata
!= (RData
*)&r
->rdatastorage
) mDNSPlatformMemFree(r
->resrec
.rdata
);
4097 r
->resrec
.rdata
= mDNSNULL
;
4098 ReleaseCacheEntity(m
, (CacheEntity
*)r
);
4101 // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls
4102 // The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records
4103 mDNSlocal
void CheckCacheExpiration(mDNS
*const m
, CacheGroup
*cg
)
4105 CacheRecord
**rp
= &cg
->members
;
4107 if (m
->lock_rrcache
) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
4108 m
->lock_rrcache
= 1;
4112 CacheRecord
*const rr
= *rp
;
4113 mDNSs32 event
= RRExpireTime(rr
);
4114 if (m
->timenow
- event
>= 0) // If expired, delete it
4116 *rp
= rr
->next
; // Cut it from the list
4117 verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m
, rr
));
4118 if (rr
->CRActiveQuestion
) // If this record has one or more active questions, tell them it's going away
4120 CacheRecordRmv(m
, rr
);
4121 m
->rrcache_active
--;
4123 ReleaseCacheRecord(m
, rr
);
4125 else // else, not expired; see if we need to query
4127 if (rr
->DelayDelivery
&& rr
->DelayDelivery
- m
->timenow
> 0)
4128 event
= rr
->DelayDelivery
;
4131 if (rr
->DelayDelivery
) CacheRecordDeferredAdd(m
, rr
);
4132 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
4134 if (m
->timenow
- rr
->NextRequiredQuery
< 0) // If not yet time for next query
4135 event
= rr
->NextRequiredQuery
; // then just record when we want the next query
4136 else // else trigger our question to go out now
4138 // Set NextScheduledQuery to timenow so that SendQueries() will run.
4139 // SendQueries() will see that we have records close to expiration, and send FEQs for them.
4140 m
->NextScheduledQuery
= m
->timenow
;
4141 // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(),
4142 // which will correctly update m->NextCacheCheck for us
4143 event
= m
->timenow
+ 0x3FFFFFFF;
4147 if (m
->NextCacheCheck
- (event
+ CacheCheckGracePeriod(rr
)) > 0)
4148 m
->NextCacheCheck
= (event
+ CacheCheckGracePeriod(rr
));
4152 if (cg
->rrcache_tail
!= rp
) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg
->rrcache_tail
, rp
);
4153 cg
->rrcache_tail
= rp
;
4154 m
->lock_rrcache
= 0;
4157 mDNSlocal
void AnswerNewQuestion(mDNS
*const m
)
4159 mDNSBool ShouldQueryImmediately
= mDNStrue
;
4161 DNSQuestion
*q
= m
->NewQuestions
; // Grab the question we're going to answer
4162 const mDNSu32 slot
= HashSlot(&q
->qname
);
4163 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
4165 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4167 if (cg
) CheckCacheExpiration(m
, cg
);
4168 m
->NewQuestions
= q
->next
; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
4170 if (m
->lock_rrcache
) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
4171 // This should be safe, because calling the client's question callback may cause the
4172 // question list to be modified, but should not ever cause the rrcache list to be modified.
4173 // If the client's question callback deletes the question, then m->CurrentQuestion will
4174 // be advanced, and we'll exit out of the loop
4175 m
->lock_rrcache
= 1;
4176 if (m
->CurrentQuestion
) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set");
4177 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
4179 if (q
->InterfaceID
== mDNSInterface_Any
) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
4181 if (m
->CurrentRecord
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4182 m
->CurrentRecord
= m
->ResourceRecords
;
4183 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
4185 AuthRecord
*rr
= m
->CurrentRecord
;
4186 m
->CurrentRecord
= rr
->next
;
4187 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
4188 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4190 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4191 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4192 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4195 m
->CurrentRecord
= mDNSNULL
;
4198 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4199 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4201 // SecsSinceRcvd is whole number of elapsed seconds, rounded down
4202 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
4203 if (rr
->resrec
.rroriginalttl
<= SecsSinceRcvd
)
4205 LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)",
4206 rr
->resrec
.rroriginalttl
, SecsSinceRcvd
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4207 continue; // Go to next one in loop
4210 // If this record set is marked unique, then that means we can reasonably assume we have the whole set
4211 // -- we don't need to rush out on the network and query immediately to see if there are more answers out there
4212 if ((rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) || (q
->ExpectUnique
))
4213 ShouldQueryImmediately
= mDNSfalse
;
4214 q
->CurrentAnswers
++;
4215 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
4216 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
4217 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4218 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4219 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4221 else if (RRTypeIsAddressType(rr
->resrec
.rrtype
) && RRTypeIsAddressType(q
->qtype
))
4222 if (rr
->resrec
.namehash
== q
->qnamehash
&& SameDomainName(rr
->resrec
.name
, &q
->qname
))
4223 ShouldQueryImmediately
= mDNSfalse
;
4225 if (ShouldQueryImmediately
&& m
->CurrentQuestion
== q
)
4227 q
->ThisQInterval
= InitialQuestionInterval
;
4228 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
4229 m
->NextScheduledQuery
= m
->timenow
;
4231 m
->CurrentQuestion
= mDNSNULL
;
4232 m
->lock_rrcache
= 0;
4235 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any appropriate answers,
4236 // stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions
4237 mDNSlocal
void AnswerNewLocalOnlyQuestion(mDNS
*const m
)
4239 DNSQuestion
*q
= m
->NewLocalOnlyQuestions
; // Grab the question we're going to answer
4240 m
->NewLocalOnlyQuestions
= q
->next
; // Advance NewQuestions to the next (if any)
4242 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4244 if (m
->CurrentQuestion
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set");
4245 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
4247 if (m
->CurrentRecord
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4248 m
->CurrentRecord
= m
->ResourceRecords
;
4249 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
4251 AuthRecord
*rr
= m
->CurrentRecord
;
4252 m
->CurrentRecord
= rr
->next
;
4253 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4255 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4256 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4257 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4261 m
->CurrentQuestion
= mDNSNULL
;
4262 m
->CurrentRecord
= mDNSNULL
;
4265 mDNSlocal CacheEntity
*GetCacheEntity(mDNS
*const m
, const CacheGroup
*const PreserveCG
)
4267 CacheEntity
*e
= mDNSNULL
;
4269 if (m
->lock_rrcache
) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL
); }
4270 m
->lock_rrcache
= 1;
4272 // If we have no free records, ask the client layer to give us some more memory
4273 if (!m
->rrcache_free
&& m
->MainCallback
)
4275 if (m
->rrcache_totalused
!= m
->rrcache_size
)
4276 LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
4277 m
->rrcache_totalused
, m
->rrcache_size
);
4279 // We don't want to be vulnerable to a malicious attacker flooding us with an infinite
4280 // number of bogus records so that we keep growing our cache until the machine runs out of memory.
4281 // To guard against this, if we're actively using less than 1/32 of our cache, then we
4282 // purge all the unused records and recycle them, instead of allocating more memory.
4283 if (m
->rrcache_size
>= 512 && m
->rrcache_size
/ 32 > m
->rrcache_active
)
4284 debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
4285 m
->rrcache_size
, m
->rrcache_active
);
4288 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
4289 m
->MainCallback(m
, mStatus_GrowCache
);
4290 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
4294 // If we still have no free records, recycle all the records we can.
4295 // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
4296 if (!m
->rrcache_free
)
4299 mDNSu32 oldtotalused
= m
->rrcache_totalused
;
4302 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
4304 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
4307 CacheRecord
**rp
= &(*cp
)->members
;
4310 // Records that answer still-active questions are not candidates for recycling
4311 // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
4312 if ((*rp
)->CRActiveQuestion
|| (*rp
)->NextInCFList
)
4316 CacheRecord
*rr
= *rp
;
4317 *rp
= (*rp
)->next
; // Cut record from list
4318 ReleaseCacheRecord(m
, rr
);
4321 if ((*cp
)->rrcache_tail
!= rp
) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot
, (*cp
)->rrcache_tail
, rp
);
4322 (*cp
)->rrcache_tail
= rp
;
4323 if ((*cp
)->members
|| (*cp
)==PreserveCG
) cp
=&(*cp
)->next
;
4324 else ReleaseCacheGroup(m
, cp
);
4328 debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused
, m
->rrcache_totalused
);
4332 if (m
->rrcache_free
) // If there are records in the free list, take one
4334 e
= m
->rrcache_free
;
4335 m
->rrcache_free
= e
->next
;
4336 if (++m
->rrcache_totalused
>= m
->rrcache_report
)
4338 debugf("RR Cache now using %ld objects", m
->rrcache_totalused
);
4339 if (m
->rrcache_report
< 100) m
->rrcache_report
+= 10;
4340 else m
->rrcache_report
+= 100;
4342 mDNSPlatformMemZero(e
, sizeof(*e
));
4345 m
->lock_rrcache
= 0;
4350 mDNSlocal CacheRecord
*GetCacheRecord(mDNS
*const m
, CacheGroup
*cg
, mDNSu16 RDLength
)
4352 CacheRecord
*r
= (CacheRecord
*)GetCacheEntity(m
, cg
);
4355 r
->resrec
.rdata
= (RData
*)&r
->rdatastorage
; // By default, assume we're usually going to be using local storage
4356 if (RDLength
> InlineCacheRDSize
) // If RDLength is too big, allocate extra storage
4358 r
->resrec
.rdata
= (RData
*)mDNSPlatformMemAllocate(sizeofRDataHeader
+ RDLength
);
4359 if (r
->resrec
.rdata
) r
->resrec
.rdata
->MaxRDLength
= r
->resrec
.rdlength
= RDLength
;
4360 else { ReleaseCacheEntity(m
, (CacheEntity
*)r
); r
= mDNSNULL
; }
4366 mDNSlocal CacheGroup
*GetCacheGroup(mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
4368 mDNSu16 namelen
= DomainNameLength(rr
->name
);
4369 CacheGroup
*cg
= (CacheGroup
*)GetCacheEntity(m
, mDNSNULL
);
4370 if (!cg
) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr
->name
->c
); return(mDNSNULL
); }
4371 cg
->next
= m
->rrcache_hash
[slot
];
4372 cg
->namehash
= rr
->namehash
;
4373 cg
->members
= mDNSNULL
;
4374 cg
->rrcache_tail
= &cg
->members
;
4375 cg
->name
= (domainname
*)cg
->namestorage
;
4376 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
4377 if (namelen
> InlineCacheGroupNameSize
) cg
->name
= mDNSPlatformMemAllocate(namelen
);
4380 LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr
->name
->c
);
4381 ReleaseCacheEntity(m
, (CacheEntity
*)cg
);
4384 AssignDomainName(cg
->name
, rr
->name
);
4386 if (CacheGroupForRecord(m
, slot
, rr
)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr
->name
->c
);
4387 m
->rrcache_hash
[slot
] = cg
;
4388 if (CacheGroupForRecord(m
, slot
, rr
) != cg
) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr
->name
->c
);
4393 mDNSlocal
void PurgeCacheResourceRecord(mDNS
*const m
, CacheRecord
*rr
)
4395 // Make sure we mark this record as thoroughly expired -- we don't ever want to give
4396 // a positive answer using an expired record (e.g. from an interface that has gone away).
4397 // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
4398 // summary deletion without giving the proper callback to any questions that are monitoring it.
4399 // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
4400 rr
->TimeRcvd
= m
->timenow
- mDNSPlatformOneSecond
* 60;
4401 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4402 rr
->resrec
.rroriginalttl
= 0;
4403 SetNextCacheCheckTime(m
, rr
);
4406 mDNSexport mDNSs32
mDNS_TimeNow(const mDNS
*const m
)
4409 mDNSPlatformLock(m
);
4412 LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
4413 if (!m
->timenow
) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
4416 if (m
->timenow
) time
= m
->timenow
;
4417 else time
= mDNS_TimeNow_NoLock(m
);
4418 mDNSPlatformUnlock(m
);
4422 mDNSexport mDNSs32
mDNS_Execute(mDNS
*const m
)
4424 mDNS_Lock(m
); // Must grab lock before trying to read m->timenow
4426 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
4430 verbosedebugf("mDNS_Execute");
4431 if (m
->CurrentQuestion
) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
4433 // 1. If we're past the probe suppression time, we can clear it
4434 if (m
->SuppressProbes
&& m
->timenow
- m
->SuppressProbes
>= 0) m
->SuppressProbes
= 0;
4436 // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
4437 if (m
->NumFailedProbes
&& m
->timenow
- m
->ProbeFailTime
>= mDNSPlatformOneSecond
* 10) m
->NumFailedProbes
= 0;
4439 // 3. Purge our cache of stale old records
4440 if (m
->rrcache_size
&& m
->timenow
- m
->NextCacheCheck
>= 0)
4443 m
->NextCacheCheck
= m
->timenow
+ 0x3FFFFFFF;
4444 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
4446 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
4449 CheckCacheExpiration(m
, *cp
);
4450 if ((*cp
)->members
) cp
=&(*cp
)->next
;
4451 else ReleaseCacheGroup(m
, cp
);
4456 // 4. See if we can answer any of our new local questions from the cache
4457 for (i
=0; m
->NewQuestions
&& i
<1000; i
++)
4459 if (m
->NewQuestions
->DelayAnswering
&& m
->timenow
- m
->NewQuestions
->DelayAnswering
< 0) break;
4460 AnswerNewQuestion(m
);
4462 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
4464 for (i
=0; m
->NewLocalOnlyQuestions
&& i
<1000; i
++) AnswerNewLocalOnlyQuestion(m
);
4465 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4467 for (i
=0; i
<1000 && m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
); i
++)
4469 AuthRecord
*rr
= m
->NewLocalRecords
;
4470 m
->NewLocalRecords
= m
->NewLocalRecords
->next
;
4471 AnswerLocalQuestions(m
, rr
, mDNStrue
);
4473 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
4475 // 5. See what packets we need to send
4476 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) DiscardDeregistrations(m
);
4477 else if (m
->SuppressSending
== 0 || m
->timenow
- m
->SuppressSending
>= 0)
4479 // If the platform code is ready, and we're not suppressing packet generation right now
4480 // then send our responses, probes, and questions.
4481 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them
4482 // We send queries next, because there might be final-stage probes that complete their probing here, causing
4483 // them to advance to announcing state, and we want those to be included in any announcements we send out.
4484 // Finally, we send responses, including the previously mentioned records that just completed probing
4485 m
->SuppressSending
= 0;
4487 // 6. Send Query packets. This may cause some probing records to advance to announcing state
4488 if (m
->timenow
- m
->NextScheduledQuery
>= 0 || m
->timenow
- m
->NextScheduledProbe
>= 0) SendQueries(m
);
4489 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
4491 LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
4492 m
->NextScheduledQuery
= m
->timenow
+ mDNSPlatformOneSecond
;
4494 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
4496 LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
4497 m
->NextScheduledProbe
= m
->timenow
+ mDNSPlatformOneSecond
;
4500 // 7. Send Response packets, including probing records just advanced to announcing state
4501 if (m
->timenow
- m
->NextScheduledResponse
>= 0) SendResponses(m
);
4502 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
4504 LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
4505 m
->NextScheduledResponse
= m
->timenow
+ mDNSPlatformOneSecond
;
4509 m
->RandomQueryDelay
= 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
4512 // Note about multi-threaded systems:
4513 // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
4514 // performing mDNS API operations that change our next scheduled event time.
4516 // On multi-threaded systems (like the current Windows implementation) that have a single main thread
4517 // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
4518 // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
4519 // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
4520 // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
4521 // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
4522 // does, the state of the signal will be noticed, causing the blocking primitive to return immediately
4523 // without blocking. This avoids the race condition between the signal from the other thread arriving
4524 // just *before* or just *after* the main thread enters the blocking primitive.
4526 // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
4527 // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
4528 // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
4529 // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
4530 // by the time it gets to the timer callback function).
4532 #ifndef UNICAST_DISABLED
4535 mDNS_Unlock(m
); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4536 return(m
->NextScheduledEvent
);
4539 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
4540 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
4541 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
4542 // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
4543 // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
4544 // found itself in a new network environment. For example, if the Ethernet hardware indicates that the
4545 // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
4546 // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
4547 // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
4548 // traffic, so it should only be called when there is legitimate reason to believe the machine
4549 // may have become attached to a new network.
4550 mDNSexport
void mDNSCoreMachineSleep(mDNS
*const m
, mDNSBool sleepstate
)
4556 m
->SleepState
= sleepstate
;
4557 LogOperation("%s at %ld", sleepstate
? "Sleeping" : "Waking", m
->timenow
);
4561 #ifndef UNICAST_DISABLED
4564 // Mark all the records we need to deregister and send them
4565 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4566 if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
4567 rr
->ImmedAnswer
= mDNSInterfaceMark
;
4577 #ifndef UNICAST_DISABLED
4580 // 1. Retrigger all our questions
4581 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
4582 if (ActiveQuestion(q
))
4584 q
->ThisQInterval
= InitialQuestionInterval
; // MUST be > zero for an active question
4585 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
4586 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
4587 q
->RecentAnswerPkts
= 0;
4588 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
);
4589 m
->NextScheduledQuery
= m
->timenow
;
4592 // 2. Re-validate our cache records
4593 m
->NextCacheCheck
= m
->timenow
;
4594 FORALL_CACHERECORDS(slot
, cg
, cr
)
4595 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForCableDisconnect
);
4597 // 3. Retrigger probing and announcing for all our authoritative records
4598 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4600 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
4601 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
4602 rr
->AnnounceCount
= InitialAnnounceCount
;
4603 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
4604 InitializeLastAPTime(m
, rr
);
4611 // ***************************************************************************
4612 #if COMPILER_LIKES_PRAGMA_MARK
4614 #pragma mark - Packet Reception Functions
4617 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4619 mDNSlocal mDNSu8
*GenerateUnicastResponse(const DNSMessage
*const query
, const mDNSu8
*const end
,
4620 const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, DNSMessage
*const response
, AuthRecord
*ResponseRecords
)
4622 mDNSu8
*responseptr
= response
->data
;
4623 const mDNSu8
*const limit
= response
->data
+ sizeof(response
->data
);
4624 const mDNSu8
*ptr
= query
->data
;
4626 mDNSu32 maxttl
= 0x70000000;
4629 // Initialize the response fields so we can answer the questions
4630 InitializeDNSMessage(&response
->h
, query
->h
.id
, ResponseFlags
);
4633 // *** 1. Write out the list of questions we are actually going to answer with this packet
4638 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
4641 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &q
); // get the question...
4642 if (!ptr
) return(mDNSNULL
);
4644 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // and search our list of proposed answers
4646 if (rr
->NR_AnswerTo
== ptr
) // If we're going to generate a record answering this question
4647 { // then put the question in the question section
4648 responseptr
= putQuestion(response
, responseptr
, limit
, &q
.qname
, q
.qtype
, q
.qclass
);
4649 if (!responseptr
) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL
); }
4650 break; // break out of the ResponseRecords loop, and go on to the next question
4655 if (response
->h
.numQuestions
== 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL
); }
4659 // *** 2. Write Answers
4661 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4662 if (rr
->NR_AnswerTo
)
4664 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAnswers
, &rr
->resrec
, maxttl
);
4665 if (p
) responseptr
= p
;
4666 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response
->h
.flags
.b
[0] |= kDNSFlag0_TC
; }
4670 // *** 3. Write Additionals
4672 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4673 if (rr
->NR_AdditionalTo
&& !rr
->NR_AnswerTo
)
4675 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAdditionals
, &rr
->resrec
, maxttl
);
4676 if (p
) responseptr
= p
;
4677 else debugf("GenerateUnicastResponse: No more space for additionals");
4680 return(responseptr
);
4683 // AuthRecord *our is our Resource Record
4684 // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
4685 // Returns 0 if there is no conflict
4686 // Returns +1 if there was a conflict and we won
4687 // Returns -1 if there was a conflict and we lost and have to rename
4688 mDNSlocal
int CompareRData(AuthRecord
*our
, CacheRecord
*pkt
)
4690 mDNSu8 ourdata
[256], *ourptr
= ourdata
, *ourend
;
4691 mDNSu8 pktdata
[256], *pktptr
= pktdata
, *pktend
;
4692 if (!our
) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
4693 if (!pkt
) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
4695 ourend
= putRData(mDNSNULL
, ourdata
, ourdata
+ sizeof(ourdata
), &our
->resrec
);
4696 pktend
= putRData(mDNSNULL
, pktdata
, pktdata
+ sizeof(pktdata
), &pkt
->resrec
);
4697 while (ourptr
< ourend
&& pktptr
< pktend
&& *ourptr
== *pktptr
) { ourptr
++; pktptr
++; }
4698 if (ourptr
>= ourend
&& pktptr
>= pktend
) return(0); // If data identical, not a conflict
4700 if (ourptr
>= ourend
) return(-1); // Our data ran out first; We lost
4701 if (pktptr
>= pktend
) return(+1); // Packet data ran out first; We won
4702 if (*pktptr
> *ourptr
) return(-1); // Our data is numerically lower; We lost
4703 if (*pktptr
< *ourptr
) return(+1); // Packet data is numerically lower; We won
4705 LogMsg("CompareRData ERROR: Invalid state");
4709 // See if we have an authoritative record that's identical to this packet record,
4710 // whose canonical DependentOn record is the specified master record.
4711 // The DependentOn pointer is typically used for the TXT record of service registrations
4712 // It indicates that there is no inherent conflict detection for the TXT record
4713 // -- it depends on the SRV record to resolve name conflicts
4714 // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
4715 // pointer chain (if any) to make sure we reach the canonical DependentOn record
4716 // If the record has no DependentOn, then just return that record's pointer
4717 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4718 mDNSlocal mDNSBool
MatchDependentOn(const mDNS
*const m
, const CacheRecord
*const pktrr
, const AuthRecord
*const master
)
4720 const AuthRecord
*r1
;
4721 for (r1
= m
->ResourceRecords
; r1
; r1
=r1
->next
)
4723 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
4725 const AuthRecord
*r2
= r1
;
4726 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
4727 if (r2
== master
) return(mDNStrue
);
4730 for (r1
= m
->DuplicateRecords
; r1
; r1
=r1
->next
)
4732 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
4734 const AuthRecord
*r2
= r1
;
4735 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
4736 if (r2
== master
) return(mDNStrue
);
4742 // Find the canonical RRSet pointer for this RR received in a packet.
4743 // If we find any identical AuthRecord in our authoritative list, then follow its RRSet
4744 // pointers (if any) to make sure we return the canonical member of this name/type/class
4745 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4746 mDNSlocal
const AuthRecord
*FindRRSet(const mDNS
*const m
, const CacheRecord
*const pktrr
)
4748 const AuthRecord
*rr
;
4749 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4751 if (IdenticalResourceRecord(&rr
->resrec
, &pktrr
->resrec
))
4753 while (rr
->RRSet
&& rr
!= rr
->RRSet
) rr
= rr
->RRSet
;
4760 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
4761 // as one of our records (our) but different rdata.
4762 // 1. If our record is not a type that's supposed to be unique, we don't care.
4763 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
4764 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
4765 // points to our record, ignore this conflict (e.g. the packet record matches one of our
4766 // TXT records, and that record is marked as dependent on 'our', its SRV record).
4767 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
4768 // are members of the same RRSet, then this is not a conflict.
4769 mDNSlocal mDNSBool
PacketRRConflict(const mDNS
*const m
, const AuthRecord
*const our
, const CacheRecord
*const pktrr
)
4771 const AuthRecord
*ourset
= our
->RRSet
? our
->RRSet
: our
;
4773 // If not supposed to be unique, not a conflict
4774 if (!(our
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)) return(mDNSfalse
);
4776 // If a dependent record, not a conflict
4777 if (our
->DependentOn
|| MatchDependentOn(m
, pktrr
, our
)) return(mDNSfalse
);
4779 // If the pktrr matches a member of ourset, not a conflict
4780 if (FindRRSet(m
, pktrr
) == ourset
) return(mDNSfalse
);
4782 // Okay, this is a conflict
4786 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
4787 // the record list and/or question list.
4788 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4789 mDNSlocal
void ResolveSimultaneousProbe(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
4790 DNSQuestion
*q
, AuthRecord
*our
)
4793 const mDNSu8
*ptr
= LocateAuthorities(query
, end
);
4794 mDNSBool FoundUpdate
= mDNSfalse
;
4796 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
4798 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, kDNSRecordTypePacketAuth
, &m
->rec
);
4800 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
4802 FoundUpdate
= mDNStrue
;
4803 if (PacketRRConflict(m
, our
, &m
->rec
.r
))
4805 int result
= (int)our
->resrec
.rrclass
- (int)m
->rec
.r
.resrec
.rrclass
;
4806 if (!result
) result
= (int)our
->resrec
.rrtype
- (int)m
->rec
.r
.resrec
.rrtype
;
4807 if (!result
) result
= CompareRData(our
, &m
->rec
.r
);
4809 debugf("ResolveSimultaneousProbe: %##s (%s): We won", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4810 else if (result
< 0)
4812 debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4813 mDNS_Deregister_internal(m
, our
, mDNS_Dereg_conflict
);
4818 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4821 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4823 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4826 mDNSlocal CacheRecord
*FindIdenticalRecordInCache(const mDNS
*const m
, ResourceRecord
*pktrr
)
4828 mDNSu32 slot
= HashSlot(pktrr
->name
);
4829 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, pktrr
);
4831 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4832 if (pktrr
->InterfaceID
== rr
->resrec
.InterfaceID
&& IdenticalResourceRecord(pktrr
, &rr
->resrec
)) break;
4836 // ProcessQuery examines a received query to see if we have any answers to give
4837 mDNSlocal mDNSu8
*ProcessQuery(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
4838 const mDNSAddr
*srcaddr
, const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, mDNSBool QueryWasMulticast
, mDNSBool QueryWasLocalUnicast
,
4839 DNSMessage
*const response
)
4841 mDNSBool FromLocalSubnet
= AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
4842 AuthRecord
*ResponseRecords
= mDNSNULL
;
4843 AuthRecord
**nrp
= &ResponseRecords
;
4844 CacheRecord
*ExpectedAnswers
= mDNSNULL
; // Records in our cache we expect to see updated
4845 CacheRecord
**eap
= &ExpectedAnswers
;
4846 DNSQuestion
*DupQuestions
= mDNSNULL
; // Our questions that are identical to questions in this packet
4847 DNSQuestion
**dqp
= &DupQuestions
;
4848 mDNSs32 delayresponse
= 0;
4849 mDNSBool SendLegacyResponse
= mDNSfalse
;
4850 const mDNSu8
*ptr
= query
->data
;
4851 mDNSu8
*responseptr
= mDNSNULL
;
4856 // *** 1. Parse Question Section and mark potential answers
4858 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
4860 mDNSBool QuestionNeedsMulticastResponse
;
4861 int NumAnswersForThisQuestion
= 0;
4862 DNSQuestion pktq
, *q
;
4863 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &pktq
); // get the question...
4864 if (!ptr
) goto exit
;
4866 // The only queries that *need* a multicast response are:
4867 // * Queries sent via multicast
4869 // * that don't have the kDNSQClass_UnicastResponse bit set
4870 // These queries need multicast responses because other clients will:
4871 // * suppress their own identical questions when they see these questions, and
4872 // * expire their cache records if they don't see the expected responses
4873 // For other queries, we may still choose to send the occasional multicast response anyway,
4874 // to keep our neighbours caches warm, and for ongoing conflict detection.
4875 QuestionNeedsMulticastResponse
= QueryWasMulticast
&& !LegacyQuery
&& !(pktq
.qclass
& kDNSQClass_UnicastResponse
);
4876 // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
4877 pktq
.qclass
&= ~kDNSQClass_UnicastResponse
;
4879 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
4880 // can result in user callbacks which may change the record list and/or question list.
4881 // Also note: we just mark potential answer records here, without trying to build the
4882 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
4883 // from that list while we're in the middle of trying to build it.
4884 if (m
->CurrentRecord
) LogMsg("ProcessQuery ERROR m->CurrentRecord already set");
4885 m
->CurrentRecord
= m
->ResourceRecords
;
4886 while (m
->CurrentRecord
)
4888 rr
= m
->CurrentRecord
;
4889 m
->CurrentRecord
= rr
->next
;
4890 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && (QueryWasMulticast
|| QueryWasLocalUnicast
|| rr
->AllowRemoteQuery
))
4892 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
4893 ResolveSimultaneousProbe(m
, query
, end
, &pktq
, rr
);
4894 else if (ResourceRecordIsValidAnswer(rr
))
4896 NumAnswersForThisQuestion
++;
4898 // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
4899 // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
4900 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
4901 // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
4902 // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link)
4903 // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
4904 if (QuestionNeedsMulticastResponse
|| (!FromLocalSubnet
&& QueryWasMulticast
&& !LegacyQuery
))
4906 // We only mark this question for sending if it is at least one second since the last time we multicast it
4907 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
4908 // This is to guard against the case where someone blasts us with queries as fast as they can.
4909 if (m
->timenow
- (rr
->LastMCTime
+ mDNSPlatformOneSecond
) >= 0 ||
4910 (rr
->LastMCInterface
!= mDNSInterfaceMark
&& rr
->LastMCInterface
!= InterfaceID
))
4911 rr
->NR_AnswerTo
= (mDNSu8
*)~0;
4913 else if (!rr
->NR_AnswerTo
) rr
->NR_AnswerTo
= LegacyQuery
? ptr
: (mDNSu8
*)~1;
4918 // If we couldn't answer this question, someone else might be able to,
4919 // so use random delay on response to reduce collisions
4920 if (NumAnswersForThisQuestion
== 0) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
4922 // We only do the following accelerated cache expiration processing and duplicate question suppression processing
4923 // for multicast queries with multicast responses.
4924 // For any query generating a unicast response we don't do this because we can't assume we will see the response
4925 if (QuestionNeedsMulticastResponse
)
4927 const mDNSu32 slot
= HashSlot(&pktq
.qname
);
4928 CacheGroup
*cg
= CacheGroupForName(m
, slot
, pktq
.qnamehash
, &pktq
.qname
);
4931 // Make a list indicating which of our own cache records we expect to see updated as a result of this query
4932 // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
4933 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4934 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && rr
->resrec
.rdlength
<= SmallRecordLimit
)
4935 if (!rr
->NextInKAList
&& eap
!= &rr
->NextInKAList
)
4938 eap
= &rr
->NextInKAList
;
4939 if (rr
->MPUnansweredQ
== 0 || m
->timenow
- rr
->MPLastUnansweredQT
>= mDNSPlatformOneSecond
)
4941 // Although MPUnansweredQ is only really used for multi-packet query processing,
4942 // we increment it for both single-packet and multi-packet queries, so that it stays in sync
4943 // with the MPUnansweredKA value, which by necessity is incremented for both query types.
4944 rr
->MPUnansweredQ
++;
4945 rr
->MPLastUnansweredQT
= m
->timenow
;
4946 rr
->MPExpectingKA
= mDNStrue
;
4950 // Check if this question is the same as any of mine.
4951 // We only do this for non-truncated queries. Right now it would be too complicated to try
4952 // to keep track of duplicate suppression state between multiple packets, especially when we
4953 // can't guarantee to receive all of the Known Answer packets that go with a particular query.
4954 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
4955 for (q
= m
->Questions
; q
; q
=q
->next
)
4956 if (!q
->Target
.type
&& ActiveQuestion(q
) && m
->timenow
- q
->LastQTxTime
> mDNSPlatformOneSecond
/ 4)
4957 if (!q
->InterfaceID
|| q
->InterfaceID
== InterfaceID
)
4958 if (q
->NextInDQList
== mDNSNULL
&& dqp
!= &q
->NextInDQList
)
4959 if (q
->qtype
== pktq
.qtype
&& q
->qclass
== pktq
.qclass
&& q
->qnamehash
== pktq
.qnamehash
&& SameDomainName(&q
->qname
, &pktq
.qname
))
4960 { *dqp
= q
; dqp
= &q
->NextInDQList
; }
4965 // *** 2. Now we can safely build the list of marked answers
4967 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Now build our list of potential answers
4968 if (rr
->NR_AnswerTo
) // If we marked the record...
4969 AddRecordToResponseList(&nrp
, rr
, mDNSNULL
); // ... add it to the list
4972 // *** 3. Add additional records
4974 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
4977 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
4979 for (i
=0; i
<query
->h
.numAnswers
; i
++) // For each record in the query's answer section...
4981 // Get the record...
4983 CacheRecord
*ourcacherr
;
4984 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &m
->rec
);
4985 if (!ptr
) goto exit
;
4987 // See if this Known-Answer suppresses any of our currently planned answers
4988 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4989 if (MustSendRecord(rr
) && ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
4990 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
4992 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
4993 for (rr
=m
->ResourceRecords
; rr
; rr
=rr
->next
)
4995 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
4996 if (rr
->ImmedAnswer
== InterfaceID
&& ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
4998 if (srcaddr
->type
== mDNSAddrType_IPv4
)
5000 if (mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= zerov4Addr
;
5002 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
5004 if (mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= zerov6Addr
;
5006 if (mDNSIPv4AddressIsZero(rr
->v4Requester
) && mDNSIPv6AddressIsZero(rr
->v6Requester
))
5008 rr
->ImmedAnswer
= mDNSNULL
;
5009 rr
->ImmedUnicast
= mDNSfalse
;
5010 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5011 LogMsg("Suppressed after%4d: %s", m
->timenow
- rr
->ImmedAnswerMarkTime
, ARDisplayString(m
, rr
));
5017 // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
5018 // 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).
5019 ourcacherr
= FindIdenticalRecordInCache(m
, &m
->rec
.r
.resrec
);
5020 if (ourcacherr
&& ourcacherr
->MPExpectingKA
&& m
->timenow
- ourcacherr
->MPLastUnansweredQT
< mDNSPlatformOneSecond
)
5022 ourcacherr
->MPUnansweredKA
++;
5023 ourcacherr
->MPExpectingKA
= mDNSfalse
;
5026 // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
5027 // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
5028 // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
5029 eap
= &ExpectedAnswers
;
5032 CacheRecord
*rr
= *eap
;
5033 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
5034 { *eap
= rr
->NextInKAList
; rr
->NextInKAList
= mDNSNULL
; }
5035 else eap
= &rr
->NextInKAList
;
5038 // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
5041 dqp
= &DupQuestions
;
5044 DNSQuestion
*q
= *dqp
;
5045 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
5046 { *dqp
= q
->NextInDQList
; q
->NextInDQList
= mDNSNULL
; }
5047 else dqp
= &q
->NextInDQList
;
5050 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5054 // *** 5. Cancel any additionals that were added because of now-deleted records
5056 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
5057 if (rr
->NR_AdditionalTo
&& !MustSendRecord(rr
->NR_AdditionalTo
))
5058 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
5061 // *** 6. Mark the send flags on the records we plan to send
5063 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
5065 if (rr
->NR_AnswerTo
)
5067 mDNSBool SendMulticastResponse
= mDNSfalse
; // Send modern multicast response
5068 mDNSBool SendUnicastResponse
= mDNSfalse
; // Send modern unicast response (not legacy unicast response)
5070 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
5071 if (m
->timenow
- (rr
->LastMCTime
+ TicksTTL(rr
)/4) >= 0)
5073 SendMulticastResponse
= mDNStrue
;
5074 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
5075 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
5076 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
5077 if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) rr
->NR_AnswerTo
= (mDNSu8
*)~0;
5080 // If the client insists on a multicast response, then we'd better send one
5081 if (rr
->NR_AnswerTo
== (mDNSu8
*)~0) SendMulticastResponse
= mDNStrue
;
5082 else if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) SendUnicastResponse
= mDNStrue
;
5083 else if (rr
->NR_AnswerTo
) SendLegacyResponse
= mDNStrue
;
5085 if (SendMulticastResponse
|| SendUnicastResponse
)
5087 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5088 rr
->ImmedAnswerMarkTime
= m
->timenow
;
5090 m
->NextScheduledResponse
= m
->timenow
;
5091 // If we're already planning to send this on another interface, just send it on all interfaces
5092 if (rr
->ImmedAnswer
&& rr
->ImmedAnswer
!= InterfaceID
)
5093 rr
->ImmedAnswer
= mDNSInterfaceMark
;
5096 rr
->ImmedAnswer
= InterfaceID
; // Record interface to send it on
5097 if (SendUnicastResponse
) rr
->ImmedUnicast
= mDNStrue
;
5098 if (srcaddr
->type
== mDNSAddrType_IPv4
)
5100 if (mDNSIPv4AddressIsZero(rr
->v4Requester
)) rr
->v4Requester
= srcaddr
->ip
.v4
;
5101 else if (!mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= onesIPv4Addr
;
5103 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
5105 if (mDNSIPv6AddressIsZero(rr
->v6Requester
)) rr
->v6Requester
= srcaddr
->ip
.v6
;
5106 else if (!mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= onesIPv6Addr
;
5110 // If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
5111 // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
5112 // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
5113 // else, for a simple unique record reply, we can reply immediately; no need for delay
5114 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
) delayresponse
= mDNSPlatformOneSecond
* 20; // Divided by 50 = 400ms
5115 else if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
5117 else if (rr
->NR_AdditionalTo
&& rr
->NR_AdditionalTo
->NR_AnswerTo
== (mDNSu8
*)~0)
5119 // Since additional records are an optimization anyway, we only ever send them on one interface at a time
5120 // If two clients on different interfaces do queries that invoke the same optional additional answer,
5121 // then the earlier client is out of luck
5122 rr
->ImmedAdditional
= InterfaceID
;
5123 // No need to set m->NextScheduledResponse here
5124 // We'll send these additional records when we send them, or not, as the case may be
5129 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5131 if (delayresponse
&& (!m
->SuppressSending
|| (m
->SuppressSending
- m
->timenow
) < (delayresponse
+ 49) / 50))
5133 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5134 mDNSs32 oldss
= m
->SuppressSending
;
5135 if (oldss
&& delayresponse
)
5136 LogMsg("Current SuppressSending delay%5ld; require%5ld", m
->SuppressSending
- m
->timenow
, (delayresponse
+ 49) / 50);
5138 // Pick a random delay:
5139 // We start with the base delay chosen above (typically either 1 second or 20 seconds),
5140 // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
5141 // This is an integer value, with resolution determined by the platform clock rate.
5142 // We then divide that by 50 to get the delay value in ticks. We defer the division until last
5143 // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
5144 // The +49 before dividing is to ensure we round up, not down, to ensure that even
5145 // on platforms where the native clock rate is less than fifty ticks per second,
5146 // we still guarantee that the final calculated delay is at least one platform tick.
5147 // We want to make sure we don't ever allow the delay to be zero ticks,
5148 // because if that happens we'll fail the Bonjour Conformance Test.
5149 // Our final computed delay is 20-120ms for normal delayed replies,
5150 // or 400-500ms in the case of multi-packet known-answer lists.
5151 m
->SuppressSending
= m
->timenow
+ (delayresponse
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*5) + 49) / 50;
5152 if (m
->SuppressSending
== 0) m
->SuppressSending
= 1;
5153 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5154 if (oldss
&& delayresponse
)
5155 LogMsg("Set SuppressSending to %5ld", m
->SuppressSending
- m
->timenow
);
5160 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
5162 if (SendLegacyResponse
)
5163 responseptr
= GenerateUnicastResponse(query
, end
, InterfaceID
, LegacyQuery
, response
, ResponseRecords
);
5166 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5169 // *** 9. Finally, clear our link chains ready for use next time
5171 while (ResponseRecords
)
5173 rr
= ResponseRecords
;
5174 ResponseRecords
= rr
->NextResponse
;
5175 rr
->NextResponse
= mDNSNULL
;
5176 rr
->NR_AnswerTo
= mDNSNULL
;
5177 rr
->NR_AdditionalTo
= mDNSNULL
;
5180 while (ExpectedAnswers
)
5183 rr
= ExpectedAnswers
;
5184 ExpectedAnswers
= rr
->NextInKAList
;
5185 rr
->NextInKAList
= mDNSNULL
;
5187 // For non-truncated queries, we can definitively say that we should expect
5188 // to be seeing a response for any records still left in the ExpectedAnswers list
5189 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
5190 if (rr
->UnansweredQueries
== 0 || m
->timenow
- rr
->LastUnansweredTime
>= mDNSPlatformOneSecond
)
5192 rr
->UnansweredQueries
++;
5193 rr
->LastUnansweredTime
= m
->timenow
;
5194 if (rr
->UnansweredQueries
> 1)
5195 debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
5196 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5197 SetNextCacheCheckTime(m
, rr
);
5200 // If we've seen multiple unanswered queries for this record,
5201 // then mark it to expire in five seconds if we don't get a response by then.
5202 if (rr
->UnansweredQueries
>= MaxUnansweredQueries
)
5204 // Only show debugging message if this record was not about to expire anyway
5205 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
5206 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5207 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5208 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
5210 // Make a guess, based on the multi-packet query / known answer counts, whether we think we
5211 // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
5212 // possible packet loss of up to 20% of the additional KA packets.)
5213 else if (rr
->MPUnansweredQ
* 4 > rr
->MPUnansweredKA
* 5 + 8)
5215 // We want to do this conservatively.
5216 // If there are so many machines on the network that they have to use multi-packet known-answer lists,
5217 // then we don't want them to all hit the network simultaneously with their final expiration queries.
5218 // By setting the record to expire in four minutes, we achieve two things:
5219 // (a) the 90-95% final expiration queries will be less bunched together
5220 // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
5221 mDNSu32 remain
= (mDNSu32
)(RRExpireTime(rr
) - m
->timenow
) / 4;
5222 if (remain
> 240 * (mDNSu32
)mDNSPlatformOneSecond
)
5223 remain
= 240 * (mDNSu32
)mDNSPlatformOneSecond
;
5225 // Only show debugging message if this record was not about to expire anyway
5226 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
5227 debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5228 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5230 if (remain
<= 60 * (mDNSu32
)mDNSPlatformOneSecond
)
5231 rr
->UnansweredQueries
++; // Treat this as equivalent to one definite unanswered query
5232 rr
->MPUnansweredQ
= 0; // Clear MPQ/MPKA statistics
5233 rr
->MPUnansweredKA
= 0;
5234 rr
->MPExpectingKA
= mDNSfalse
;
5236 if (remain
< kDefaultReconfirmTimeForNoAnswer
)
5237 remain
= kDefaultReconfirmTimeForNoAnswer
;
5238 mDNS_Reconfirm_internal(m
, rr
, remain
);
5242 while (DupQuestions
)
5245 DNSQuestion
*q
= DupQuestions
;
5246 DupQuestions
= q
->NextInDQList
;
5247 q
->NextInDQList
= mDNSNULL
;
5248 i
= RecordDupSuppressInfo(q
->DupSuppress
, m
->timenow
, InterfaceID
, srcaddr
->type
);
5249 debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q
->qname
.c
, DNSTypeName(q
->qtype
), InterfaceID
,
5250 srcaddr
->type
== mDNSAddrType_IPv4
? "v4" : "v6", i
);
5253 return(responseptr
);
5256 mDNSlocal
void mDNSCoreReceiveQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
5257 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
5258 const mDNSInterfaceID InterfaceID
)
5260 mDNSu8
*responseend
= mDNSNULL
;
5261 mDNSBool QueryWasLocalUnicast
= !mDNSAddrIsDNSMulticast(dstaddr
) && AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
5263 if (!InterfaceID
&& mDNSAddrIsDNSMulticast(dstaddr
))
5265 LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s (Multicast, but no InterfaceID)",
5266 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
5267 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
5268 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
5269 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5270 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
5274 verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5275 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
5276 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
5277 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
5278 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5279 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
5281 responseend
= ProcessQuery(m
, msg
, end
, srcaddr
, InterfaceID
,
5282 (srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
), mDNSAddrIsDNSMulticast(dstaddr
), QueryWasLocalUnicast
, &m
->omsg
);
5284 if (responseend
) // If responseend is non-null, that means we built a unicast response packet
5286 debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
5287 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
5288 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
5289 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s",
5290 srcaddr
, mDNSVal16(srcport
), InterfaceID
, srcaddr
->type
);
5291 mDNSSendDNSMessage(m
, &m
->omsg
, responseend
, InterfaceID
, srcaddr
, srcport
, -1, mDNSNULL
);
5295 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
5296 // the record list and/or question list.
5297 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
5298 mDNSlocal
void mDNSCoreReceiveResponse(mDNS
*const m
,
5299 const DNSMessage
*const response
, const mDNSu8
*end
,
5300 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
5301 const mDNSInterfaceID InterfaceID
)
5305 // We ignore questions (if any) in a DNS response packet
5306 const mDNSu8
*ptr
= LocateAnswers(response
, end
);
5308 // "(CacheRecord*)1" is a special (non-zero) end-of-list marker
5309 // We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList
5310 // set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling.
5311 CacheRecord
*CacheFlushRecords
= (CacheRecord
*)1;
5312 CacheRecord
**cfp
= &CacheFlushRecords
;
5314 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
5315 // to guard against spoof responses, then the only credible protection against that is cryptographic
5316 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
5317 int totalrecords
= response
->h
.numAnswers
+ response
->h
.numAuthorities
+ response
->h
.numAdditionals
;
5319 (void)srcaddr
; // Currently used only for display in debugging message
5323 verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5324 srcaddr
, dstaddr
, InterfaceID
,
5325 response
->h
.numQuestions
, response
->h
.numQuestions
== 1 ? ", " : "s,",
5326 response
->h
.numAnswers
, response
->h
.numAnswers
== 1 ? ", " : "s,",
5327 response
->h
.numAuthorities
, response
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5328 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s");
5330 // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us
5331 if (!mDNSAddrIsDNSMulticast(dstaddr
))
5333 if (!AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
) || (mDNSu32
)(m
->timenow
- m
->ExpectUnicastResponse
) > (mDNSu32
)(mDNSPlatformOneSecond
*2))
5335 // For now we don't put standard wide-area unicast responses in our main cache
5336 // (Later we should fix this and cache all known results in a unified manner.)
5337 if (response
->h
.id
.NotAnInteger
!= 0 || srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
)
5341 for (i
= 0; i
< totalrecords
&& ptr
&& ptr
< end
; i
++)
5343 const mDNSu8 RecordType
= (mDNSu8
)((i
< response
->h
.numAnswers
) ? kDNSRecordTypePacketAns
: kDNSRecordTypePacketAdd
);
5344 ptr
= GetLargeResourceRecord(m
, response
, ptr
, end
, InterfaceID
, RecordType
, &m
->rec
);
5345 if (!ptr
) goto exit
; // Break out of the loop and clean up our CacheFlushRecords list before exiting
5347 // 1. Check that this packet resource record does not conflict with any of ours
5348 if (m
->CurrentRecord
) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
5349 m
->CurrentRecord
= m
->ResourceRecords
;
5350 while (m
->CurrentRecord
)
5352 AuthRecord
*rr
= m
->CurrentRecord
;
5353 m
->CurrentRecord
= rr
->next
;
5354 if (PacketRRMatchesSignature(&m
->rec
.r
, rr
)) // If interface, name, type (if shared record) and class match...
5356 // ... check to see if type and rdata are identical
5357 if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
&& SameRData(&m
->rec
.r
.resrec
, &rr
->resrec
))
5359 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
5360 if (m
->rec
.r
.resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/2 || m
->SleepState
)
5362 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
5363 if (rr
->ImmedAnswer
== InterfaceID
) { rr
->ImmedAnswer
= mDNSNULL
; rr
->ImmedUnicast
= mDNSfalse
; }
5367 if (rr
->ImmedAnswer
== mDNSNULL
) { rr
->ImmedAnswer
= InterfaceID
; m
->NextScheduledResponse
= m
->timenow
; }
5368 else if (rr
->ImmedAnswer
!= InterfaceID
) { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
5371 // else, the packet RR has different type or different rdata -- check to see if this is a conflict
5372 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0 && PacketRRConflict(m
, rr
, &m
->rec
.r
))
5374 debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr
-> resrec
.rdatahash
, ARDisplayString(m
, rr
));
5375 debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m
->rec
.r
.resrec
.rdatahash
, CRDisplayString(m
, &m
->rec
.r
));
5377 // If this record is marked DependentOn another record for conflict detection purposes,
5378 // then *that* record has to be bumped back to probing state to resolve the conflict
5379 while (rr
->DependentOn
) rr
= rr
->DependentOn
;
5381 // If we've just whacked this record's ProbeCount, don't need to do it again
5382 if (rr
->ProbeCount
<= DefaultProbeCountForTypeUnique
)
5384 // If we'd previously verified this record, put it back to probing state and try again
5385 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
)
5387 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5388 rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
5389 rr
->ProbeCount
= DefaultProbeCountForTypeUnique
+ 1;
5390 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(kDNSRecordTypeUnique
);
5391 InitializeLastAPTime(m
, rr
);
5392 RecordProbeFailure(m
, rr
); // Repeated late conflicts also cause us to back off to the slower probing rate
5394 // If we're probing for this record, we just failed
5395 else if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
5397 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5398 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
5400 // We assumed this record must be unique, but we were wrong.
5401 // (e.g. There are two mDNSResponders on the same machine giving
5402 // different answers for the reverse mapping record.)
5403 // This is simply a misconfiguration, and we don't try to recover from it.
5404 else if (rr
->resrec
.RecordType
== kDNSRecordTypeKnownUnique
)
5406 debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
5407 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5408 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
5411 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
5412 rr
->resrec
.RecordType
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5415 // Else, matching signature, different type or rdata, but not a considered a conflict.
5416 // If the packet record has the cache-flush bit set, then we check to see if we
5417 // have any record(s) of the same type that we should re-assert to rescue them
5418 // (see note about "multi-homing and bridged networks" at the end of this function).
5419 else if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
)
5420 if ((m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) && m
->timenow
- rr
->LastMCTime
> mDNSPlatformOneSecond
/2)
5421 { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
5425 // 2. See if we want to add this packet resource record to our cache
5426 if (m
->rrcache_size
) // Only try to cache answers if we have a cache to put them in
5428 const mDNSu32 slot
= HashSlot(m
->rec
.r
.resrec
.name
);
5429 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &m
->rec
.r
.resrec
);
5431 // 2a. Check if this packet resource record is already in our cache
5432 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5434 // If we found this exact resource record, refresh its TTL
5435 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
5437 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
5438 verbosedebugf("Found record size %5d interface %p already in cache: %s",
5439 m
->rec
.r
.resrec
.rdlength
, InterfaceID
, CRDisplayString(m
, &m
->rec
.r
));
5440 rr
->TimeRcvd
= m
->timenow
;
5442 if (m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
5444 // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
5445 if (rr
->NextInCFList
== mDNSNULL
&& cfp
!= &rr
->NextInCFList
)
5446 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
5448 // If this packet record is marked unique, and our previous cached copy was not, then fix it
5449 if (!(rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))
5452 for (q
= m
->Questions
; q
; q
=q
->next
) if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) q
->UniqueAnswers
++;
5453 rr
->resrec
.RecordType
= m
->rec
.r
.resrec
.RecordType
;
5457 if (!mDNSPlatformMemSame(m
->rec
.r
.resrec
.rdata
->u
.data
, rr
->resrec
.rdata
->u
.data
, m
->rec
.r
.resrec
.rdlength
))
5459 // If the rdata of the packet record differs in name capitalization from the record in our cache
5460 // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
5461 // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
5462 rr
->resrec
.rroriginalttl
= 0;
5463 rr
->UnansweredQueries
= MaxUnansweredQueries
;
5464 SetNextCacheCheckTime(m
, rr
);
5465 // DO NOT break out here -- we want to continue as if we never found it
5467 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0)
5469 rr
->resrec
.rroriginalttl
= m
->rec
.r
.resrec
.rroriginalttl
;
5470 rr
->UnansweredQueries
= 0;
5471 rr
->MPUnansweredQ
= 0;
5472 rr
->MPUnansweredKA
= 0;
5473 rr
->MPExpectingKA
= mDNSfalse
;
5474 SetNextCacheCheckTime(m
, rr
);
5479 // If the packet TTL is zero, that means we're deleting this record.
5480 // To give other hosts on the network a chance to protest, we push the deletion
5481 // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
5482 // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
5483 // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
5484 rr
->resrec
.rroriginalttl
= 1;
5485 rr
->UnansweredQueries
= MaxUnansweredQueries
;
5486 SetNextCacheCheckTime(m
, rr
);
5492 // If packet resource record not in our cache, add it now
5493 // (unless it is just a deletion of a record we never had, in which case we don't care)
5494 if (!rr
&& m
->rec
.r
.resrec
.rroriginalttl
> 0)
5496 // If we don't have a CacheGroup for this name, make one now
5497 if (!cg
) cg
= GetCacheGroup(m
, slot
, &m
->rec
.r
.resrec
);
5498 if (cg
) rr
= GetCacheRecord(m
, cg
, m
->rec
.r
.resrec
.rdlength
); // Make a cache record, being careful not to recycle cg
5499 if (!rr
) NoCacheAnswer(m
, &m
->rec
.r
);
5502 RData
*saveptr
= rr
->resrec
.rdata
; // Save the rr->resrec.rdata pointer
5503 *rr
= m
->rec
.r
; // Block copy the CacheRecord object
5504 rr
->resrec
.rdata
= saveptr
; // Restore rr->resrec.rdata after the structure assignment
5505 rr
->resrec
.name
= cg
->name
; // And set rr->resrec.name to point into our CacheGroup header
5506 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
5507 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
5508 // If this is an oversized record with external storage allocated, copy rdata to external storage
5509 if (rr
->resrec
.rdata
!= (RData
*)&rr
->rdatastorage
&& !(m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
))
5510 LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m
->rec
.r
.resrec
.name
->c
);
5511 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
5512 mDNSPlatformMemCopy(m
->rec
.r
.resrec
.rdata
, rr
->resrec
.rdata
, sizeofRDataHeader
+ m
->rec
.r
.resrec
.rdlength
);
5513 rr
->next
= mDNSNULL
; // Clear 'next' pointer
5514 *(cg
->rrcache_tail
) = rr
; // Append this record to tail of cache slot list
5515 cg
->rrcache_tail
= &(rr
->next
); // Advance tail pointer
5516 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) // If marked unique, assume we may have
5517 rr
->DelayDelivery
= m
->timenow
+ mDNSPlatformOneSecond
; // to delay delivery of this 'add' event
5519 rr
->DelayDelivery
= CheckForSoonToExpireRecords(m
, rr
->resrec
.name
, rr
->resrec
.namehash
, slot
);
5520 CacheRecordAdd(m
, rr
); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us
5524 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5528 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5530 // If we've just received one or more records with their cache flush bits set,
5531 // then scan that cache slot to see if there are any old stale records we need to flush
5532 while (CacheFlushRecords
!= (CacheRecord
*)1)
5534 CacheRecord
*r1
= CacheFlushRecords
, *r2
;
5535 const mDNSu32 slot
= HashSlot(r1
->resrec
.name
);
5536 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &r1
->resrec
);
5537 CacheFlushRecords
= CacheFlushRecords
->NextInCFList
;
5538 r1
->NextInCFList
= mDNSNULL
;
5539 for (r2
= cg
? cg
->members
: mDNSNULL
; r2
; r2
=r2
->next
)
5540 if (SameResourceRecordSignature(&r1
->resrec
, &r2
->resrec
))
5541 if (RRExpireTime(r2
) - m
->timenow
> mDNSPlatformOneSecond
)
5543 // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
5544 // else, if record is old, mark it to be flushed
5545 if (m
->timenow
- r2
->TimeRcvd
< mDNSPlatformOneSecond
)
5547 if (r2
->resrec
.rroriginalttl
!= r1
->resrec
.rroriginalttl
&& r1
->resrec
.rroriginalttl
> 1)
5548 r2
->resrec
.rroriginalttl
= r1
->resrec
.rroriginalttl
;
5550 else // else, if record is old, mark it to be flushed
5552 verbosedebugf("Cache flush %p X %p %s", r1
, r2
, CRDisplayString(m
, r2
));
5553 // We set stale records to expire in one second.
5554 // This gives the owner a chance to rescue it if necessary.
5555 // This is important in the case of multi-homing and bridged networks:
5556 // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
5557 // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
5558 // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
5559 // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
5560 // By delaying the deletion by one second, we give X a change to notice that this bridging has
5561 // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
5562 // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
5563 // final expiration queries for this record.
5564 r2
->resrec
.rroriginalttl
= 1;
5565 r2
->UnansweredQueries
= MaxUnansweredQueries
;
5567 r2
->TimeRcvd
= m
->timenow
;
5568 SetNextCacheCheckTime(m
, r2
);
5570 if (r1
->DelayDelivery
) // If we were planning to delay delivery of this record, see if we still need to
5572 // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
5573 r1
->DelayDelivery
= CheckForSoonToExpireRecords(m
, r1
->resrec
.name
, r1
->resrec
.namehash
, slot
);
5574 if (!r1
->DelayDelivery
) CacheRecordDeferredAdd(m
, r1
);
5579 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, void *const pkt
, const mDNSu8
*const end
,
5580 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*const dstaddr
, const mDNSIPPort dstport
,
5581 const mDNSInterfaceID InterfaceID
)
5583 DNSMessage
*msg
= (DNSMessage
*)pkt
;
5584 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
5585 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
5587 mDNSu8
*ptr
= mDNSNULL
;
5588 const mDNSu8 UpdateR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
5590 #ifndef UNICAST_DISABLED
5591 if (srcport
.NotAnInteger
== NATPMPPort
.NotAnInteger
)
5594 uDNS_ReceiveNATMap(m
, pkt
, (mDNSu16
)(end
- (mDNSu8
*)pkt
));
5599 if ((unsigned)(end
- (mDNSu8
*)pkt
) < sizeof(DNSMessageHeader
)) { LogMsg("DNS Message too short"); return; }
5600 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
5601 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
5602 ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
5603 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
5604 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
5605 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
5606 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
5608 if (!m
) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
5610 // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
5611 // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
5612 if (!mDNSAddressIsValid(srcaddr
)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr
); return; }
5616 #ifndef UNICAST_DISABLED
5617 if (!mDNSAddressIsAllDNSLinkGroup(dstaddr
) && (QR_OP
== StdR
|| QR_OP
== UpdateR
))
5618 uDNS_ReceiveMsg(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5619 // Note: mDNSCore also needs to get access to received unicast responses
5621 if (QR_OP
== StdQ
) mDNSCoreReceiveQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5622 else if (QR_OP
== StdR
) mDNSCoreReceiveResponse(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5623 else if (QR_OP
!= UpdateR
)
5624 LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
5625 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1], srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
);
5627 // Packet reception often causes a change to the task list:
5628 // 1. Inbound queries can cause us to need to send responses
5629 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
5630 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
5631 // 4. Response packets that answer questions may cause our client to issue new questions
5635 // ***************************************************************************
5636 #if COMPILER_LIKES_PRAGMA_MARK
5639 #pragma mark - Searcher Functions
5642 #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5644 mDNSlocal DNSQuestion
*FindDuplicateQuestion(const mDNS
*const m
, const DNSQuestion
*const question
)
5647 // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
5648 // This prevents circular references, where two questions are each marked as a duplicate of the other.
5649 // Accordingly, we break out of the loop when we get to 'question', because there's no point searching
5650 // further in the list.
5651 for (q
= m
->Questions
; q
&& q
!= question
; q
=q
->next
) // Scan our list of questions
5652 if (q
->InterfaceID
== question
->InterfaceID
&& // for another question with the same InterfaceID,
5653 SameQTarget(q
, question
) && // and same unicast/multicast target settings
5654 q
->qtype
== question
->qtype
&& // type,
5655 q
->qclass
== question
->qclass
&& // class,
5656 q
->qnamehash
== question
->qnamehash
&&
5657 SameDomainName(&q
->qname
, &question
->qname
)) // and name
5662 // This is called after a question is deleted, in case other identical questions were being
5663 // suppressed as duplicates
5664 mDNSlocal
void UpdateQuestionDuplicates(mDNS
*const m
, const DNSQuestion
*const question
)
5667 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5668 if (q
->DuplicateOf
== question
) // To see if any questions were referencing this as their duplicate
5670 q
->ThisQInterval
= question
->ThisQInterval
;
5671 q
->RequestUnicast
= question
->RequestUnicast
;
5672 q
->LastQTime
= question
->LastQTime
;
5673 q
->RecentAnswerPkts
= 0;
5674 q
->DuplicateOf
= FindDuplicateQuestion(m
, q
);
5675 q
->LastQTxTime
= question
->LastQTxTime
;
5676 SetNextQueryTime(m
,q
);
5680 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
5681 ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger))
5683 mDNSlocal mStatus
mDNS_StartQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5685 if (question
->Target
.type
&& !ValidQuestionTarget(question
))
5687 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5688 question
->Target
.type
, mDNSVal16(question
->TargetPort
));
5689 question
->Target
.type
= mDNSAddrType_None
;
5692 if (!question
->Target
.type
) // No question->Target specified, so clear TargetPort and TargetQID
5694 question
->TargetPort
= zeroIPPort
;
5695 question
->TargetQID
= zeroID
;
5698 #ifndef UNICAST_DISABLED
5699 // If the client has specified 'kDNSServiceFlagsForceMulticast'
5700 // then we do a multicast query on that interface, even for unicast domains.
5701 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5702 question
->uDNS_info
.id
= zeroID
;
5703 else return uDNS_StartQuery(m
, question
);
5705 question
->uDNS_info
.id
= zeroID
;
5706 #endif // UNICAST_DISABLED
5708 //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5710 if (m
->rrcache_size
== 0) // Can't do queries if we have no cache space allocated
5711 return(mStatus_NoCache
);
5715 // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
5716 DNSQuestion
**q
= &m
->Questions
;
5717 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5718 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5722 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5723 question
->qname
.c
, DNSTypeName(question
->qtype
));
5724 return(mStatus_AlreadyRegistered
);
5727 // If this question is referencing a specific interface, make sure it exists
5728 if (question
->InterfaceID
&& question
->InterfaceID
!= mDNSInterface_LocalOnly
)
5730 NetworkInterfaceInfo
*intf
;
5731 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5732 if (intf
->InterfaceID
== question
->InterfaceID
) break;
5735 debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question
->qname
.c
, question
->InterfaceID
);
5736 return(mStatus_BadInterfaceErr
);
5740 if (!ValidateDomainName(&question
->qname
))
5742 LogMsg("Attempt to start query with invalid qname %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5743 return(mStatus_Invalid
);
5746 // Note: In the case where we already have the answer to this question in our cache, that may be all the client
5747 // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
5748 // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds
5749 // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately.
5750 if (!m
->RandomQueryDelay
) m
->RandomQueryDelay
= 1 + (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
5752 question
->next
= mDNSNULL
;
5753 question
->qnamehash
= DomainNameHashValue(&question
->qname
); // MUST do this before FindDuplicateQuestion()
5754 question
->DelayAnswering
= CheckForSoonToExpireRecords(m
, &question
->qname
, question
->qnamehash
, HashSlot(&question
->qname
));
5755 question
->ThisQInterval
= InitialQuestionInterval
* 2; // MUST be > zero for an active question
5756 question
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
5757 question
->LastQTime
= m
->timenow
- m
->RandomQueryDelay
; // Avoid inter-machine synchronization
5758 question
->LastAnswerPktNum
= m
->PktNum
;
5759 question
->RecentAnswerPkts
= 0;
5760 question
->CurrentAnswers
= 0;
5761 question
->LargeAnswers
= 0;
5762 question
->UniqueAnswers
= 0;
5763 question
->DuplicateOf
= FindDuplicateQuestion(m
, question
);
5764 question
->NextInDQList
= mDNSNULL
;
5765 for (i
=0; i
<DupSuppressInfoSize
; i
++)
5766 question
->DupSuppress
[i
].InterfaceID
= mDNSNULL
;
5767 // question->InterfaceID must be already set by caller
5768 question
->SendQNow
= mDNSNULL
;
5769 question
->SendOnAll
= mDNSfalse
;
5770 question
->LastQTxTime
= m
->timenow
;
5772 if (!question
->DuplicateOf
)
5773 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started",
5774 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
, question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
);
5776 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)",
5777 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
, question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
, question
->DuplicateOf
);
5780 if (question
->InterfaceID
== mDNSInterface_LocalOnly
)
5782 if (!m
->NewLocalOnlyQuestions
) m
->NewLocalOnlyQuestions
= question
;
5786 if (!m
->NewQuestions
) m
->NewQuestions
= question
;
5787 SetNextQueryTime(m
,question
);
5790 return(mStatus_NoError
);
5794 mDNSlocal mStatus
mDNS_StopQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5796 const mDNSu32 slot
= HashSlot(&question
->qname
);
5797 CacheGroup
*cg
= CacheGroupForName(m
, slot
, question
->qnamehash
, &question
->qname
);
5799 DNSQuestion
**q
= &m
->Questions
;
5801 if (uDNS_IsActiveQuery(question
, &m
->uDNS_info
)) return uDNS_StopQuery(m
, question
);
5803 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5804 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5805 if (*q
) *q
= (*q
)->next
;
5808 if (question
->ThisQInterval
>= 0) // Only log error message if the query was supposed to be active
5809 LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
5810 question
->qname
.c
, DNSTypeName(question
->qtype
));
5811 return(mStatus_BadReferenceErr
);
5814 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
5815 UpdateQuestionDuplicates(m
, question
);
5816 // But don't trash ThisQInterval until afterwards.
5817 question
->ThisQInterval
= -1;
5819 // If there are any cache records referencing this as their active question, then see if any other
5820 // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
5821 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5823 if (rr
->CRActiveQuestion
== question
)
5826 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5827 if (ActiveQuestion(q
) && ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
5829 verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), q
);
5830 rr
->CRActiveQuestion
= q
; // Question used to be active; new value may or may not be null
5831 if (!q
) m
->rrcache_active
--; // If no longer active, decrement rrcache_active count
5835 // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at,
5836 // bump its pointer forward one question.
5837 if (m
->CurrentQuestion
== question
)
5839 debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
5840 question
->qname
.c
, DNSTypeName(question
->qtype
));
5841 m
->CurrentQuestion
= question
->next
;
5844 if (m
->NewQuestions
== question
)
5846 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
5847 question
->qname
.c
, DNSTypeName(question
->qtype
));
5848 m
->NewQuestions
= question
->next
;
5851 if (m
->NewLocalOnlyQuestions
== question
) m
->NewLocalOnlyQuestions
= question
->next
;
5853 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5854 question
->next
= mDNSNULL
;
5855 return(mStatus_NoError
);
5858 mDNSexport mStatus
mDNS_StartQuery(mDNS
*const m
, DNSQuestion
*const question
)
5862 status
= mDNS_StartQuery_internal(m
, question
);
5867 mDNSexport mStatus
mDNS_StopQuery(mDNS
*const m
, DNSQuestion
*const question
)
5871 status
= mDNS_StopQuery_internal(m
, question
);
5876 mDNSexport mStatus
mDNS_Reconfirm(mDNS
*const m
, CacheRecord
*const rr
)
5880 status
= mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
5885 mDNSexport mStatus
mDNS_ReconfirmByValue(mDNS
*const m
, ResourceRecord
*const rr
)
5887 mStatus status
= mStatus_BadReferenceErr
;
5890 cr
= FindIdenticalRecordInCache(m
, rr
);
5891 if (cr
) status
= mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
5896 mDNSexport mStatus
mDNS_StartBrowse(mDNS
*const m
, DNSQuestion
*const question
,
5897 const domainname
*const srv
, const domainname
*const domain
,
5898 const mDNSInterfaceID InterfaceID
, mDNSBool ForceMCast
, mDNSQuestionCallback
*Callback
, void *Context
)
5900 question
->InterfaceID
= InterfaceID
;
5901 question
->Target
= zeroAddr
;
5902 question
->qtype
= kDNSType_PTR
;
5903 question
->qclass
= kDNSClass_IN
;
5904 question
->LongLived
= mDNSfalse
;
5905 question
->ExpectUnique
= mDNSfalse
;
5906 question
->ForceMCast
= ForceMCast
;
5907 question
->QuestionCallback
= Callback
;
5908 question
->QuestionContext
= Context
;
5909 if (!ConstructServiceName(&question
->qname
, mDNSNULL
, srv
, domain
)) return(mStatus_BadParamErr
);
5911 #ifndef UNICAST_DISABLED
5912 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5914 question
->LongLived
= mDNSfalse
;
5915 question
->uDNS_info
.id
= zeroID
;
5916 return(mDNS_StartQuery(m
, question
));
5921 // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5923 question
->LongLived
= mDNStrue
;
5924 status
= uDNS_StartQuery(m
, question
);
5929 return(mDNS_StartQuery(m
, question
));
5930 #endif // UNICAST_DISABLED
5933 mDNSlocal mDNSBool
MachineHasActiveIPv6(mDNS
*const m
)
5935 NetworkInterfaceInfo
*intf
;
5936 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5937 if (intf
->ip
.type
== mDNSAddrType_IPv6
) return(mDNStrue
);
5941 mDNSlocal
void FoundServiceInfoSRV(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
5943 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5944 mDNSBool PortChanged
= (mDNSBool
)(query
->info
->port
.NotAnInteger
!= answer
->rdata
->u
.srv
.port
.NotAnInteger
);
5945 if (!AddRecord
) return;
5946 if (answer
->rrtype
!= kDNSType_SRV
) return;
5948 query
->info
->port
= answer
->rdata
->u
.srv
.port
;
5950 // If this is our first answer, then set the GotSRV flag and start the address query
5953 query
->GotSRV
= mDNStrue
;
5954 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5955 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5956 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5957 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5958 mDNS_StartQuery(m
, &query
->qAv4
);
5959 // Only do the AAAA query if this machine actually has IPv6 active
5960 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5962 // If this is not our first answer, only re-issue the address query if the target host name has changed
5963 else if ((query
->qAv4
.InterfaceID
!= query
->qSRV
.InterfaceID
&& query
->qAv4
.InterfaceID
!= answer
->InterfaceID
) ||
5964 !SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
))
5966 mDNS_StopQuery(m
, &query
->qAv4
);
5967 if (query
->qAv6
.ThisQInterval
>= 0) mDNS_StopQuery(m
, &query
->qAv6
);
5968 if (SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
) && !PortChanged
)
5970 // If we get here, it means:
5971 // 1. This is not our first SRV answer
5972 // 2. The interface ID is different, but the target host and port are the same
5973 // This implies that we're seeing the exact same SRV record on more than one interface, so we should
5974 // make our address queries at least as broad as the original SRV query so that we catch all the answers.
5975 query
->qAv4
.InterfaceID
= query
->qSRV
.InterfaceID
; // Will be mDNSInterface_Any, or a specific interface
5976 query
->qAv6
.InterfaceID
= query
->qSRV
.InterfaceID
;
5980 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5981 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5982 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5983 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5985 debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query
->qAv4
.qname
.c
);
5986 mDNS_StartQuery(m
, &query
->qAv4
);
5987 // Only do the AAAA query if this machine actually has IPv6 active
5988 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5990 else if (query
->ServiceInfoQueryCallback
&& query
->GotADD
&& query
->GotTXT
&& PortChanged
)
5992 if (++query
->Answers
>= 100)
5993 debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
5994 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.srv
.target
.c
,
5995 mDNSVal16(answer
->rdata
->u
.srv
.port
));
5996 query
->ServiceInfoQueryCallback(m
, query
);
5998 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5999 // callback function is allowed to do anything, including deleting this query and freeing its memory.
6002 mDNSlocal
void FoundServiceInfoTXT(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
6004 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
6005 if (!AddRecord
) return;
6006 if (answer
->rrtype
!= kDNSType_TXT
) return;
6007 if (answer
->rdlength
> sizeof(query
->info
->TXTinfo
)) return;
6009 query
->GotTXT
= mDNStrue
;
6010 query
->info
->TXTlen
= answer
->rdlength
;
6011 query
->info
->TXTinfo
[0] = 0; // In case answer->rdlength is zero
6012 mDNSPlatformMemCopy(answer
->rdata
->u
.txt
.c
, query
->info
->TXTinfo
, answer
->rdlength
);
6014 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query
->info
->name
.c
, query
->GotADD
);
6016 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
6017 // callback function is allowed to do anything, including deleting this query and freeing its memory.
6018 if (query
->ServiceInfoQueryCallback
&& query
->GotADD
)
6020 if (++query
->Answers
>= 100)
6021 debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
6022 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.txt
.c
);
6023 query
->ServiceInfoQueryCallback(m
, query
);
6027 mDNSlocal
void FoundServiceInfo(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
6029 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
6030 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
6031 if (!AddRecord
) return;
6033 if (answer
->rrtype
== kDNSType_A
)
6035 query
->info
->ip
.type
= mDNSAddrType_IPv4
;
6036 query
->info
->ip
.ip
.v4
= answer
->rdata
->u
.ipv4
;
6038 else if (answer
->rrtype
== kDNSType_AAAA
)
6040 query
->info
->ip
.type
= mDNSAddrType_IPv6
;
6041 query
->info
->ip
.ip
.v6
= answer
->rdata
->u
.ipv6
;
6045 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer
->name
->c
, answer
->rrtype
, DNSTypeName(answer
->rrtype
));
6049 query
->GotADD
= mDNStrue
;
6050 query
->info
->InterfaceID
= answer
->InterfaceID
;
6052 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query
->info
->ip
.type
, query
->info
->name
.c
, query
->GotTXT
);
6054 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
6055 // callback function is allowed to do anything, including deleting this query and freeing its memory.
6056 if (query
->ServiceInfoQueryCallback
&& query
->GotTXT
)
6058 if (++query
->Answers
>= 100)
6060 if (answer
->rrtype
== kDNSType_A
)
6061 debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.ipv4
);
6063 debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.ipv6
);
6065 query
->ServiceInfoQueryCallback(m
, query
);
6069 // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
6070 // If the query is not interface-specific, then InterfaceID may be zero
6071 // Each time the Callback is invoked, the remainder of the fields will have been filled in
6072 // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
6073 mDNSexport mStatus
mDNS_StartResolveService(mDNS
*const m
,
6074 ServiceInfoQuery
*query
, ServiceInfo
*info
, mDNSServiceInfoQueryCallback
*Callback
, void *Context
)
6079 query
->qSRV
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6080 query
->qSRV
.InterfaceID
= info
->InterfaceID
;
6081 query
->qSRV
.Target
= zeroAddr
;
6082 AssignDomainName(&query
->qSRV
.qname
, &info
->name
);
6083 query
->qSRV
.qtype
= kDNSType_SRV
;
6084 query
->qSRV
.qclass
= kDNSClass_IN
;
6085 query
->qSRV
.LongLived
= mDNSfalse
;
6086 query
->qSRV
.ExpectUnique
= mDNStrue
;
6087 query
->qSRV
.ForceMCast
= mDNSfalse
;
6088 query
->qSRV
.QuestionCallback
= FoundServiceInfoSRV
;
6089 query
->qSRV
.QuestionContext
= query
;
6091 query
->qTXT
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6092 query
->qTXT
.InterfaceID
= info
->InterfaceID
;
6093 query
->qTXT
.Target
= zeroAddr
;
6094 AssignDomainName(&query
->qTXT
.qname
, &info
->name
);
6095 query
->qTXT
.qtype
= kDNSType_TXT
;
6096 query
->qTXT
.qclass
= kDNSClass_IN
;
6097 query
->qTXT
.LongLived
= mDNSfalse
;
6098 query
->qTXT
.ExpectUnique
= mDNStrue
;
6099 query
->qTXT
.ForceMCast
= mDNSfalse
;
6100 query
->qTXT
.QuestionCallback
= FoundServiceInfoTXT
;
6101 query
->qTXT
.QuestionContext
= query
;
6103 query
->qAv4
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6104 query
->qAv4
.InterfaceID
= info
->InterfaceID
;
6105 query
->qAv4
.Target
= zeroAddr
;
6106 query
->qAv4
.qname
.c
[0] = 0;
6107 query
->qAv4
.qtype
= kDNSType_A
;
6108 query
->qAv4
.qclass
= kDNSClass_IN
;
6109 query
->qAv4
.LongLived
= mDNSfalse
;
6110 query
->qAv4
.ExpectUnique
= mDNStrue
;
6111 query
->qAv4
.ForceMCast
= mDNSfalse
;
6112 query
->qAv4
.QuestionCallback
= FoundServiceInfo
;
6113 query
->qAv4
.QuestionContext
= query
;
6115 query
->qAv6
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6116 query
->qAv6
.InterfaceID
= info
->InterfaceID
;
6117 query
->qAv6
.Target
= zeroAddr
;
6118 query
->qAv6
.qname
.c
[0] = 0;
6119 query
->qAv6
.qtype
= kDNSType_AAAA
;
6120 query
->qAv6
.qclass
= kDNSClass_IN
;
6121 query
->qAv6
.LongLived
= mDNSfalse
;
6122 query
->qAv6
.ExpectUnique
= mDNStrue
;
6123 query
->qAv6
.ForceMCast
= mDNSfalse
;
6124 query
->qAv6
.QuestionCallback
= FoundServiceInfo
;
6125 query
->qAv6
.QuestionContext
= query
;
6127 query
->GotSRV
= mDNSfalse
;
6128 query
->GotTXT
= mDNSfalse
;
6129 query
->GotADD
= mDNSfalse
;
6133 query
->ServiceInfoQueryCallback
= Callback
;
6134 query
->ServiceInfoQueryContext
= Context
;
6136 // info->name = Must already be set up by client
6137 // info->interface = Must already be set up by client
6138 info
->ip
= zeroAddr
;
6139 info
->port
= zeroIPPort
;
6142 // We use mDNS_StartQuery_internal here because we're already holding the lock
6143 status
= mDNS_StartQuery_internal(m
, &query
->qSRV
);
6144 if (status
== mStatus_NoError
) status
= mDNS_StartQuery_internal(m
, &query
->qTXT
);
6145 if (status
!= mStatus_NoError
) mDNS_StopResolveService(m
, query
);
6151 mDNSexport
void mDNS_StopResolveService (mDNS
*const m
, ServiceInfoQuery
*q
)
6154 // We use mDNS_StopQuery_internal here because we're already holding the lock
6155 if (q
->qSRV
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qSRV
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qSRV
);
6156 if (q
->qTXT
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qTXT
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qTXT
);
6157 if (q
->qAv4
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qAv4
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qAv4
);
6158 if (q
->qAv6
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qAv6
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qAv6
);
6162 mDNSexport mStatus
mDNS_GetDomains(mDNS
*const m
, DNSQuestion
*const question
, mDNS_DomainType DomainType
, const domainname
*dom
,
6163 const mDNSInterfaceID InterfaceID
, mDNSQuestionCallback
*Callback
, void *Context
)
6165 question
->InterfaceID
= InterfaceID
;
6166 question
->Target
= zeroAddr
;
6167 question
->qtype
= kDNSType_PTR
;
6168 question
->qclass
= kDNSClass_IN
;
6169 question
->LongLived
= mDNSfalse
;
6170 question
->ExpectUnique
= mDNSfalse
;
6171 question
->ForceMCast
= mDNSfalse
;
6172 question
->QuestionCallback
= Callback
;
6173 question
->QuestionContext
= Context
;
6174 if (DomainType
> mDNS_DomainTypeMax
) return(mStatus_BadParamErr
);
6175 if (!MakeDomainNameFromDNSNameString(&question
->qname
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
6176 if (!dom
) dom
= &localdomain
;
6177 if (!AppendDomainName(&question
->qname
, dom
)) return(mStatus_BadParamErr
);
6178 return(mDNS_StartQuery(m
, question
));
6181 // ***************************************************************************
6182 #if COMPILER_LIKES_PRAGMA_MARK
6184 #pragma mark - Responder Functions
6187 mDNSexport mStatus
mDNS_Register(mDNS
*const m
, AuthRecord
*const rr
)
6191 status
= mDNS_Register_internal(m
, rr
);
6196 mDNSexport mStatus
mDNS_Update(mDNS
*const m
, AuthRecord
*const rr
, mDNSu32 newttl
,
6197 const mDNSu16 newrdlength
, RData
*const newrdata
, mDNSRecordUpdateCallback
*Callback
)
6199 #ifndef UNICAST_DISABLED
6200 mDNSBool unicast
= !(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(rr
->resrec
.name
));
6202 mDNSBool unicast
= mDNSfalse
;
6205 if (!ValidateRData(rr
->resrec
.rrtype
, newrdlength
, newrdata
))
6206 { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr
->resrec
, &newrdata
->u
, m
->MsgBuffer
)); return(mStatus_Invalid
); }
6210 // If TTL is unspecified, leave TTL unchanged
6211 if (newttl
== 0) newttl
= rr
->resrec
.rroriginalttl
;
6213 // If we already have an update queued up which has not gone through yet,
6214 // give the client a chance to free that memory
6215 if (!unicast
&& rr
->NewRData
)
6217 RData
*n
= rr
->NewRData
;
6218 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
6219 if (rr
->UpdateCallback
)
6220 rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
6223 rr
->NewRData
= newrdata
;
6224 rr
->newrdlength
= newrdlength
;
6225 rr
->UpdateCallback
= Callback
;
6227 if (unicast
) { mStatus status
= uDNS_UpdateRecord(m
, rr
); mDNS_Unlock(m
); return(status
); }
6229 if (rr
->resrec
.rroriginalttl
== newttl
&& rr
->resrec
.rdlength
== newrdlength
&& mDNSPlatformMemSame(rr
->resrec
.rdata
->u
.data
, newrdata
->u
.data
, newrdlength
))
6230 CompleteRDataUpdate(m
, rr
);
6234 domainname type
, domain
;
6235 DeconstructServiceName(rr
->resrec
.name
, &name
, &type
, &domain
);
6236 rr
->AnnounceCount
= InitialAnnounceCount
;
6237 // iChat often does suprious record updates where no data has changed. For the _presence service type, using
6238 // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
6239 // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
6240 // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
6241 // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
6242 if (SameDomainLabel(type
.c
, (mDNSu8
*)"\x6_ichat")) rr
->AnnounceCount
= 1;
6243 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6244 InitializeLastAPTime(m
, rr
);
6245 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
6246 if (!rr
->UpdateBlocked
&& rr
->UpdateCredits
) rr
->UpdateCredits
--;
6247 if (!rr
->NextUpdateCredit
) rr
->NextUpdateCredit
= NonZeroTime(m
->timenow
+ kUpdateCreditRefreshInterval
);
6248 if (rr
->AnnounceCount
> rr
->UpdateCredits
+ 1) rr
->AnnounceCount
= (mDNSu8
)(rr
->UpdateCredits
+ 1);
6249 if (rr
->UpdateCredits
<= 5)
6251 mDNSu32 delay
= 6 - rr
->UpdateCredits
; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
6252 if (!rr
->UpdateBlocked
) rr
->UpdateBlocked
= NonZeroTime(m
->timenow
+ (mDNSs32
)delay
* mDNSPlatformOneSecond
);
6253 rr
->ThisAPInterval
*= 4;
6254 rr
->LastAPTime
= rr
->UpdateBlocked
- rr
->ThisAPInterval
;
6255 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr
->resrec
.name
->c
, delay
, delay
> 1 ? "s" : "");
6257 rr
->resrec
.rroriginalttl
= newttl
;
6261 return(mStatus_NoError
);
6264 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
6265 // the record list and/or question list.
6266 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6267 mDNSexport mStatus
mDNS_Deregister(mDNS
*const m
, AuthRecord
*const rr
)
6271 status
= mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
6276 mDNSexport
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
);
6278 mDNSlocal NetworkInterfaceInfo
*FindFirstAdvertisedInterface(mDNS
*const m
)
6280 NetworkInterfaceInfo
*intf
;
6281 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6282 if (intf
->Advertise
) break;
6286 mDNSlocal
void AdvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6289 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
6290 if (!primary
) primary
= set
; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
6292 // Send dynamic update for non-linklocal IPv4 Addresses
6293 mDNS_SetupResourceRecord(&set
->RR_A
, mDNSNULL
, set
->InterfaceID
, kDNSType_A
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNS_HostNameCallback
, set
);
6294 mDNS_SetupResourceRecord(&set
->RR_PTR
, mDNSNULL
, set
->InterfaceID
, kDNSType_PTR
, kHostNameTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
6295 mDNS_SetupResourceRecord(&set
->RR_HINFO
, mDNSNULL
, set
->InterfaceID
, kDNSType_HINFO
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
6297 #if ANSWER_REMOTE_HOSTNAME_QUERIES
6298 set
->RR_A
.AllowRemoteQuery
= mDNStrue
;
6299 set
->RR_PTR
.AllowRemoteQuery
= mDNStrue
;
6300 set
->RR_HINFO
.AllowRemoteQuery
= mDNStrue
;
6302 // 1. Set up Address record to map from host name ("foo.local.") to IP address
6303 // 2. Set up reverse-lookup PTR record to map from our address back to our host name
6304 AssignDomainName(set
->RR_A
.resrec
.name
, &m
->MulticastHostname
);
6305 if (set
->ip
.type
== mDNSAddrType_IPv4
)
6307 set
->RR_A
.resrec
.rrtype
= kDNSType_A
;
6308 set
->RR_A
.resrec
.rdata
->u
.ipv4
= set
->ip
.ip
.v4
;
6309 // Note: This is reverse order compared to a normal dotted-decimal IP address
6310 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.",
6311 set
->ip
.ip
.v4
.b
[3], set
->ip
.ip
.v4
.b
[2], set
->ip
.ip
.v4
.b
[1], set
->ip
.ip
.v4
.b
[0]);
6313 else if (set
->ip
.type
== mDNSAddrType_IPv6
)
6316 set
->RR_A
.resrec
.rrtype
= kDNSType_AAAA
;
6317 set
->RR_A
.resrec
.rdata
->u
.ipv6
= set
->ip
.ip
.v6
;
6318 for (i
= 0; i
< 16; i
++)
6320 static const char hexValues
[] = "0123456789ABCDEF";
6321 buffer
[i
* 4 ] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] & 0x0F];
6322 buffer
[i
* 4 + 1] = '.';
6323 buffer
[i
* 4 + 2] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] >> 4];
6324 buffer
[i
* 4 + 3] = '.';
6326 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
6329 MakeDomainNameFromDNSNameString(set
->RR_PTR
.resrec
.name
, buffer
);
6330 set
->RR_PTR
.HostTarget
= mDNStrue
; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
6331 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
6333 set
->RR_A
.RRSet
= &primary
->RR_A
; // May refer to self
6335 mDNS_Register_internal(m
, &set
->RR_A
);
6336 mDNS_Register_internal(m
, &set
->RR_PTR
);
6338 if (m
->HIHardware
.c
[0] > 0 && m
->HISoftware
.c
[0] > 0 && m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0] <= 254)
6340 mDNSu8
*p
= set
->RR_HINFO
.resrec
.rdata
->u
.data
;
6341 AssignDomainName(set
->RR_HINFO
.resrec
.name
, &m
->MulticastHostname
);
6342 set
->RR_HINFO
.DependentOn
= &set
->RR_A
;
6343 mDNSPlatformMemCopy(&m
->HIHardware
, p
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
6345 mDNSPlatformMemCopy(&m
->HISoftware
, p
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
6346 mDNS_Register_internal(m
, &set
->RR_HINFO
);
6350 debugf("Not creating HINFO record: platform support layer provided no information");
6351 set
->RR_HINFO
.resrec
.RecordType
= kDNSRecordTypeUnregistered
;
6355 mDNSlocal
void DeadvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6357 NetworkInterfaceInfo
*intf
;
6359 // If we still have address records referring to this one, update them
6360 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
6361 AuthRecord
*A
= primary
? &primary
->RR_A
: mDNSNULL
;
6362 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6363 if (intf
->RR_A
.RRSet
== &set
->RR_A
)
6364 intf
->RR_A
.RRSet
= A
;
6366 // Unregister these records.
6367 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
6368 // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
6369 // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
6370 // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
6371 if (set
->RR_A
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_A
, mDNS_Dereg_normal
);
6372 if (set
->RR_PTR
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_PTR
, mDNS_Dereg_normal
);
6373 if (set
->RR_HINFO
.resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_HINFO
, mDNS_Dereg_normal
);
6376 mDNSexport
void mDNS_SetFQDN(mDNS
*const m
)
6378 domainname newmname
;
6379 NetworkInterfaceInfo
*intf
;
6383 if (!AppendDomainLabel(&newmname
, &m
->hostlabel
)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6384 if (!AppendLiteralLabelString(&newmname
, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6385 if (SameDomainName(&m
->MulticastHostname
, &newmname
)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
6388 AssignDomainName(&m
->MulticastHostname
, &newmname
);
6390 // 1. Stop advertising our address records on all interfaces
6391 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6392 if (intf
->Advertise
) DeadvertiseInterface(m
, intf
);
6394 // 2. Start advertising our address records using the new name
6395 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6396 if (intf
->Advertise
) AdvertiseInterface(m
, intf
);
6398 // 3. Make sure that any SRV records (and the like) that reference our
6399 // host name in their rdata get updated to reference this new host name
6400 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) if (rr
->HostTarget
) SetTargetToHostName(m
, rr
);
6401 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
) if (rr
->HostTarget
) SetTargetToHostName(m
, rr
);
6406 mDNSexport
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6408 (void)rr
; // Unused parameter
6412 char *msg
= "Unknown result";
6413 if (result
== mStatus_NoError
) msg
= "Name registered";
6414 else if (result
== mStatus_NameConflict
) msg
= "Name conflict";
6415 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6419 if (result
== mStatus_NoError
)
6421 // Notify the client that the host name is successfully registered
6422 if (m
->MainCallback
)
6424 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
6425 m
->MainCallback(m
, result
);
6426 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
6429 else if (result
== mStatus_NameConflict
)
6431 domainlabel oldlabel
= m
->hostlabel
;
6433 // 1. First give the client callback a chance to pick a new name
6434 if (m
->MainCallback
)
6436 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
6437 m
->MainCallback(m
, mStatus_NameConflict
);
6438 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
6441 // 2. If the client callback didn't do it, add (or increment) an index ourselves
6442 if (SameDomainLabel(m
->hostlabel
.c
, oldlabel
.c
))
6443 IncrementLabelSuffix(&m
->hostlabel
, mDNSfalse
);
6445 // 3. Generate the FQDNs from the hostlabel,
6446 // and make sure all SRV records, etc., are updated to reference our new hostname
6448 LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel
.c
, m
->hostlabel
.c
);
6450 else if (result
== mStatus_MemFree
)
6452 // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
6453 // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
6454 debugf("mDNS_HostNameCallback: MemFree (ignored)");
6457 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result
, rr
->resrec
.name
->c
);
6460 mDNSlocal
void UpdateInterfaceProtocols(mDNS
*const m
, NetworkInterfaceInfo
*active
)
6462 NetworkInterfaceInfo
*intf
;
6463 active
->IPv4Available
= mDNSfalse
;
6464 active
->IPv6Available
= mDNSfalse
;
6465 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6466 if (intf
->InterfaceID
== active
->InterfaceID
)
6468 if (intf
->ip
.type
== mDNSAddrType_IPv4
&& intf
->McastTxRx
) active
->IPv4Available
= mDNStrue
;
6469 if (intf
->ip
.type
== mDNSAddrType_IPv6
&& intf
->McastTxRx
) active
->IPv6Available
= mDNStrue
;
6473 mDNSexport mStatus
mDNS_RegisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, mDNSs32 delay
)
6475 mDNSBool FirstOfType
= mDNStrue
;
6476 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6478 if (!set
->InterfaceID
)
6479 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set
->ip
); return(mStatus_Invalid
); }
6481 if (!mDNSAddressIsValidNonZero(&set
->mask
))
6482 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set
->ip
, &set
->mask
); return(mStatus_Invalid
); }
6486 // Assume this interface will be active
6487 set
->InterfaceActive
= mDNStrue
;
6488 set
->IPv4Available
= (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
);
6489 set
->IPv6Available
= (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
);
6495 LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
6497 return(mStatus_AlreadyRegistered
);
6500 // This InterfaceID is already in the list, so mark this interface inactive for now
6501 if ((*p
)->InterfaceID
== set
->InterfaceID
)
6503 set
->InterfaceActive
= mDNSfalse
;
6504 if (set
->ip
.type
== (*p
)->ip
.type
) FirstOfType
= mDNSfalse
;
6505 if (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
) (*p
)->IPv4Available
= mDNStrue
;
6506 if (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
) (*p
)->IPv6Available
= mDNStrue
;
6512 set
->next
= mDNSNULL
;
6516 AdvertiseInterface(m
, set
);
6518 debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set
->InterfaceID
, &set
->ip
,
6519 set
->InterfaceActive
?
6520 "not represented in list; marking active and retriggering queries" :
6521 "already represented in list; marking inactive for now");
6523 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6524 // giving the false impression that there's an active representative of this interface when there really isn't.
6525 // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
6526 // even if we believe that we previously had an active representative of this interface.
6527 if (set
->McastTxRx
&& ((m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) || FirstOfType
|| set
->InterfaceActive
))
6531 mDNSs32 initial
= InitialQuestionInterval
;
6533 // Use a small amount of randomness:
6534 // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at
6535 // exactly the same time, we don't want them to all go and hit the network with identical queries at exactly the same moment.
6536 if (!m
->SuppressSending
) m
->SuppressSending
= m
->timenow
+ (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
6540 LogMsg("Repeated transitions for interface %s (%#a); delaying packets by %d seconds",
6541 set
->ifname
, &set
->ip
, delay
/mDNSPlatformOneSecond
);
6542 initial
= InitialQuestionInterval
* 8; // Delay between first and second queries is eight seconds
6543 if (!m
->SuppressProbes
||
6544 m
->SuppressProbes
- (m
->timenow
+ delay
) < 0)
6545 m
->SuppressProbes
= (m
->timenow
+ delay
);
6547 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
6548 if (!q
->InterfaceID
|| q
->InterfaceID
== set
->InterfaceID
) // If non-specific Q, or Q on this specific interface,
6549 { // then reactivate this question
6550 q
->ThisQInterval
= initial
; // MUST be > zero for an active question
6551 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
6552 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
+ delay
;
6553 q
->RecentAnswerPkts
= 0;
6554 SetNextQueryTime(m
,q
);
6557 // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
6558 // we now need them to re-probe if necessary, and then re-announce.
6559 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
6560 if (!rr
->resrec
.InterfaceID
|| rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6562 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
6563 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
6564 rr
->AnnounceCount
= delay
? (mDNSu8
)1 : InitialAnnounceCount
;
6565 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6566 InitializeLastAPTime(m
, rr
);
6571 return(mStatus_NoError
);
6574 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
6575 // the record list and/or question list.
6576 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6577 mDNSexport
void mDNS_DeregisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6579 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6581 mDNSBool revalidate
= mDNSfalse
;
6582 // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
6583 // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
6584 // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
6585 if (m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) revalidate
= mDNStrue
;
6589 // Find this record in our list
6590 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
6591 if (!*p
) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m
); return; }
6593 // Unlink this record from our list
6595 set
->next
= mDNSNULL
;
6597 if (!set
->InterfaceActive
)
6599 // If this interface not the active member of its set, update the v4/v6Available flags for the active member
6600 NetworkInterfaceInfo
*intf
;
6601 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6602 if (intf
->InterfaceActive
&& intf
->InterfaceID
== set
->InterfaceID
)
6603 UpdateInterfaceProtocols(m
, intf
);
6607 NetworkInterfaceInfo
*intf
;
6608 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6609 if (intf
->InterfaceID
== set
->InterfaceID
)
6613 debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
6615 intf
->InterfaceActive
= mDNStrue
;
6616 UpdateInterfaceProtocols(m
, intf
);
6618 // See if another representative *of the same type* exists. If not, we mave have gone from
6619 // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
6620 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6621 if (intf
->InterfaceID
== set
->InterfaceID
&& intf
->ip
.type
== set
->ip
.type
)
6623 if (!intf
) revalidate
= mDNStrue
;
6631 debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
6634 // 1. Deactivate any questions specific to this interface
6635 for (q
= m
->Questions
; q
; q
=q
->next
)
6636 if (q
->InterfaceID
== set
->InterfaceID
)
6637 q
->ThisQInterval
= 0;
6639 // 2. Flush any cache records received on this interface
6640 revalidate
= mDNSfalse
; // Don't revalidate if we're flushing the records
6641 FORALL_CACHERECORDS(slot
, cg
, rr
)
6642 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6643 PurgeCacheResourceRecord(m
, rr
);
6647 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6648 if (set
->Advertise
) DeadvertiseInterface(m
, set
);
6650 // If we have any cache records received on this interface that went away, then re-verify them.
6651 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6652 // giving the false impression that there's an active representative of this interface when there really isn't.
6653 // Don't need to do this when shutting down, because *all* interfaces are about to go away
6654 if (revalidate
&& !m
->mDNS_shutdown
)
6659 m
->NextCacheCheck
= m
->timenow
;
6660 FORALL_CACHERECORDS(slot
, cg
, rr
)
6661 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6662 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForCableDisconnect
);
6668 mDNSlocal
void ServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6670 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6671 (void)m
; // Unused parameter
6675 char *msg
= "Unknown result";
6676 if (result
== mStatus_NoError
) msg
= "Name Registered";
6677 else if (result
== mStatus_NameConflict
) msg
= "Name Conflict";
6678 else if (result
== mStatus_MemFree
) msg
= "Memory Free";
6679 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6683 // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
6684 if (result
== mStatus_NoError
&& rr
!= &sr
->RR_SRV
) return;
6686 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6687 if (result
== mStatus_NameConflict
)
6689 sr
->Conflict
= mDNStrue
; // Record that this service set had a conflict
6690 mDNS_DeregisterService(m
, sr
); // Unlink the records from our list
6694 if (result
== mStatus_MemFree
)
6696 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
6697 // don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
6699 if (sr
->RR_PTR
.resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6700 for (i
=0; i
<sr
->NumSubTypes
; i
++) if (sr
->SubTypes
[i
].resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6702 // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
6703 // then we can now report the NameConflict to the client
6704 if (sr
->Conflict
) result
= mStatus_NameConflict
;
6707 // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
6708 // function is allowed to do anything, including deregistering this service and freeing its memory.
6709 if (sr
->ServiceCallback
)
6710 sr
->ServiceCallback(m
, sr
, result
);
6713 mDNSlocal
void NSSCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6715 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6716 if (sr
->ServiceCallback
)
6717 sr
->ServiceCallback(m
, sr
, result
);
6721 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
6722 // Type is service type (e.g. "_ipp._tcp.")
6723 // Domain is fully qualified domain name (i.e. ending with a null label)
6724 // We always register a TXT, even if it is empty (so that clients are not
6725 // left waiting forever looking for a nonexistent record.)
6726 // If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6727 // then the default host name (m->MulticastHostname) is automatically used
6728 mDNSexport mStatus
mDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*sr
,
6729 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6730 const domainname
*const host
, mDNSIPPort port
, const mDNSu8 txtinfo
[], mDNSu16 txtlen
,
6731 AuthRecord
*SubTypes
, mDNSu32 NumSubTypes
,
6732 const mDNSInterfaceID InterfaceID
, mDNSServiceCallback Callback
, void *Context
)
6737 sr
->ServiceCallback
= Callback
;
6738 sr
->ServiceContext
= Context
;
6739 sr
->Extras
= mDNSNULL
;
6740 sr
->NumSubTypes
= NumSubTypes
;
6741 sr
->SubTypes
= SubTypes
;
6742 sr
->Conflict
= mDNSfalse
;
6743 if (host
&& host
->c
[0]) sr
->Host
= *host
;
6744 else sr
->Host
.c
[0] = 0;
6746 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6747 if (!port
.NotAnInteger
) return(mDNS_RegisterNoSuchService(m
, &sr
->RR_SRV
, name
, type
, domain
, mDNSNULL
, mDNSInterface_Any
, NSSCallback
, sr
));
6749 // Initialize the AuthRecord objects to sane values
6750 mDNS_SetupResourceRecord(&sr
->RR_ADV
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeAdvisory
, ServiceCallback
, sr
);
6751 mDNS_SetupResourceRecord(&sr
->RR_PTR
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6752 mDNS_SetupResourceRecord(&sr
->RR_SRV
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6753 mDNS_SetupResourceRecord(&sr
->RR_TXT
, mDNSNULL
, InterfaceID
, kDNSType_TXT
, kStandardTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6755 // If the client is registering an oversized TXT record,
6756 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
6757 if (sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
< txtlen
)
6758 sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
= txtlen
;
6760 // Set up the record names
6761 // For now we only create an advisory record for the main type, not for subtypes
6762 // We need to gain some operational experience before we decide if there's a need to create them for subtypes too
6763 if (ConstructServiceName(sr
->RR_ADV
.resrec
.name
, (domainlabel
*)"\x09_services", (domainname
*)"\x07_dns-sd\x04_udp", domain
) == mDNSNULL
)
6764 return(mStatus_BadParamErr
);
6765 if (ConstructServiceName(sr
->RR_PTR
.resrec
.name
, mDNSNULL
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6766 if (ConstructServiceName(sr
->RR_SRV
.resrec
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6767 AssignDomainName(sr
->RR_TXT
.resrec
.name
, sr
->RR_SRV
.resrec
.name
);
6769 // 1. Set up the ADV record rdata to advertise our service type
6770 AssignDomainName(&sr
->RR_ADV
.resrec
.rdata
->u
.name
, sr
->RR_PTR
.resrec
.name
);
6772 // 2. Set up the PTR record rdata to point to our service name
6773 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6774 AssignDomainName(&sr
->RR_PTR
.resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6775 sr
->RR_PTR
.Additional1
= &sr
->RR_SRV
;
6776 sr
->RR_PTR
.Additional2
= &sr
->RR_TXT
;
6778 // 2a. Set up any subtype PTRs to point to our service name
6779 // If the client is using subtypes, it is the client's responsibility to have
6780 // already set the first label of the record name to the subtype being registered
6781 for (i
=0; i
<NumSubTypes
; i
++)
6784 AssignDomainName(&st
, sr
->SubTypes
[i
].resrec
.name
);
6785 st
.c
[1+st
.c
[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
6786 AppendDomainName(&st
, type
);
6787 mDNS_SetupResourceRecord(&sr
->SubTypes
[i
], mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6788 if (ConstructServiceName(sr
->SubTypes
[i
].resrec
.name
, mDNSNULL
, &st
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6789 AssignDomainName(&sr
->SubTypes
[i
].resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6790 sr
->SubTypes
[i
].Additional1
= &sr
->RR_SRV
;
6791 sr
->SubTypes
[i
].Additional2
= &sr
->RR_TXT
;
6794 // 3. Set up the SRV record rdata.
6795 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.priority
= 0;
6796 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.weight
= 0;
6797 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
= port
;
6799 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6800 if (sr
->Host
.c
[0]) AssignDomainName(&sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
, &sr
->Host
);
6801 else { sr
->RR_SRV
.HostTarget
= mDNStrue
; sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
.c
[0] = '\0'; }
6803 // 4. Set up the TXT record rdata,
6804 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6805 if (txtinfo
== mDNSNULL
) sr
->RR_TXT
.resrec
.rdlength
= 0;
6806 else if (txtinfo
!= sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
)
6808 sr
->RR_TXT
.resrec
.rdlength
= txtlen
;
6809 if (sr
->RR_TXT
.resrec
.rdlength
> sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
) return(mStatus_BadParamErr
);
6810 mDNSPlatformMemCopy(txtinfo
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, txtlen
);
6812 sr
->RR_TXT
.DependentOn
= &sr
->RR_SRV
;
6814 #ifndef UNICAST_DISABLED
6815 // If the client has specified an explicit InterfaceID,
6816 // then we do a multicast registration on that interface, even for unicast domains.
6817 if (!(InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6821 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6822 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6823 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6824 // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
6825 if (!sr
->RR_TXT
.resrec
.rdlength
) { sr
->RR_TXT
.resrec
.rdlength
= 1; sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6826 status
= uDNS_RegisterService(m
, sr
);
6832 err
= mDNS_Register_internal(m
, &sr
->RR_SRV
);
6833 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_TXT
);
6834 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
6835 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
6836 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
6837 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
6838 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
6839 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_ADV
);
6840 for (i
=0; i
<NumSubTypes
; i
++) if (!err
) err
= mDNS_Register_internal(m
, &sr
->SubTypes
[i
]);
6841 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_PTR
);
6845 if (err
) mDNS_DeregisterService(m
, sr
);
6849 mDNSexport mStatus
mDNS_AddRecordToService(mDNS
*const m
, ServiceRecordSet
*sr
,
6850 ExtraResourceRecord
*extra
, RData
*rdata
, mDNSu32 ttl
)
6852 ExtraResourceRecord
**e
;
6855 extra
->next
= mDNSNULL
;
6856 mDNS_SetupResourceRecord(&extra
->r
, rdata
, sr
->RR_PTR
.resrec
.InterfaceID
, extra
->r
.resrec
.rrtype
, ttl
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6857 AssignDomainName(extra
->r
.resrec
.name
, sr
->RR_SRV
.resrec
.name
);
6859 #ifndef UNICAST_DISABLED
6860 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6863 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6864 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6865 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6866 // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck)
6867 if (extra
->r
.resrec
.rrtype
== kDNSType_TXT
&& extra
->r
.resrec
.rdlength
== 0)
6868 { extra
->r
.resrec
.rdlength
= 1; extra
->r
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6869 status
= uDNS_AddRecordToService(m
, sr
, extra
);
6877 while (*e
) e
= &(*e
)->next
;
6879 if (ttl
== 0) ttl
= kStandardTTL
;
6881 extra
->r
.DependentOn
= &sr
->RR_SRV
;
6883 debugf("mDNS_AddRecordToService adding record to %##s", extra
->r
.resrec
.name
->c
);
6885 status
= mDNS_Register_internal(m
, &extra
->r
);
6886 if (status
== mStatus_NoError
) *e
= extra
;
6891 mDNSexport mStatus
mDNS_RemoveRecordFromService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
, mDNSRecordCallback MemFreeCallback
, void *Context
)
6893 ExtraResourceRecord
**e
;
6898 while (*e
&& *e
!= extra
) e
= &(*e
)->next
;
6901 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra
->r
.resrec
.name
->c
);
6902 status
= mStatus_BadReferenceErr
;
6906 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra
->r
.resrec
.name
->c
);
6907 extra
->r
.RecordCallback
= MemFreeCallback
;
6908 extra
->r
.RecordContext
= Context
;
6910 #ifndef UNICAST_DISABLED
6911 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6912 status
= uDNS_DeregisterRecord(m
, &extra
->r
);
6915 status
= mDNS_Deregister_internal(m
, &extra
->r
, mDNS_Dereg_normal
);
6921 mDNSexport mStatus
mDNS_RenameAndReregisterService(mDNS
*const m
, ServiceRecordSet
*const sr
, const domainlabel
*newname
)
6923 // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
6924 // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
6925 domainlabel name1
, name2
;
6926 domainname type
, domain
;
6927 domainname
*host
= mDNSNULL
;
6928 ExtraResourceRecord
*extras
= sr
->Extras
;
6931 DeconstructServiceName(sr
->RR_SRV
.resrec
.name
, &name1
, &type
, &domain
);
6935 IncrementLabelSuffix(&name2
, mDNStrue
);
6938 LogMsg("Service \"%##s\" renamed to \"%#s\"", sr
->RR_SRV
.resrec
.name
->c
, newname
->c
);
6939 if (sr
->RR_SRV
.HostTarget
== mDNSfalse
&& sr
->Host
.c
[0]) host
= &sr
->Host
;
6941 err
= mDNS_RegisterService(m
, sr
, newname
, &type
, &domain
,
6942 host
, sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, sr
->RR_TXT
.resrec
.rdlength
,
6943 sr
->SubTypes
, sr
->NumSubTypes
,
6944 sr
->RR_PTR
.resrec
.InterfaceID
, sr
->ServiceCallback
, sr
->ServiceContext
);
6946 // mDNS_RegisterService() just reset sr->Extras to NULL.
6947 // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
6948 // through the old list of extra records, and re-add them to our freshly created service registration
6949 while (!err
&& extras
)
6951 ExtraResourceRecord
*e
= extras
;
6952 extras
= extras
->next
;
6953 err
= mDNS_AddRecordToService(m
, sr
, e
, e
->r
.resrec
.rdata
, e
->r
.resrec
.rroriginalttl
);
6959 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
6960 // which may change the record list and/or question list.
6961 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6962 mDNSexport mStatus
mDNS_DeregisterService(mDNS
*const m
, ServiceRecordSet
*sr
)
6964 // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
6965 if (!sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
.NotAnInteger
) return(mDNS_DeregisterNoSuchService(m
, &sr
->RR_SRV
));
6967 #ifndef UNICAST_DISABLED
6968 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6972 status
= uDNS_DeregisterService(m
, sr
);
6977 if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeUnregistered
)
6979 debugf("Service set for %##s already deregistered", sr
->RR_SRV
.resrec
.name
->c
);
6980 return(mStatus_BadReferenceErr
);
6982 else if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeDeregistering
)
6984 debugf("Service set for %##s already in the process of deregistering", sr
->RR_SRV
.resrec
.name
->c
);
6985 return(mStatus_NoError
);
6991 ExtraResourceRecord
*e
;
6995 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
6996 // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
6997 mDNS_Deregister_internal(m
, &sr
->RR_SRV
, mDNS_Dereg_repeat
);
6998 mDNS_Deregister_internal(m
, &sr
->RR_TXT
, mDNS_Dereg_repeat
);
7000 mDNS_Deregister_internal(m
, &sr
->RR_ADV
, mDNS_Dereg_normal
);
7002 // We deregister all of the extra records, but we leave the sr->Extras list intact
7003 // in case the client wants to do a RenameAndReregister and reinstate the registration
7006 mDNS_Deregister_internal(m
, &e
->r
, mDNS_Dereg_repeat
);
7010 for (i
=0; i
<sr
->NumSubTypes
; i
++)
7011 mDNS_Deregister_internal(m
, &sr
->SubTypes
[i
], mDNS_Dereg_normal
);
7013 // Be sure to deregister the PTR last!
7014 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
7015 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
7016 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
7017 // we've deregistered all our records and done any other necessary cleanup before that happens.
7018 status
= mDNS_Deregister_internal(m
, &sr
->RR_PTR
, mDNS_Dereg_normal
);
7024 // Create a registration that asserts that no such service exists with this name.
7025 // This can be useful where there is a given function is available through several protocols.
7026 // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
7027 // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
7028 // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
7029 // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
7030 mDNSexport mStatus
mDNS_RegisterNoSuchService(mDNS
*const m
, AuthRecord
*const rr
,
7031 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
7032 const domainname
*const host
,
7033 const mDNSInterfaceID InterfaceID
, mDNSRecordCallback Callback
, void *Context
)
7035 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, Callback
, Context
);
7036 if (ConstructServiceName(rr
->resrec
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
7037 rr
->resrec
.rdata
->u
.srv
.priority
= 0;
7038 rr
->resrec
.rdata
->u
.srv
.weight
= 0;
7039 rr
->resrec
.rdata
->u
.srv
.port
= zeroIPPort
;
7040 if (host
&& host
->c
[0]) AssignDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, host
);
7041 else rr
->HostTarget
= mDNStrue
;
7042 return(mDNS_Register(m
, rr
));
7045 mDNSexport mStatus
mDNS_AdvertiseDomains(mDNS
*const m
, AuthRecord
*rr
,
7046 mDNS_DomainType DomainType
, const mDNSInterfaceID InterfaceID
, char *domname
)
7048 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, mDNSNULL
, mDNSNULL
);
7049 if (!MakeDomainNameFromDNSNameString(rr
->resrec
.name
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
7050 if (!MakeDomainNameFromDNSNameString(&rr
->resrec
.rdata
->u
.name
, domname
)) return(mStatus_BadParamErr
);
7051 return(mDNS_Register(m
, rr
));
7054 // ***************************************************************************
7055 #if COMPILER_LIKES_PRAGMA_MARK
7058 #pragma mark - Startup and Shutdown
7061 mDNSlocal
void mDNS_GrowCache_internal(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
7063 if (storage
&& numrecords
)
7066 debugf("Adding cache storage for %d more records (%d bytes)", numrecords
, numrecords
*sizeof(CacheEntity
));
7067 for (i
=0; i
<numrecords
; i
++) storage
[i
].next
= &storage
[i
+1];
7068 storage
[numrecords
-1].next
= m
->rrcache_free
;
7069 m
->rrcache_free
= storage
;
7070 m
->rrcache_size
+= numrecords
;
7074 mDNSexport
void mDNS_GrowCache(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
7077 mDNS_GrowCache_internal(m
, storage
, numrecords
);
7081 mDNSexport mStatus
mDNS_Init(mDNS
*const m
, mDNS_PlatformSupport
*const p
,
7082 CacheEntity
*rrcachestorage
, mDNSu32 rrcachesize
,
7083 mDNSBool AdvertiseLocalAddresses
, mDNSCallback
*Callback
, void *Context
)
7089 if (!rrcachestorage
) rrcachesize
= 0;
7093 m
->CanReceiveUnicastOn5353
= mDNSfalse
; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
7094 m
->AdvertiseLocalAddresses
= AdvertiseLocalAddresses
;
7095 m
->mDNSPlatformStatus
= mStatus_Waiting
;
7096 m
->UnicastPort4
= zeroIPPort
;
7097 m
->UnicastPort6
= zeroIPPort
;
7098 m
->MainCallback
= Callback
;
7099 m
->MainContext
= Context
;
7100 m
->rec
.r
.resrec
.RecordType
= 0;
7102 // For debugging: To catch and report locking failures
7104 m
->mDNS_reentrancy
= 0;
7105 m
->mDNS_shutdown
= mDNSfalse
;
7106 m
->lock_rrcache
= 0;
7107 m
->lock_Questions
= 0;
7108 m
->lock_Records
= 0;
7110 // Task Scheduling variables
7111 result
= mDNSPlatformTimeInit();
7112 if (result
!= mStatus_NoError
) return(result
);
7113 m
->timenow_adjust
= (mDNSs32
)mDNSRandom(0xFFFFFFFF);
7114 timenow
= mDNS_TimeNow_NoLock(m
);
7116 m
->timenow
= 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
7117 m
->timenow_last
= timenow
;
7118 m
->NextScheduledEvent
= timenow
;
7119 m
->SuppressSending
= timenow
;
7120 m
->NextCacheCheck
= timenow
+ 0x78000000;
7121 m
->NextScheduledQuery
= timenow
+ 0x78000000;
7122 m
->NextScheduledProbe
= timenow
+ 0x78000000;
7123 m
->NextScheduledResponse
= timenow
+ 0x78000000;
7124 m
->ExpectUnicastResponse
= timenow
+ 0x78000000;
7125 m
->RandomQueryDelay
= 0;
7127 m
->SendDeregistrations
= mDNSfalse
;
7128 m
->SendImmediateAnswers
= mDNSfalse
;
7129 m
->SleepState
= mDNSfalse
;
7131 // These fields only required for mDNS Searcher...
7132 m
->Questions
= mDNSNULL
;
7133 m
->NewQuestions
= mDNSNULL
;
7134 m
->CurrentQuestion
= mDNSNULL
;
7135 m
->LocalOnlyQuestions
= mDNSNULL
;
7136 m
->NewLocalOnlyQuestions
= mDNSNULL
;
7137 m
->rrcache_size
= 0;
7138 m
->rrcache_totalused
= 0;
7139 m
->rrcache_active
= 0;
7140 m
->rrcache_report
= 10;
7141 m
->rrcache_free
= mDNSNULL
;
7143 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++) m
->rrcache_hash
[slot
] = mDNSNULL
;
7145 mDNS_GrowCache_internal(m
, rrcachestorage
, rrcachesize
);
7147 // Fields below only required for mDNS Responder...
7148 m
->hostlabel
.c
[0] = 0;
7149 m
->nicelabel
.c
[0] = 0;
7150 m
->MulticastHostname
.c
[0] = 0;
7151 m
->HIHardware
.c
[0] = 0;
7152 m
->HISoftware
.c
[0] = 0;
7153 m
->ResourceRecords
= mDNSNULL
;
7154 m
->DuplicateRecords
= mDNSNULL
;
7155 m
->NewLocalRecords
= mDNSNULL
;
7156 m
->CurrentRecord
= mDNSNULL
;
7157 m
->HostInterfaces
= mDNSNULL
;
7158 m
->ProbeFailTime
= 0;
7159 m
->NumFailedProbes
= 0;
7160 m
->SuppressProbes
= 0;
7162 #ifndef UNICAST_DISABLED
7164 m
->SuppressStdPort53Queries
= 0;
7166 result
= mDNSPlatformInit(m
);
7171 mDNSexport
void mDNSCoreInitComplete(mDNS
*const m
, mStatus result
)
7173 m
->mDNSPlatformStatus
= result
;
7174 if (m
->MainCallback
)
7177 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
7178 m
->MainCallback(m
, mStatus_NoError
);
7179 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
7184 mDNSexport
void mDNS_Close(mDNS
*const m
)
7186 mDNSu32 rrcache_active
= 0;
7187 mDNSu32 rrcache_totalused
= 0;
7189 NetworkInterfaceInfo
*intf
;
7192 m
->mDNS_shutdown
= mDNStrue
;
7194 #ifndef UNICAST_DISABLED
7197 rrcache_totalused
= m
->rrcache_totalused
;
7198 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
7200 while(m
->rrcache_hash
[slot
])
7202 CacheGroup
*cg
= m
->rrcache_hash
[slot
];
7205 CacheRecord
*rr
= cg
->members
;
7206 cg
->members
= cg
->members
->next
;
7207 if (rr
->CRActiveQuestion
) rrcache_active
++;
7208 ReleaseCacheRecord(m
, rr
);
7210 cg
->rrcache_tail
= &cg
->members
;
7211 ReleaseCacheGroup(m
, &m
->rrcache_hash
[slot
]);
7214 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused
, rrcache_active
);
7215 if (rrcache_active
!= m
->rrcache_active
)
7216 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active
, m
->rrcache_active
);
7218 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
7219 if (intf
->Advertise
)
7220 DeadvertiseInterface(m
, intf
);
7222 // Make sure there are nothing but deregistering records remaining in the list
7223 if (m
->CurrentRecord
) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
7224 m
->CurrentRecord
= m
->ResourceRecords
;
7225 while (m
->CurrentRecord
)
7227 AuthRecord
*rr
= m
->CurrentRecord
;
7228 if (rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
7230 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr
->resrec
.RecordType
, rr
->resrec
.name
->c
);
7231 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
7234 m
->CurrentRecord
= rr
->next
;
7237 if (m
->ResourceRecords
) debugf("mDNS_Close: Sending final packets for deregistering records");
7238 else debugf("mDNS_Close: No deregistering records remain");
7240 // If any deregistering records remain, send their deregistration announcements before we exit
7241 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) DiscardDeregistrations(m
);
7242 else if (m
->ResourceRecords
) SendResponses(m
);
7243 if (m
->ResourceRecords
) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m
, m
->ResourceRecords
));
7246 debugf("mDNS_Close: mDNSPlatformClose");
7247 mDNSPlatformClose(m
);
7248 debugf("mDNS_Close: done");