1 /* -*- Mode: C; tab-width: 4 -*-
3 * Copyright (c) 2002-2003 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.526 2005/10/20 00:10:33 cheshire
49 <rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
51 Revision 1.525 2005/09/24 00:47:17 cheshire
54 Revision 1.524 2005/09/16 21:06:49 cheshire
55 Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
57 Revision 1.523 2005/03/21 00:33:51 shersche
58 <rdar://problem/4021486> Fix build warnings on Win32 platform
60 Revision 1.522 2005/03/04 21:48:12 cheshire
61 <rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity
63 Revision 1.521 2005/02/25 04:21:00 cheshire
64 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
66 Revision 1.520 2005/02/16 01:14:11 cheshire
67 Convert RR Cache LogOperation() calls to debugf()
69 Revision 1.519 2005/02/15 01:57:20 cheshire
70 When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero
72 Revision 1.518 2005/02/10 22:35:17 cheshire
73 <rdar://problem/3727944> Update name
75 Revision 1.517 2005/02/03 00:21:21 cheshire
76 Update comments about BIND named and zero-length TXT records
78 Revision 1.516 2005/01/28 06:06:32 cheshire
81 Revision 1.515 2005/01/27 00:21:49 cheshire
82 <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
84 Revision 1.514 2005/01/21 01:33:45 cheshire
85 <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
87 Revision 1.513 2005/01/21 00:07:54 cheshire
88 <rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict
90 Revision 1.512 2005/01/20 00:37:45 cheshire
91 <rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse
92 Take care not to recycle records while they are on the CacheFlushRecords list
94 Revision 1.511 2005/01/19 22:48:53 cheshire
95 <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
97 Revision 1.510 2005/01/19 03:12:45 cheshire
98 Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
100 Revision 1.509 2005/01/19 03:08:49 cheshire
101 <rdar://problem/3961051> CPU Spin in mDNSResponder
102 Log messages to help catch and report CPU spins
104 Revision 1.508 2005/01/18 18:56:32 cheshire
105 <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
107 Revision 1.507 2005/01/18 01:12:07 cheshire
108 <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
110 Revision 1.506 2005/01/17 23:28:53 cheshire
113 Revision 1.505 2005/01/11 02:02:56 shersche
114 Move variable declaration to the beginning of statement block
116 Revision 1.504 2004/12/20 20:24:35 cheshire
117 <rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer
119 Revision 1.503 2004/12/20 18:41:47 cheshire
120 <rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space
122 Revision 1.502 2004/12/20 18:04:08 cheshire
123 <rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache
125 Revision 1.501 2004/12/19 23:50:18 cheshire
126 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
127 Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services
129 Revision 1.500 2004/12/18 03:13:46 cheshire
130 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
132 Revision 1.499 2004/12/17 23:37:45 cheshire
133 <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
134 (and other repetitive configuration changes)
136 Revision 1.498 2004/12/17 05:25:46 cheshire
137 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
139 Revision 1.497 2004/12/17 03:20:58 cheshire
140 <rdar://problem/3925168> Don't send unicast replies we know will be ignored
142 Revision 1.496 2004/12/16 22:18:26 cheshire
143 Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
145 Revision 1.495 2004/12/16 21:27:37 ksekar
146 Fixed build failures when compiled with verbose debugging messages
148 Revision 1.494 2004/12/16 20:46:56 cheshire
149 Fix compiler warnings
151 Revision 1.493 2004/12/16 20:13:00 cheshire
152 <rdar://problem/3324626> Cache memory management improvements
154 Revision 1.492 2004/12/16 08:03:24 shersche
155 Fix compilation error when UNICAST_DISABLED is set
157 Revision 1.491 2004/12/11 01:52:11 cheshire
158 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
160 Revision 1.490 2004/12/10 20:06:25 cheshire
161 <rdar://problem/3915074> Reduce egregious stack space usage
162 Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes
164 Revision 1.489 2004/12/10 20:03:43 cheshire
165 <rdar://problem/3915074> Reduce egregious stack space usage
166 Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes
168 Revision 1.488 2004/12/10 19:50:41 cheshire
169 <rdar://problem/3915074> Reduce egregious stack space usage
170 Reduced SendResponses() stack frame from 9K to 176 bytes
172 Revision 1.487 2004/12/10 19:39:13 cheshire
173 <rdar://problem/3915074> Reduce egregious stack space usage
174 Reduced SendQueries() stack frame from 18K to 112 bytes
176 Revision 1.486 2004/12/10 14:16:17 cheshire
177 <rdar://problem/3889788> Relax update rate limiting
178 We now allow an average rate of ten updates per minute.
179 Updates in excess of that are rate limited, but more gently than before.
181 Revision 1.485 2004/12/10 02:09:24 cheshire
182 <rdar://problem/3898376> Modify default TTLs
184 Revision 1.484 2004/12/09 03:15:40 ksekar
185 <rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains
187 Revision 1.483 2004/12/07 23:00:14 ksekar
188 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration:
189 Call RecordProbeFailure even if there is no record callback
191 Revision 1.482 2004/12/07 22:49:06 cheshire
192 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
194 Revision 1.481 2004/12/07 21:26:04 ksekar
195 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
197 Revision 1.480 2004/12/07 20:42:33 cheshire
198 Add explicit context parameter to mDNS_RemoveRecordFromService()
200 Revision 1.479 2004/12/07 17:50:49 ksekar
201 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
203 Revision 1.478 2004/12/06 21:15:22 ksekar
204 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
206 Revision 1.477 2004/12/04 02:12:45 cheshire
207 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
209 Revision 1.476 2004/11/29 23:34:31 cheshire
210 On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
211 is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
212 only nudges the time value to 1 if the interval calculation happens to result in the value zero.
214 Revision 1.475 2004/11/29 23:13:31 cheshire
215 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
216 Additional check: Make sure we don't unnecessarily send packets containing only additionals.
217 (This could occur with multi-packet KA lists, if the answer and additionals were marked
218 by the query packet, and then the answer were later suppressed in a subsequent KA packet.)
220 Revision 1.474 2004/11/29 17:18:12 cheshire
221 Remove "Unknown DNS packet type" message for update responses
223 Revision 1.473 2004/11/25 01:57:52 cheshire
224 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
226 Revision 1.472 2004/11/25 01:28:09 cheshire
227 <rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too)
229 Revision 1.471 2004/11/25 01:10:13 cheshire
230 Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
232 Revision 1.470 2004/11/24 21:54:44 cheshire
233 <rdar://problem/3894475> mDNSCore not receiving unicast responses properly
235 Revision 1.469 2004/11/24 04:50:39 cheshire
238 Revision 1.468 2004/11/24 01:47:07 cheshire
239 <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
241 Revision 1.467 2004/11/24 01:41:28 cheshire
242 Rename CompleteProbing() to AcknowledgeRecord()
244 Revision 1.466 2004/11/23 21:08:07 ksekar
245 Don't use ID to demux multicast/unicast now that unicast uses random IDs
247 Revision 1.465 2004/11/15 20:09:21 ksekar
248 <rdar://problem/3719050> Wide Area support for Add/Remove record
250 Revision 1.464 2004/11/03 01:44:36 cheshire
251 Update debugging messages
253 Revision 1.463 2004/10/29 02:38:48 cheshire
254 Fix Windows compile errors
256 Revision 1.462 2004/10/28 19:21:07 cheshire
257 Guard against registering interface with zero InterfaceID
259 Revision 1.461 2004/10/28 19:02:16 cheshire
260 Remove \n from LogMsg() call
262 Revision 1.460 2004/10/28 03:24:40 cheshire
263 Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
265 Revision 1.459 2004/10/26 22:34:37 cheshire
266 <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
268 Revision 1.458 2004/10/26 20:45:28 cheshire
269 Show mask in "invalid mask" message
271 Revision 1.457 2004/10/26 06:28:36 cheshire
272 Now that we don't check IP TTL any more, remove associated log message
274 Revision 1.456 2004/10/26 06:21:42 cheshire
275 Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address)
277 Revision 1.455 2004/10/26 06:11:40 cheshire
278 Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
280 Revision 1.454 2004/10/23 01:16:00 cheshire
281 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
283 Revision 1.453 2004/10/22 20:52:06 ksekar
284 <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
286 Revision 1.452 2004/10/20 01:50:40 cheshire
287 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
288 Implemented ForceMCast mode for AuthRecords as well as for Questions
290 Revision 1.451 2004/10/19 21:33:15 cheshire
291 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
292 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
293 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
295 Revision 1.450 2004/10/19 17:42:59 ksekar
296 Fixed compiler warnings for non-debug builds.
298 Revision 1.449 2004/10/18 22:57:07 cheshire
299 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
301 Revision 1.448 2004/10/16 00:16:59 cheshire
302 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
304 Revision 1.447 2004/10/15 00:51:21 cheshire
305 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
307 Revision 1.446 2004/10/14 00:43:34 cheshire
308 <rdar://problem/3815984> Services continue to announce SRV and HINFO
310 Revision 1.445 2004/10/12 21:07:09 cheshire
311 Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
313 Revision 1.444 2004/10/11 17:54:16 ksekar
314 Changed hashtable pointer output from debugf to verbosedebugf.
316 Revision 1.443 2004/10/10 07:05:45 cheshire
317 For consistency, use symbol "localdomain" instead of literal string
319 Revision 1.442 2004/10/08 20:25:10 cheshire
320 Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time
322 Revision 1.441 2004/10/08 03:25:01 ksekar
323 <rdar://problem/3831716> domain enumeration should use LLQs
325 Revision 1.440 2004/10/06 01:44:19 cheshire
326 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
328 Revision 1.439 2004/10/03 23:14:11 cheshire
329 Add "mDNSEthAddr" type and "zeroEthAddr" constant
331 Revision 1.438 2004/09/29 23:07:04 cheshire
332 Patch from Pavel Repin to fix compile error on Windows
334 Revision 1.437 2004/09/28 02:23:50 cheshire
335 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
336 Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot
337 For records with the cache flush bit set, defer the decision until the end of the packet
339 Revision 1.436 2004/09/28 01:27:04 cheshire
340 Update incorrect log message
342 Revision 1.435 2004/09/25 02:41:39 cheshire
343 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
345 Revision 1.434 2004/09/25 02:32:06 cheshire
348 Revision 1.433 2004/09/25 02:24:27 cheshire
349 Removed unused rr->UseCount
351 Revision 1.432 2004/09/24 21:35:17 cheshire
352 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
353 TargetPort and TargetQID are allowed to be undefined if no question->Target is set
355 Revision 1.431 2004/09/24 21:33:12 cheshire
358 Revision 1.430 2004/09/24 02:15:49 cheshire
359 <rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces
361 Revision 1.429 2004/09/24 00:20:21 cheshire
362 <rdar://problem/3483349> Any rrtype is a conflict for unique records
364 Revision 1.428 2004/09/24 00:12:25 cheshire
365 Get rid of unused RRUniqueOrKnownUnique(RR)
367 Revision 1.427 2004/09/23 20:44:11 cheshire
368 <rdar://problem/3813148> Reduce timeout before expiring records on failure
370 Revision 1.426 2004/09/23 20:21:07 cheshire
371 <rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
372 Associate a unique sequence number with each received packet, and only increment the count of recent answer
373 packets if the packet sequence number for this answer record is not one we've already seen and counted.
375 Revision 1.425 2004/09/23 20:14:38 cheshire
376 Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
378 Revision 1.424 2004/09/23 00:58:36 cheshire
379 <rdar://problem/3781269> Rate limiting interferes with updating TXT records
381 Revision 1.423 2004/09/23 00:50:53 cheshire
382 <rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep
384 Revision 1.422 2004/09/22 02:34:46 cheshire
385 Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
387 Revision 1.421 2004/09/21 23:29:49 cheshire
388 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
390 Revision 1.420 2004/09/21 23:01:42 cheshire
391 Update debugf messages
393 Revision 1.419 2004/09/21 19:51:14 cheshire
394 Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
396 Revision 1.418 2004/09/21 18:40:17 cheshire
397 <rdar://problem/3376752> Adjust default record TTLs
399 Revision 1.417 2004/09/21 17:32:16 cheshire
400 <rdar://problem/3809484> Rate limiting imposed too soon
402 Revision 1.416 2004/09/20 23:52:01 cheshire
403 CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
405 Revision 1.415 2004/09/18 01:14:09 cheshire
406 <rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces
408 Revision 1.414 2004/09/18 01:06:48 cheshire
411 Revision 1.413 2004/09/17 01:08:48 cheshire
412 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
413 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
414 declared in that file are ONLY appropriate to single-address-space embedded applications.
415 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
417 Revision 1.412 2004/09/17 00:46:33 cheshire
418 mDNS_TimeNow should take const mDNS parameter
420 Revision 1.411 2004/09/17 00:31:51 cheshire
421 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
423 Revision 1.410 2004/09/17 00:19:10 cheshire
424 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
426 Revision 1.409 2004/09/16 21:59:15 cheshire
427 For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
429 Revision 1.408 2004/09/16 21:36:36 cheshire
430 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
431 Changes to add necessary locking calls around unicast DNS operations
433 Revision 1.407 2004/09/16 02:29:39 cheshire
434 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
435 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
437 Revision 1.406 2004/09/16 01:58:14 cheshire
438 Fix compiler warnings
440 Revision 1.405 2004/09/16 00:24:48 cheshire
441 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
443 Revision 1.404 2004/09/15 21:44:11 cheshire
444 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
445 Show time value in log to help diagnose errors
447 Revision 1.403 2004/09/15 00:46:32 ksekar
448 Changed debugf to verbosedebugf in CheckCacheExpiration
450 Revision 1.402 2004/09/14 23:59:55 cheshire
451 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
453 Revision 1.401 2004/09/14 23:27:46 cheshire
456 Revision 1.400 2004/09/02 03:48:47 cheshire
457 <rdar://problem/3709039> Disable targeted unicast query support by default
458 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
459 2. New field AllowRemoteQuery in AuthRecord structure
460 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
461 4. mDNS.c only answers remote queries if AllowRemoteQuery is set
463 Revision 1.399 2004/09/02 01:39:40 cheshire
464 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
466 Revision 1.398 2004/09/01 03:59:29 ksekar
467 <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
469 Revision 1.397 2004/08/25 22:04:25 rpantos
470 Fix the standard Windows compile error.
472 Revision 1.396 2004/08/25 00:37:27 ksekar
473 <rdar://problem/3774635>: Cleanup DynDNS hostname registration code
475 Revision 1.395 2004/08/18 17:21:18 ksekar
476 Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
478 Revision 1.394 2004/08/14 03:22:41 cheshire
479 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
480 Add GetUserSpecifiedDDNSName() routine
481 Convert ServiceRegDomain to domainname instead of C string
482 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
484 Revision 1.393 2004/08/13 23:42:52 cheshire
485 Removed unused "zeroDomainNamePtr"
487 Revision 1.392 2004/08/13 23:37:02 cheshire
488 Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
489 "uDNS_info.UnicastHostname" for clarity
491 Revision 1.391 2004/08/13 23:25:00 cheshire
492 Now that we do both uDNS and mDNS, global replace "m->hostname" with
493 "m->MulticastHostname" for clarity
495 Revision 1.390 2004/08/11 02:17:01 cheshire
496 <rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record
498 Revision 1.389 2004/08/10 23:19:14 ksekar
499 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
500 Moved routines/constants to allow extern access for garbage collection daemon
502 Revision 1.388 2004/07/30 17:40:06 ksekar
503 <rdar://problem/3739115>: TXT Record updates not available for wide-area services
505 Revision 1.387 2004/07/26 22:49:30 ksekar
506 <rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
508 Revision 1.386 2004/07/13 21:24:24 rpantos
509 Fix for <rdar://problem/3701120>.
511 Revision 1.385 2004/06/18 19:09:59 cheshire
512 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
514 Revision 1.384 2004/06/15 04:31:23 cheshire
515 Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
517 Revision 1.383 2004/06/11 00:04:59 cheshire
518 <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
520 Revision 1.382 2004/06/08 04:59:40 cheshire
521 Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
523 Revision 1.381 2004/06/05 00:57:30 cheshire
524 Remove incorrect LogMsg()
526 Revision 1.380 2004/06/05 00:04:26 cheshire
527 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
529 Revision 1.379 2004/05/28 23:42:36 ksekar
530 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
532 Revision 1.378 2004/05/25 17:25:25 cheshire
533 Remove extraneous blank lines and white space
535 Revision 1.377 2004/05/18 23:51:25 cheshire
536 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
538 Revision 1.376 2004/05/05 18:30:44 ksekar
539 Restored surpressed Cache Tail debug messages.
541 Revision 1.375 2004/04/26 21:36:25 cheshire
542 Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive
543 is indicated as being available on that interface
545 Revision 1.374 2004/04/21 02:53:26 cheshire
546 Typo in debugf statement
548 Revision 1.373 2004/04/21 02:49:11 cheshire
549 To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
551 Revision 1.372 2004/04/21 02:38:51 cheshire
554 Revision 1.371 2004/04/14 23:09:28 ksekar
555 Support for TSIG signed dynamic updates.
557 Revision 1.370 2004/04/09 17:40:26 cheshire
558 Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
560 Revision 1.369 2004/04/09 16:34:00 cheshire
561 Debugging code for later; currently unused
563 Revision 1.368 2004/04/02 19:19:48 cheshire
564 Add code to do optional logging of multi-packet KA list time intervals
566 Revision 1.367 2004/03/20 03:16:10 cheshire
567 Minor refinement to "Excessive update rate" message
569 Revision 1.366 2004/03/20 03:12:57 cheshire
570 <rdar://problem/3587619>: UpdateCredits not granted promptly enough
572 Revision 1.365 2004/03/19 23:51:22 cheshire
573 Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
575 Revision 1.364 2004/03/13 01:57:33 ksekar
576 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
578 Revision 1.363 2004/03/12 21:00:51 cheshire
579 Also show port numbers when logging "apparent spoof mDNS Response" messages
581 Revision 1.362 2004/03/12 08:58:18 cheshire
582 Guard against empty TXT records
584 Revision 1.361 2004/03/09 03:00:46 cheshire
585 <rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good.
587 Revision 1.360 2004/03/08 02:52:41 cheshire
588 Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages
590 Revision 1.359 2004/03/02 03:21:56 cheshire
591 <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
593 Revision 1.358 2004/02/20 08:18:34 cheshire
594 <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
596 Revision 1.357 2004/02/18 01:47:41 cheshire
597 <rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms
599 Revision 1.356 2004/02/06 23:04:19 ksekar
600 Basic Dynamic Update support via mDNS_Register (dissabled via
601 UNICAST_REGISTRATION #define)
603 Revision 1.355 2004/02/05 09:32:33 cheshire
604 Fix from Bob Bradley: When using the "%.*s" string form,
605 guard against truncating in the middle of a multi-byte UTF-8 character.
607 Revision 1.354 2004/02/05 09:30:22 cheshire
610 Revision 1.353 2004/01/28 03:41:00 cheshire
611 <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
613 Revision 1.352 2004/01/28 02:30:07 ksekar
614 Added default Search Domains to unicast browsing, controlled via
615 Networking sharing prefs pane. Stopped sending unicast messages on
616 every interface. Fixed unicast resolving via mach-port API.
618 Revision 1.351 2004/01/27 20:15:22 cheshire
619 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
621 Revision 1.350 2004/01/24 23:38:16 cheshire
622 Use mDNSVal16() instead of shifting and ORing operations
624 Revision 1.349 2004/01/23 23:23:14 ksekar
625 Added TCP support for truncated unicast messages.
627 Revision 1.348 2004/01/22 03:54:11 cheshire
628 Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
629 which means "do this query via multicast, even if it's apparently a unicast domain"
631 Revision 1.347 2004/01/22 03:50:49 cheshire
632 If the client has specified an explicit InterfaceID, then do query by multicast, not unicast
634 Revision 1.346 2004/01/22 03:48:41 cheshire
635 Make sure uDNS client doesn't accidentally use query ID zero
637 Revision 1.345 2004/01/22 03:43:08 cheshire
638 Export constants like mDNSInterface_LocalOnly so that the client layers can use them
640 Revision 1.344 2004/01/21 21:53:18 cheshire
641 <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
643 Revision 1.343 2003/12/23 00:07:47 cheshire
644 Make port number in debug message be five-character field, left justified
646 Revision 1.342 2003/12/20 01:34:28 cheshire
647 <rdar://problem/3515876>: Error putting additional records into packets
648 Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
649 after every record, not once at the end.
651 Revision 1.341 2003/12/18 22:56:12 cheshire
652 <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
654 Revision 1.340 2003/12/16 02:31:37 cheshire
655 Minor update to comments
657 Revision 1.339 2003/12/13 05:50:33 bradley
658 Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform
659 layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to
660 fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf.
662 Revision 1.338 2003/12/13 03:05:27 ksekar
663 <rdar://problem/3192548>: DynDNS: Unicast query of service records
665 Revision 1.337 2003/12/01 21:46:05 cheshire
666 mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
668 Revision 1.336 2003/12/01 21:26:19 cheshire
669 Guard against zero-length sbuffer in mDNS_vsnprintf()
671 Revision 1.335 2003/12/01 20:27:48 cheshire
672 Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors
674 Revision 1.334 2003/11/20 22:59:53 cheshire
675 Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
676 Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
678 Revision 1.333 2003/11/20 20:49:53 cheshire
679 Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
681 Revision 1.332 2003/11/20 05:47:37 cheshire
682 <rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query
683 Now that we only include answers in the known answer list if they are less than
684 halfway to expiry, the check to also see if we have another query scheduled
685 before the record expires is no longer necessary (and in fact, not correct).
687 Revision 1.331 2003/11/19 22:31:48 cheshire
688 When automatically adding A records to SRVs, add them as additionals, not answers
690 Revision 1.330 2003/11/19 22:28:50 cheshire
691 Increment/Decrement mDNS_reentrancy around calls to m->MainCallback()
692 to allow client to make mDNS calls (specifically the call to mDNS_GrowCache())
694 Revision 1.329 2003/11/19 22:19:24 cheshire
695 Show log message when ignoring packets with bad TTL.
696 This is to help diagnose problems on Linux versions that may not report the TTL reliably.
698 Revision 1.328 2003/11/19 22:06:38 cheshire
699 Show log messages when a service or hostname is renamed
701 Revision 1.327 2003/11/19 22:03:44 cheshire
702 Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
704 Revision 1.326 2003/11/17 22:27:02 cheshire
705 Another fix from ramaprasad.kr@hp.com: Improve reply delay computation
706 on platforms that have native clock rates below fifty ticks per second.
708 Revision 1.325 2003/11/17 20:41:44 cheshire
709 Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
711 Revision 1.324 2003/11/17 20:36:32 cheshire
712 Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and
713 DeadvertiseInterface() -- they're internal private routines, not API routines.
715 Revision 1.323 2003/11/14 20:59:08 cheshire
716 Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
717 Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
719 Revision 1.322 2003/11/14 19:47:52 cheshire
720 Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
722 Revision 1.321 2003/11/14 19:18:34 cheshire
723 Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
725 Revision 1.320 2003/11/13 06:45:04 cheshire
726 Fix compiler warning on certain compilers
728 Revision 1.319 2003/11/13 00:47:40 cheshire
729 <rdar://problem/3437556> We should delay AAAA record query if A record already in cache.
731 Revision 1.318 2003/11/13 00:33:26 cheshire
732 Change macro "RRIsAddressType" to "RRTypeIsAddressType"
734 Revision 1.317 2003/11/13 00:10:49 cheshire
735 <rdar://problem/3436412>: Verify that rr data is different before updating.
737 Revision 1.316 2003/11/08 23:37:54 cheshire
738 Give explicit zero initializers to blank static structure, required by certain compilers.
739 (Thanks to ramaprasad.kr@hp.com for reporting this.)
741 Revision 1.315 2003/11/07 03:32:56 cheshire
742 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
743 This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
744 purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
745 to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
746 can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
748 Revision 1.314 2003/11/07 03:19:49 cheshire
749 Minor variable renaming for clarity
751 Revision 1.313 2003/11/07 03:14:49 cheshire
752 Previous checkin proved to be overly simplistic; reversing
754 Revision 1.312 2003/11/03 23:45:15 cheshire
755 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
756 Build cache lists in FIFO order, not customary C LIFO order
757 (Append new elements to tail of cache list, instead of prepending at the head.)
759 Revision 1.311 2003/10/09 18:00:11 cheshire
760 Another compiler warning fix.
762 Revision 1.310 2003/10/07 20:27:05 cheshire
763 Patch from Bob Bradley, to fix warning and compile error on Windows
765 Revision 1.309 2003/09/26 01:06:36 cheshire
766 <rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too
767 Made new routine HaveSentEntireRRSet() to check if flag should be set
769 Revision 1.308 2003/09/23 01:05:01 cheshire
770 Minor changes to comments and debugf() message
772 Revision 1.307 2003/09/09 20:13:30 cheshire
773 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
774 Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
775 rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
777 Revision 1.306 2003/09/09 03:00:03 cheshire
778 <rdar://problem/3413099> Services take a long time to disappear when switching networks.
779 Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
781 Revision 1.305 2003/09/09 02:49:31 cheshire
782 <rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
784 Revision 1.304 2003/09/09 02:41:19 cheshire
785 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
787 Revision 1.303 2003/09/05 19:55:02 cheshire
788 <rdar://problem/3409533> Include address records when announcing SRV records
790 Revision 1.302 2003/09/05 00:01:36 cheshire
791 <rdar://problem/3407549> Don't accelerate queries that have large KA lists
793 Revision 1.301 2003/09/04 22:51:13 cheshire
794 <rdar://problem/3398213> Group probes and goodbyes better
796 Revision 1.300 2003/09/03 02:40:37 cheshire
797 <rdar://problem/3404842> mDNSResponder complains about '_'s
798 Underscores are not supposed to be legal in standard DNS names, but IANA appears
799 to have allowed them in previous service name registrations, so we should too.
801 Revision 1.299 2003/09/03 02:33:09 cheshire
802 <rdar://problem/3404795> CacheRecordRmv ERROR
803 Don't update m->NewQuestions until *after* CheckCacheExpiration();
805 Revision 1.298 2003/09/03 01:47:01 cheshire
806 <rdar://problem/3319418> Services always in a state of flux
807 Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds
809 Revision 1.297 2003/08/29 19:44:15 cheshire
810 <rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
811 1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries
812 that already have at least one unique answer in the cache
813 2. For these queries, go straight to QM, skipping QU
815 Revision 1.296 2003/08/29 19:08:21 cheshire
816 <rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
817 Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
819 Revision 1.295 2003/08/28 01:10:59 cheshire
820 <rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
822 Revision 1.294 2003/08/27 02:30:22 cheshire
823 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
824 One more change: "query->GotTXT" is now a straightforward bi-state boolean again
826 Revision 1.293 2003/08/27 02:25:31 cheshire
827 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
829 Revision 1.292 2003/08/21 19:27:36 cheshire
830 <rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
832 Revision 1.291 2003/08/21 18:57:44 cheshire
833 <rdar://problem/3387140> Synchronized queries on the network
835 Revision 1.290 2003/08/21 02:25:23 cheshire
836 Minor changes to comments and debugf() messages
838 Revision 1.289 2003/08/21 02:21:50 cheshire
839 <rdar://problem/3386473> Efficiency: Reduce repeated queries
841 Revision 1.288 2003/08/20 23:39:30 cheshire
842 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
844 Revision 1.287 2003/08/20 20:47:18 cheshire
847 Revision 1.286 2003/08/20 02:18:51 cheshire
848 <rdar://problem/3344098> Cleanup: Review syslog messages
850 Revision 1.285 2003/08/20 01:59:06 cheshire
851 <rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
852 Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
854 Revision 1.284 2003/08/19 22:20:00 cheshire
855 <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
856 More minor refinements
858 Revision 1.283 2003/08/19 22:16:27 cheshire
859 Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
861 Revision 1.282 2003/08/19 06:48:25 cheshire
862 <rdar://problem/3376552> Guard against excessive record updates
863 Each record starts with 10 UpdateCredits.
864 Every update consumes one UpdateCredit.
865 UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
866 As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
867 When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
869 Revision 1.281 2003/08/19 04:49:28 cheshire
870 <rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right
871 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.
872 2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
873 3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
875 Revision 1.280 2003/08/19 02:33:36 cheshire
878 Revision 1.279 2003/08/19 02:31:11 cheshire
879 <rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
880 Final expiration queries now only mark the question for sending on the particular interface
881 pertaining to the record that's expiring.
883 Revision 1.278 2003/08/18 22:53:37 cheshire
884 <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
886 Revision 1.277 2003/08/18 19:05:44 cheshire
887 <rdar://problem/3382423> UpdateRecord not working right
888 Added "newrdlength" field to hold new length of updated rdata
890 Revision 1.276 2003/08/16 03:39:00 cheshire
891 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
893 Revision 1.275 2003/08/16 02:51:27 cheshire
894 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
895 Don't try to compute namehash etc, until *after* validating the name
897 Revision 1.274 2003/08/16 01:12:40 cheshire
898 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
899 Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
900 simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
901 and in the process of copying it, the C compiler may run off the end of the rdata object into
902 unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
903 call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
905 Revision 1.273 2003/08/15 20:16:02 cheshire
906 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
907 We want to avoid touching the rdata pages, so we don't page them in.
908 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
909 Moved this from the RData to the ResourceRecord object.
910 2. To avoid unnecessarily touching the rdata just to compare it,
911 compute a hash of the rdata and store the hash in the ResourceRecord object.
913 Revision 1.272 2003/08/14 19:29:04 cheshire
914 <rdar://problem/3378473> Include cache records in SIGINFO output
915 Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
917 Revision 1.271 2003/08/14 02:17:05 cheshire
918 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
920 Revision 1.270 2003/08/13 17:07:28 ksekar
921 <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
922 Added check to result of mDNS_Register() before linking extra record into list.
924 Revision 1.269 2003/08/12 19:56:23 cheshire
927 Revision 1.268 2003/08/12 15:01:10 cheshire
930 Revision 1.267 2003/08/12 14:59:27 cheshire
931 <rdar://problem/3374490> Rate-limiting blocks some legitimate responses
932 When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
933 whether to suppress the response, also check LastMCInterface to see if it matches.
935 Revision 1.266 2003/08/12 12:47:16 cheshire
936 In mDNSCoreMachineSleep debugf message, display value of m->timenow
938 Revision 1.265 2003/08/11 20:04:28 cheshire
939 <rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
941 Revision 1.264 2003/08/09 00:55:02 cheshire
942 <rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
943 Don't scan the whole cache after every packet.
945 Revision 1.263 2003/08/09 00:35:29 cheshire
946 Moved AnswerNewQuestion() later in the file, in preparation for next checkin
948 Revision 1.262 2003/08/08 19:50:33 cheshire
949 <rdar://problem/3370332> Remove "Cache size now xxx" messages
951 Revision 1.261 2003/08/08 19:18:45 cheshire
952 <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
954 Revision 1.260 2003/08/08 18:55:48 cheshire
955 <rdar://problem/3370365> Guard against time going backwards
957 Revision 1.259 2003/08/08 18:36:04 cheshire
958 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
960 Revision 1.258 2003/08/08 16:22:05 cheshire
961 <rdar://problem/3335473> Need to check validity of TXT (and other) records
962 Remove unneeded LogMsg
964 Revision 1.257 2003/08/07 01:41:08 cheshire
965 <rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
967 Revision 1.256 2003/08/06 23:25:51 cheshire
968 <rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four
970 Revision 1.255 2003/08/06 23:22:50 cheshire
971 Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
973 Revision 1.254 2003/08/06 21:33:39 cheshire
974 Fix compiler warnings on PocketPC 2003 (Windows CE)
976 Revision 1.253 2003/08/06 20:43:57 cheshire
977 <rdar://problem/3335473> Need to check validity of TXT (and other) records
978 Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
980 Revision 1.252 2003/08/06 20:35:47 cheshire
981 Enhance debugging routine GetRRDisplayString() so it can also be used to display
982 other RDataBody objects, not just the one currently attached the given ResourceRecord
984 Revision 1.251 2003/08/06 19:07:34 cheshire
985 <rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
986 Was checking LastAPTime instead of LastMCTime
988 Revision 1.250 2003/08/06 19:01:55 cheshire
991 Revision 1.249 2003/08/06 00:13:28 cheshire
992 Tidy up debugf messages
994 Revision 1.248 2003/08/05 22:20:15 cheshire
995 <rdar://problem/3330324> Need to check IP TTL on responses
997 Revision 1.247 2003/08/05 00:56:39 cheshire
998 <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
1000 Revision 1.246 2003/08/04 19:20:49 cheshire
1001 Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
1003 Revision 1.245 2003/08/02 01:56:29 cheshire
1004 For debugging: log message if we ever get more than one question in a truncated packet
1006 Revision 1.244 2003/08/01 23:55:32 cheshire
1007 Fix for compiler warnings on Windows, submitted by Bob Bradley
1009 Revision 1.243 2003/07/25 02:26:09 cheshire
1010 Typo: FIxed missing semicolon
1012 Revision 1.242 2003/07/25 01:18:41 cheshire
1013 Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
1015 Revision 1.241 2003/07/23 21:03:42 cheshire
1016 Only show "Found record..." debugf message in verbose mode
1018 Revision 1.240 2003/07/23 21:01:11 cheshire
1019 <rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
1020 After sending a packet, suppress further sending for the next 100ms.
1022 Revision 1.239 2003/07/22 01:30:05 cheshire
1023 <rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once
1025 Revision 1.238 2003/07/22 00:10:20 cheshire
1026 <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
1028 Revision 1.237 2003/07/19 03:23:13 cheshire
1029 <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
1031 Revision 1.236 2003/07/19 03:04:55 cheshire
1032 Fix warnings; some debugf message improvements
1034 Revision 1.235 2003/07/19 00:03:32 cheshire
1035 <rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received
1036 ScheduleNextTask is quite an expensive operation.
1037 We don't need to do all that work after receiving a no-op packet that didn't change our state.
1039 Revision 1.234 2003/07/18 23:52:11 cheshire
1040 To improve consistency of field naming, global search-and-replace:
1041 NextProbeTime -> NextScheduledProbe
1042 NextResponseTime -> NextScheduledResponse
1044 Revision 1.233 2003/07/18 00:29:59 cheshire
1045 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
1047 Revision 1.232 2003/07/18 00:11:38 cheshire
1048 Add extra case to switch statements to handle HINFO data for Get, Put and Display
1049 (In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT)
1051 Revision 1.231 2003/07/18 00:06:37 cheshire
1052 To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->"
1054 Revision 1.230 2003/07/17 18:16:54 cheshire
1055 <rdar://problem/3319418> Services always in a state of flux
1056 In preparation for working on this, made some debugf messages a little more selective
1058 Revision 1.229 2003/07/17 17:35:04 cheshire
1059 <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
1061 Revision 1.228 2003/07/16 20:50:27 cheshire
1062 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1064 Revision 1.227 2003/07/16 05:01:36 cheshire
1065 Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
1066 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1068 Revision 1.226 2003/07/16 04:51:44 cheshire
1069 Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
1071 Revision 1.225 2003/07/16 04:46:41 cheshire
1072 Minor wording cleanup: The correct DNS term is "response", not "reply"
1074 Revision 1.224 2003/07/16 04:39:02 cheshire
1075 Textual cleanup (no change to functionality):
1076 Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
1078 Revision 1.223 2003/07/16 00:09:22 cheshire
1079 Textual cleanup (no change to functionality):
1080 Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
1081 replace with macro "TicksTTL(rr)"
1082 Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
1083 replaced with macro "RRExpireTime(rr)"
1085 Revision 1.222 2003/07/15 23:40:46 cheshire
1086 Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
1088 Revision 1.221 2003/07/15 22:17:56 cheshire
1089 <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
1091 Revision 1.220 2003/07/15 02:12:51 cheshire
1092 Slight tidy-up of debugf messages and comments
1094 Revision 1.219 2003/07/15 01:55:12 cheshire
1095 <rdar://problem/3315777> Need to implement service registration with subtypes
1097 Revision 1.218 2003/07/14 16:26:06 cheshire
1098 <rdar://problem/3324795> Duplicate query suppression not working right
1099 Refinement: Don't record DS information for a question in the first quarter second
1100 right after we send it -- in the case where a question happens to be accelerated by
1101 the maximum allowed amount, we don't want it to then be suppressed because the previous
1102 time *we* sent that question falls (just) within the valid duplicate suppression window.
1104 Revision 1.217 2003/07/13 04:43:53 cheshire
1105 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1106 Minor refinement: No need to make address query broader than the original SRV query that provoked it
1108 Revision 1.216 2003/07/13 03:13:17 cheshire
1109 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1110 If we get an identical SRV on a second interface, convert address queries to non-specific
1112 Revision 1.215 2003/07/13 02:28:00 cheshire
1113 <rdar://problem/3325166> SendResponses didn't all its responses
1114 Delete all references to RRInterfaceActive -- it's now superfluous
1116 Revision 1.214 2003/07/13 01:47:53 cheshire
1117 Fix one error and one warning in the Windows build
1119 Revision 1.213 2003/07/12 04:25:48 cheshire
1120 Fix minor signed/unsigned warnings
1122 Revision 1.212 2003/07/12 01:59:11 cheshire
1123 Minor changes to debugf messages
1125 Revision 1.211 2003/07/12 01:47:01 cheshire
1126 <rdar://problem/3324495> After name conflict, appended number should be higher than previous number
1128 Revision 1.210 2003/07/12 01:43:28 cheshire
1129 <rdar://problem/3324795> Duplicate query suppression not working right
1130 The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
1131 The code was incorrectly using the last query time plus one-half the query interval.
1132 This was only correct in the case where query acceleration was not in effect.
1134 Revision 1.209 2003/07/12 01:27:50 cheshire
1135 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1136 Fix missing "-1" in RemoveLabelSuffix()
1138 Revision 1.208 2003/07/11 01:32:38 cheshire
1139 Syntactic cleanup (no change to funcationality): Now that we only have one host name,
1140 rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
1142 Revision 1.207 2003/07/11 01:28:00 cheshire
1143 <rdar://problem/3161289> No more local.arpa
1145 Revision 1.206 2003/07/11 00:45:02 cheshire
1146 <rdar://problem/3321909> Client should get callback confirming successful host name registration
1148 Revision 1.205 2003/07/11 00:40:18 cheshire
1149 Tidy up debug message in HostNameCallback()
1151 Revision 1.204 2003/07/11 00:20:32 cheshire
1152 <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
1154 Revision 1.203 2003/07/10 23:53:41 cheshire
1155 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1157 Revision 1.202 2003/07/04 02:23:20 cheshire
1158 <rdar://problem/3311955> Responder too aggressive at flushing stale data
1159 Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
1161 Revision 1.201 2003/07/04 01:09:41 cheshire
1162 <rdar://problem/3315775> Need to implement subtype queries
1163 Modified ConstructServiceName() to allow three-part service types
1165 Revision 1.200 2003/07/03 23:55:26 cheshire
1166 Minor change to wording of syslog warning messages
1168 Revision 1.199 2003/07/03 23:51:13 cheshire
1169 <rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings
1170 Added more detailed debugging information
1172 Revision 1.198 2003/07/03 22:19:30 cheshire
1173 <rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
1174 Make exception to allow _tivo_servemedia._tcp.
1176 Revision 1.197 2003/07/02 22:33:05 cheshire
1177 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1179 When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
1180 Allow cache to grow to 512 records before considering it a potential denial-of-service attack
1182 Revision 1.196 2003/07/02 21:19:45 cheshire
1183 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
1185 Revision 1.195 2003/07/02 19:56:58 cheshire
1186 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1187 Minor refinement: m->rrcache_active was not being decremented when
1188 an active record was deleted because its TTL expired
1190 Revision 1.194 2003/07/02 18:47:40 cheshire
1191 Minor wording change to log messages
1193 Revision 1.193 2003/07/02 02:44:13 cheshire
1194 Fix warning in non-debug build
1196 Revision 1.192 2003/07/02 02:41:23 cheshire
1197 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1199 Revision 1.191 2003/07/02 02:30:51 cheshire
1200 HashSlot() returns an array index. It can't be negative; hence it should not be signed.
1202 Revision 1.190 2003/06/27 00:03:05 vlubet
1203 <rdar://problem/3304625> Merge of build failure fix for gcc 3.3
1205 Revision 1.189 2003/06/11 19:24:03 cheshire
1206 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1207 Slight refinement to previous checkin
1209 Revision 1.188 2003/06/10 20:33:28 cheshire
1210 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1212 Revision 1.187 2003/06/10 04:30:44 cheshire
1213 <rdar://problem/3286234> Need to re-probe/re-announce on configuration change
1214 Only interface-specific records were re-probing and re-announcing, not non-specific records.
1216 Revision 1.186 2003/06/10 04:24:39 cheshire
1217 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1218 Some additional refinements:
1219 Don't try to do this for unicast-response queries
1220 better tracking of Qs and KAs in multi-packet KA lists
1222 Revision 1.185 2003/06/10 03:52:49 cheshire
1223 Update comments and debug messages
1225 Revision 1.184 2003/06/10 02:26:39 cheshire
1226 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1227 Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
1229 Revision 1.183 2003/06/09 18:53:13 cheshire
1230 Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
1232 Revision 1.182 2003/06/09 18:38:42 cheshire
1233 <rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network
1234 Only issue a correction if the TTL in the proxy packet is less than half the correct value.
1236 Revision 1.181 2003/06/07 06:45:05 cheshire
1237 <rdar://problem/3283666> No need for multiple machines to all be sending the same queries
1239 Revision 1.180 2003/06/07 06:31:07 cheshire
1240 Create little four-line helper function "FindIdenticalRecordInCache()"
1242 Revision 1.179 2003/06/07 06:28:13 cheshire
1243 For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
1245 Revision 1.178 2003/06/07 06:25:12 cheshire
1246 Update some comments
1248 Revision 1.177 2003/06/07 04:50:53 cheshire
1249 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1251 Revision 1.176 2003/06/07 04:33:26 cheshire
1252 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1253 Minor change: Increment/decrement logic for q->CurrentAnswers should be in
1254 CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
1256 Revision 1.175 2003/06/07 04:11:52 cheshire
1257 Minor changes to comments and debug messages
1259 Revision 1.174 2003/06/07 01:46:38 cheshire
1260 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1262 Revision 1.173 2003/06/07 01:22:13 cheshire
1263 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1265 Revision 1.172 2003/06/07 00:59:42 cheshire
1266 <rdar://problem/3283454> Need some randomness to spread queries on the network
1268 Revision 1.171 2003/06/06 21:41:10 cheshire
1269 For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
1271 Revision 1.170 2003/06/06 21:38:55 cheshire
1272 Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
1273 already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
1275 Revision 1.169 2003/06/06 21:35:55 cheshire
1276 Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
1277 (the target is a domain name, but not necessarily a host name)
1279 Revision 1.168 2003/06/06 21:33:31 cheshire
1280 Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
1282 Revision 1.167 2003/06/06 21:30:42 cheshire
1283 <rdar://problem/3282962> Don't delay queries for shared record types
1285 Revision 1.166 2003/06/06 17:20:14 cheshire
1286 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
1287 (Global search-and-replace; no functional change to code execution.)
1289 Revision 1.165 2003/06/04 02:53:21 cheshire
1290 Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
1292 Revision 1.164 2003/06/04 01:25:33 cheshire
1293 <rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
1294 Display time interval between first and subsequent queries
1296 Revision 1.163 2003/06/03 19:58:14 cheshire
1297 <rdar://problem/3277665> mDNS_DeregisterService() fixes:
1298 When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
1299 Guard against a couple of possible mDNS_DeregisterService() race conditions.
1301 Revision 1.162 2003/06/03 19:30:39 cheshire
1302 Minor addition refinements for
1303 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1305 Revision 1.161 2003/06/03 18:29:03 cheshire
1306 Minor changes to comments and debugf() messages
1308 Revision 1.160 2003/06/03 05:02:16 cheshire
1309 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1311 Revision 1.159 2003/06/03 03:31:57 cheshire
1312 <rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine
1314 Revision 1.158 2003/06/02 22:57:09 cheshire
1315 Minor clarifying changes to comments and log messages;
1316 IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
1318 Revision 1.157 2003/05/31 00:09:49 cheshire
1319 <rdar://problem/3274862> Add ability to discover what services are on a network
1321 Revision 1.156 2003/05/30 23:56:49 cheshire
1322 <rdar://problem/3274847> Crash after error in mDNS_RegisterService()
1323 Need to set "sr->Extras = mDNSNULL" before returning
1325 Revision 1.155 2003/05/30 23:48:00 cheshire
1326 <rdar://problem/3274832> Announcements not properly grouped
1327 Due to inconsistent setting of rr->LastAPTime at different places in the
1328 code, announcements were not properly grouped into a single packet.
1329 Fixed by creating a single routine called InitializeLastAPTime().
1331 Revision 1.154 2003/05/30 23:38:14 cheshire
1332 <rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
1333 Wrote buffer[32] where it should have said buffer[64]
1335 Revision 1.153 2003/05/30 19:10:56 cheshire
1336 <rdar://problem/3274153> ConstructServiceName needs to be more restrictive
1338 Revision 1.152 2003/05/29 22:39:16 cheshire
1339 <rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
1341 Revision 1.151 2003/05/29 06:35:42 cheshire
1342 <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
1344 Revision 1.150 2003/05/29 06:25:45 cheshire
1345 <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
1347 Revision 1.149 2003/05/29 06:18:39 cheshire
1348 <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
1350 Revision 1.148 2003/05/29 06:11:34 cheshire
1351 <rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
1353 Revision 1.147 2003/05/29 06:01:18 cheshire
1354 Change some debugf() calls to LogMsg() calls to help with debugging
1356 Revision 1.146 2003/05/28 21:00:44 cheshire
1357 Re-enable "immediate answer burst" debugf message
1359 Revision 1.145 2003/05/28 20:57:44 cheshire
1360 <rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
1361 known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
1362 version of mDNSResponder, so for now we should suppress this warning message.
1364 Revision 1.144 2003/05/28 18:05:12 cheshire
1365 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1366 Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
1368 Revision 1.143 2003/05/28 04:31:29 cheshire
1369 <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
1371 Revision 1.142 2003/05/28 03:13:07 cheshire
1372 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1373 Require that the transport protocol be _udp or _tcp
1375 Revision 1.141 2003/05/28 02:19:12 cheshire
1376 <rdar://problem/3270634> Misleading messages generated by iChat
1377 Better fix: Only generate the log message for queries where the TC bit is set.
1379 Revision 1.140 2003/05/28 01:55:24 cheshire
1380 Minor change to log messages
1382 Revision 1.139 2003/05/28 01:52:51 cheshire
1383 <rdar://problem/3270634> Misleading messages generated by iChat
1385 Revision 1.138 2003/05/27 22:35:00 cheshire
1386 <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
1388 Revision 1.137 2003/05/27 20:04:33 cheshire
1389 <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
1391 Revision 1.136 2003/05/27 18:50:07 cheshire
1392 <rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
1394 Revision 1.135 2003/05/26 04:57:28 cheshire
1395 <rdar://problem/3268953> Delay queries when there are already answers in the cache
1397 Revision 1.134 2003/05/26 04:54:54 cheshire
1398 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1399 Accidentally deleted '%' case from the switch statement
1401 Revision 1.133 2003/05/26 03:21:27 cheshire
1402 Tidy up address structure naming:
1403 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
1404 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
1405 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
1407 Revision 1.132 2003/05/26 03:01:26 cheshire
1408 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1410 Revision 1.131 2003/05/26 00:42:05 cheshire
1411 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
1413 Revision 1.130 2003/05/24 16:39:48 cheshire
1414 <rdar://problem/3268631> SendResponses also needs to handle multihoming better
1416 Revision 1.129 2003/05/23 02:15:37 cheshire
1417 Fixed misleading use of the term "duplicate suppression" where it should have
1418 said "known answer suppression". (Duplicate answer suppression is something
1419 different, and duplicate question suppression is yet another thing, so the use
1420 of the completely vague term "duplicate suppression" was particularly bad.)
1422 Revision 1.128 2003/05/23 01:55:13 cheshire
1423 <rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
1425 Revision 1.127 2003/05/23 01:02:15 ksekar
1426 <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
1428 Revision 1.126 2003/05/22 02:29:22 cheshire
1429 <rdar://problem/2984918> SendQueries needs to handle multihoming better
1430 Complete rewrite of SendQueries. Works much better now :-)
1432 Revision 1.125 2003/05/22 01:50:45 cheshire
1433 Fix warnings, and improve log messages
1435 Revision 1.124 2003/05/22 01:41:50 cheshire
1436 DiscardDeregistrations doesn't need InterfaceID parameter
1438 Revision 1.123 2003/05/22 01:38:55 cheshire
1439 Change bracketing of #pragma mark
1441 Revision 1.122 2003/05/21 19:59:04 cheshire
1442 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1443 Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
1445 Revision 1.121 2003/05/21 17:54:07 ksekar
1446 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1447 New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
1449 Revision 1.120 2003/05/19 22:14:14 ksekar
1450 <rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type
1452 Revision 1.119 2003/05/16 01:34:10 cheshire
1455 Revision 1.118 2003/05/14 18:48:40 cheshire
1456 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1457 More minor refinements:
1458 mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
1459 mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
1461 Revision 1.117 2003/05/14 07:08:36 cheshire
1462 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1463 Previously, when there was any network configuration change, mDNSResponder
1464 would tear down the entire list of active interfaces and start again.
1465 That was very disruptive, and caused the entire cache to be flushed,
1466 and caused lots of extra network traffic. Now it only removes interfaces
1467 that have really gone, and only adds new ones that weren't there before.
1469 Revision 1.116 2003/05/14 06:51:56 cheshire
1470 <rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
1472 Revision 1.115 2003/05/14 06:44:31 cheshire
1473 Improve debugging message
1475 Revision 1.114 2003/05/07 01:47:03 cheshire
1476 <rdar://problem/3250330> Also protect against NULL domainlabels
1478 Revision 1.113 2003/05/07 00:28:18 cheshire
1479 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
1481 Revision 1.112 2003/05/06 00:00:46 cheshire
1482 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
1484 Revision 1.111 2003/05/05 23:42:08 cheshire
1485 <rdar://problem/3245631> Resolves never succeed
1486 Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
1487 instead of "rr->LastAPTime = timenow - rr->ThisAPInterval"
1489 Revision 1.110 2003/04/30 21:09:59 cheshire
1490 <rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
1492 Revision 1.109 2003/04/26 02:41:56 cheshire
1493 <rdar://problem/3241281> Change timenow from a local variable to a structure member
1495 Revision 1.108 2003/04/25 01:45:56 cheshire
1496 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
1498 Revision 1.107 2003/04/25 00:41:31 cheshire
1499 <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
1501 Revision 1.106 2003/04/22 03:14:45 cheshire
1502 <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
1504 Revision 1.105 2003/04/22 01:07:43 cheshire
1505 <rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
1506 If TTL parameter is zero, leave record TTL unchanged
1508 Revision 1.104 2003/04/21 19:15:52 cheshire
1509 Fix some compiler warnings
1511 Revision 1.103 2003/04/19 02:26:35 cheshire
1512 <rdar://problem/3233804> Incorrect goodbye packet after conflict
1514 Revision 1.102 2003/04/17 03:06:28 cheshire
1515 <rdar://problem/3231321> No need to query again when a service goes away
1516 Set UnansweredQueries to 2 when receiving a "goodbye" packet
1518 Revision 1.101 2003/04/15 20:58:31 jgraessl
1519 <rdar://problem/3229014> Added a hash to lookup records in the cache.
1521 Revision 1.100 2003/04/15 18:53:14 cheshire
1522 <rdar://problem/3229064> Bug in ScheduleNextTask
1523 mDNS.c 1.94 incorrectly combined two "if" statements into one.
1525 Revision 1.99 2003/04/15 18:09:13 jgraessl
1526 <rdar://problem/3228892>
1527 Reviewed by: Stuart Cheshire
1528 Added code to keep track of when the next cache item will expire so we can
1529 call TidyRRCache only when necessary.
1531 Revision 1.98 2003/04/03 03:43:55 cheshire
1532 <rdar://problem/3216837> Off-by-one error in probe rate limiting
1534 Revision 1.97 2003/04/02 01:48:17 cheshire
1535 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1536 Additional fix pointed out by Josh:
1537 Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
1539 Revision 1.96 2003/04/01 23:58:55 cheshire
1540 Minor comment changes
1542 Revision 1.95 2003/04/01 23:46:05 cheshire
1543 <rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
1544 mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
1545 to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
1546 window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
1547 FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
1549 Revision 1.94 2003/03/29 01:55:19 cheshire
1550 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1551 Solution: Major cleanup of packet timing and conflict handling rules
1553 Revision 1.93 2003/03/28 01:54:36 cheshire
1554 Minor tidyup of IPv6 (AAAA) code
1556 Revision 1.92 2003/03/27 03:30:55 cheshire
1557 <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
1558 Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
1560 1. Make mDNS_DeregisterInterface() safe to call from a callback
1561 2. Make HostNameCallback() use DeadvertiseInterface() instead
1562 (it never really needed to deregister the interface at all)
1564 Revision 1.91 2003/03/15 04:40:36 cheshire
1565 Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
1567 Revision 1.90 2003/03/14 20:26:37 cheshire
1568 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1570 Revision 1.89 2003/03/12 19:57:50 cheshire
1571 Fixed typo in debug message
1573 Revision 1.88 2003/03/12 00:17:44 cheshire
1574 <rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records
1576 Revision 1.87 2003/03/11 01:27:20 cheshire
1577 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1579 Revision 1.86 2003/03/06 20:44:33 cheshire
1582 Revision 1.85 2003/03/05 03:38:35 cheshire
1583 <rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
1584 Fixed by leaving client in list after conflict, until client explicitly deallocates
1586 Revision 1.84 2003/03/05 01:27:30 cheshire
1587 <rdar://problem/3185482> Different TTL for multicast versus unicast responses
1588 When building unicast responses, record TTLs are capped to 10 seconds
1590 Revision 1.83 2003/03/04 23:48:52 cheshire
1591 <rdar://problem/3188865> Double probes after wake from sleep
1592 Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
1594 Revision 1.82 2003/03/04 23:38:29 cheshire
1595 <rdar://problem/3099194> mDNSResponder needs performance improvements
1596 Only set rr->CRActiveQuestion to point to the
1597 currently active representative of a question set
1599 Revision 1.81 2003/02/21 03:35:34 cheshire
1600 <rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
1602 Revision 1.80 2003/02/21 02:47:53 cheshire
1603 <rdar://problem/3099194> mDNSResponder needs performance improvements
1604 Several places in the code were calling CacheRRActive(), which searched the entire
1605 question list every time, to see if this cache resource record answers any question.
1606 Instead, we now have a field "CRActiveQuestion" in the resource record structure
1608 Revision 1.79 2003/02/21 01:54:07 cheshire
1609 <rdar://problem/3099194> mDNSResponder needs performance improvements
1610 Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
1612 Revision 1.78 2003/02/20 06:48:32 cheshire
1613 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
1614 Reviewed by: Josh Graessley, Bob Bradley
1616 Revision 1.77 2003/01/31 03:35:59 cheshire
1617 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1618 When there were *two* active questions in the list, they were incorrectly
1619 finding *each other* and *both* being marked as duplicates of another question
1621 Revision 1.76 2003/01/29 02:46:37 cheshire
1623 A physical interface is identified solely by its InterfaceID (not by IP and type).
1624 On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
1625 In cases where the requested outbound protocol (v4 or v6) is not supported on
1626 that InterfaceID, the platform support layer should simply discard that packet.
1628 Revision 1.75 2003/01/29 01:47:40 cheshire
1629 Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
1631 Revision 1.74 2003/01/28 05:26:25 cheshire
1632 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1633 Add 'Active' flag for interfaces
1635 Revision 1.73 2003/01/28 03:45:12 cheshire
1636 Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
1638 Revision 1.72 2003/01/28 01:49:48 cheshire
1639 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1640 FindDuplicateQuestion() was incorrectly finding the question itself in the list,
1641 and incorrectly marking it as a duplicate (of itself), so that it became inactive.
1643 Revision 1.71 2003/01/28 01:41:44 cheshire
1644 <rdar://problem/3153091> Race condition when network change causes bad stuff
1645 When an interface goes away, interface-specific questions on that interface become orphaned.
1646 Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
1647 Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
1649 Revision 1.70 2003/01/23 19:00:20 cheshire
1650 Protect against infinite loops in mDNS_Execute
1652 Revision 1.69 2003/01/21 22:56:32 jgraessl
1653 <rdar://problem/3124348> service name changes are not properly handled
1654 Submitted by: Stuart Cheshire
1655 Reviewed by: Joshua Graessley
1656 Applying changes for 3124348 to main branch. 3124348 changes went in to a
1659 Revision 1.68 2003/01/17 04:09:27 cheshire
1660 <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
1662 Revision 1.67 2003/01/17 03:56:45 cheshire
1663 Default 24-hour TTL is far too long. Changing to two hours.
1665 Revision 1.66 2003/01/13 23:49:41 jgraessl
1666 Merged changes for the following fixes in to top of tree:
1667 <rdar://problem/3086540> computer name changes not handled properly
1668 <rdar://problem/3124348> service name changes are not properly handled
1669 <rdar://problem/3124352> announcements sent in pairs, failing chattiness test
1671 Revision 1.65 2002/12/23 22:13:28 jgraessl
1672 Reviewed by: Stuart Cheshire
1673 Initial IPv6 support for mDNSResponder.
1675 Revision 1.64 2002/11/26 20:49:06 cheshire
1676 <rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
1678 Revision 1.63 2002/09/21 20:44:49 zarzycki
1681 Revision 1.62 2002/09/20 03:25:37 cheshire
1682 Fix some compiler warnings
1684 Revision 1.61 2002/09/20 01:05:24 cheshire
1685 Don't kill the Extras list in mDNS_DeregisterService()
1687 Revision 1.60 2002/09/19 23:47:35 cheshire
1688 Added mDNS_RegisterNoSuchService() function for assertion of non-existence
1689 of a particular named service
1691 Revision 1.59 2002/09/19 21:25:34 cheshire
1692 mDNS_snprintf() doesn't need to be in a separate file
1694 Revision 1.58 2002/09/19 04:20:43 cheshire
1695 Remove high-ascii characters that confuse some systems
1697 Revision 1.57 2002/09/17 01:07:08 cheshire
1698 Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
1700 Revision 1.56 2002/09/16 19:44:17 cheshire
1701 Merge in license terms from Quinn's copy, in preparation for Darwin release
1704 #include "DNSCommon.h" // Defines general DNS untility routines
1705 #include "uDNS.h" // Defines entry points into unicast-specific routines
1706 // Disable certain benign warnings with Microsoft compilers
1707 #if(defined(_MSC_VER))
1708 // Disable "conditional expression is constant" warning for debug macros.
1709 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
1710 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
1711 #pragma warning(disable:4127)
1713 // Disable "assignment within conditional expression".
1714 // Other compilers understand the convention that if you place the assignment expression within an extra pair
1715 // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
1716 // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
1717 // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
1718 #pragma warning(disable:4706)
1721 // ***************************************************************************
1722 #if COMPILER_LIKES_PRAGMA_MARK
1724 #pragma mark - Program Constants
1727 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
1728 mDNSexport
const mDNSv4Addr zerov4Addr
= { { 0 } };
1729 mDNSexport
const mDNSv6Addr zerov6Addr
= { { 0 } };
1730 mDNSexport
const mDNSEthAddr zeroEthAddr
= { { 0 } };
1731 mDNSexport
const mDNSv4Addr onesIPv4Addr
= { { 255, 255, 255, 255 } };
1732 mDNSexport
const mDNSv6Addr onesIPv6Addr
= { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
1733 mDNSexport
const mDNSAddr zeroAddr
= { mDNSAddrType_None
, {{{ 0 }}} };
1735 mDNSexport
const mDNSInterfaceID mDNSInterface_Any
= 0;
1736 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)1;
1738 mDNSlocal
const mDNSInterfaceID mDNSInterfaceMark
= (mDNSInterfaceID
)~0;
1740 #define UnicastDNSPortAsNumber 53
1741 #define MulticastDNSPortAsNumber 5353
1742 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
1743 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
1744 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
1745 mDNSexport
const mDNSv4Addr AllDNSLinkGroupv4
= { { 224, 0, 0, 251 } };
1746 mDNSexport
const mDNSv6Addr AllDNSLinkGroupv6
= { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } };
1747 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
1748 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
1750 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
1751 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
1752 mDNSexport
const mDNSOpaque16 uQueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_RD
, 0 } };
1753 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
1754 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
1755 mDNSexport
const mDNSOpaque16 UpdateRespFlags
={ { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
1757 // Any records bigger than this are considered 'large' records
1758 #define SmallRecordLimit 1024
1760 #define kMaxUpdateCredits 10
1761 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1763 mDNSexport
const char *const mDNS_DomainTypeNames
[] =
1765 "b._dns-sd._udp.", // Browse
1766 "db._dns-sd._udp.", // Default Browse
1767 "lb._dns-sd._udp.", // Legacy Browse
1768 "r._dns-sd._udp.", // Registration
1769 "dr._dns-sd._udp." // Default Registration
1772 #ifdef UNICAST_DISABLED
1773 #define uDNS_IsActiveQuery(q, u) mDNSfalse
1776 // ***************************************************************************
1777 #if COMPILER_LIKES_PRAGMA_MARK
1779 #pragma mark - Specialized mDNS version of vsnprintf
1782 static const struct mDNSprintf_format
1784 unsigned leftJustify
: 1;
1785 unsigned forceSign
: 1;
1786 unsigned zeroPad
: 1;
1787 unsigned havePrecision
: 1;
1791 char sign
; // +, - or space
1792 unsigned int fieldWidth
;
1793 unsigned int precision
;
1794 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1796 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
1798 mDNSu32 nwritten
= 0;
1800 if (buflen
== 0) return(0);
1801 buflen
--; // Pre-reserve one space in the buffer for the terminating null
1802 if (buflen
== 0) goto exit
;
1804 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
1808 *sbuffer
++ = (char)c
;
1809 if (++nwritten
>= buflen
) goto exit
;
1813 unsigned int i
=0, j
;
1814 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
1815 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
1816 // The size needs to be enough for a 256-byte domain name plus some error text.
1817 #define mDNS_VACB_Size 300
1818 char mDNS_VACB
[mDNS_VACB_Size
];
1819 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
1820 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
1821 char *s
= mDNS_VACB_Lim
, *digits
;
1822 struct mDNSprintf_format F
= mDNSprintf_format_default
;
1824 while (1) // decode flags
1827 if (c
== '-') F
.leftJustify
= 1;
1828 else if (c
== '+') F
.forceSign
= 1;
1829 else if (c
== ' ') F
.sign
= ' ';
1830 else if (c
== '#') F
.altForm
++;
1831 else if (c
== '0') F
.zeroPad
= 1;
1835 if (c
== '*') // decode field width
1837 int f
= va_arg(arg
, int);
1838 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
1839 F
.fieldWidth
= (unsigned int)f
;
1844 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
1845 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
1848 if (c
== '.') // decode precision
1850 if ((c
= *++fmt
) == '*')
1851 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
1852 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
1853 F
.precision
= (10 * F
.precision
) + (c
- '0');
1854 F
.havePrecision
= 1;
1857 if (F
.leftJustify
) F
.zeroPad
= 0;
1860 switch (c
) // perform appropriate conversion
1863 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
1864 case 'l' : // fall through
1865 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
1867 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
1868 else n
= (unsigned long)va_arg(arg
, int);
1869 if (F
.hSize
) n
= (short) n
;
1870 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
1871 else if (F
.forceSign
) F
.sign
= '+';
1873 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1874 else n
= va_arg(arg
, unsigned int);
1875 if (F
.hSize
) n
= (unsigned short) n
;
1878 decimal
: if (!F
.havePrecision
)
1882 F
.precision
= F
.fieldWidth
;
1883 if (F
.sign
) --F
.precision
;
1885 if (F
.precision
< 1) F
.precision
= 1;
1887 if (F
.precision
> mDNS_VACB_Size
- 1)
1888 F
.precision
= mDNS_VACB_Size
- 1;
1889 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
1890 for (; i
< F
.precision
; i
++) *--s
= '0';
1891 if (F
.sign
) { *--s
= F
.sign
; i
++; }
1894 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1895 else n
= va_arg(arg
, unsigned int);
1896 if (F
.hSize
) n
= (unsigned short) n
;
1897 if (!F
.havePrecision
)
1899 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
1900 if (F
.precision
< 1) F
.precision
= 1;
1902 if (F
.precision
> mDNS_VACB_Size
- 1)
1903 F
.precision
= mDNS_VACB_Size
- 1;
1904 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
1905 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
1906 for (; i
< F
.precision
; i
++) *--s
= '0';
1910 unsigned char *a
= va_arg(arg
, unsigned char *);
1911 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
1914 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
1917 mDNSAddr
*ip
= (mDNSAddr
*)a
;
1920 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
1921 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
1922 default: F
.precision
= 0; break;
1925 switch (F
.precision
)
1927 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
1928 a
[0], a
[1], a
[2], a
[3]); break;
1929 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
1930 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
1931 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
1932 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
1933 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
1934 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
1935 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify address size "
1936 "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
1942 case 'p' : F
.havePrecision
= F
.lSize
= 1;
1944 case 'X' : digits
= "0123456789ABCDEF";
1946 case 'x' : digits
= "0123456789abcdef";
1947 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1948 else n
= va_arg(arg
, unsigned int);
1949 if (F
.hSize
) n
= (unsigned short) n
;
1950 if (!F
.havePrecision
)
1954 F
.precision
= F
.fieldWidth
;
1955 if (F
.altForm
) F
.precision
-= 2;
1957 if (F
.precision
< 1) F
.precision
= 1;
1959 if (F
.precision
> mDNS_VACB_Size
- 1)
1960 F
.precision
= mDNS_VACB_Size
- 1;
1961 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
1962 for (; i
< F
.precision
; i
++) *--s
= '0';
1963 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
1966 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
1968 case 's' : s
= va_arg(arg
, char *);
1969 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
1970 else switch (F
.altForm
)
1973 if (!F
.havePrecision
) // C string
1977 while ((i
< F
.precision
) && s
[i
]) i
++;
1978 // Make sure we don't truncate in the middle of a UTF-8 character
1979 // If last character we got was any kind of UTF-8 multi-byte character,
1980 // then see if we have to back up.
1981 // This is not as easy as the similar checks below, because
1982 // here we can't assume it's safe to examine the *next* byte, so we
1983 // have to confine ourselves to working only backwards in the string.
1984 j
= i
; // Record where we got to
1985 // Now, back up until we find first non-continuation-char
1986 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
1987 // Now s[i-1] is the first non-continuation-char
1988 // and (j-i) is the number of continuation-chars we found
1989 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
1991 i
--; // Tentatively eliminate this start-char as well
1992 // Now (j-i) is the number of characters we're considering eliminating.
1993 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
1994 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
1995 // (with sign extension) then the result has to be 0xFE.
1996 // If this is right, then we reinstate the tentatively eliminated bytes.
1997 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
2001 case 1: i
= (unsigned char) *s
++; break; // Pascal string
2002 case 2: { // DNS label-sequence name
2003 unsigned char *a
= (unsigned char *)s
;
2004 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
2005 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
2008 if (*a
> 63) { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
2009 if (s
+ *a
>= &mDNS_VACB
[254]) { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
2010 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%#s.", a
);
2013 i
= (mDNSu32
)(s
- mDNS_VACB
);
2014 s
= mDNS_VACB
; // Reset s back to the start of the buffer
2018 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
2019 if (F
.havePrecision
&& i
> F
.precision
)
2020 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2023 case 'n' : s
= va_arg(arg
, char *);
2024 if (F
.hSize
) * (short *) s
= (short)nwritten
;
2025 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
2026 else * (int *) s
= (int)nwritten
;
2029 default: s
= mDNS_VACB
;
2030 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
2032 case '%' : *sbuffer
++ = (char)c
;
2033 if (++nwritten
>= buflen
) goto exit
;
2037 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
2040 if (++nwritten
>= buflen
) goto exit
;
2041 } while (i
< --F
.fieldWidth
);
2043 // Make sure we don't truncate in the middle of a UTF-8 character.
2044 // 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
2045 // 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
2046 // 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).
2047 if (i
> buflen
- nwritten
)
2048 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2049 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
2051 if (nwritten
>= buflen
) goto exit
;
2053 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
2056 if (++nwritten
>= buflen
) goto exit
;
2065 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
2071 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);
2077 // ***************************************************************************
2078 #if COMPILER_LIKES_PRAGMA_MARK
2080 #pragma mark - General Utility Functions
2083 #define InitialQuestionInterval (mDNSPlatformOneSecond/2)
2084 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
2085 #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
2087 mDNSlocal
void SetNextQueryTime(mDNS
*const m
, const DNSQuestion
*const q
)
2089 if (ActiveQuestion(q
))
2090 if (m
->NextScheduledQuery
- (q
->LastQTime
+ q
->ThisQInterval
) > 0)
2091 m
->NextScheduledQuery
= (q
->LastQTime
+ q
->ThisQInterval
);
2094 mDNSlocal CacheGroup
*CacheGroupForName(const mDNS
*const m
, const mDNSu32 slot
, const mDNSu32 namehash
, const domainname
*const name
)
2097 for (cg
= m
->rrcache_hash
[slot
]; cg
; cg
=cg
->next
)
2098 if (cg
->namehash
== namehash
&& SameDomainName(cg
->name
, name
))
2103 mDNSlocal CacheGroup
*CacheGroupForRecord(const mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
2105 return(CacheGroupForName(m
, slot
, rr
->namehash
, rr
->name
));
2108 mDNSlocal mDNSBool
AddressIsLocalSubnet(mDNS
*const m
, const mDNSInterfaceID InterfaceID
, const mDNSAddr
*addr
)
2110 NetworkInterfaceInfo
*intf
;
2112 if (addr
->type
== mDNSAddrType_IPv4
)
2114 if (addr
->ip
.v4
.b
[0] == 169 && addr
->ip
.v4
.b
[1] == 254) return(mDNStrue
);
2115 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2116 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
2117 if (((intf
->ip
.ip
.v4
.NotAnInteger
^ addr
->ip
.v4
.NotAnInteger
) & intf
->mask
.ip
.v4
.NotAnInteger
) == 0)
2121 if (addr
->type
== mDNSAddrType_IPv6
)
2123 if (addr
->ip
.v6
.b
[0] == 0xFE && addr
->ip
.v6
.b
[1] == 0x80) return(mDNStrue
);
2124 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2125 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
2126 if ((((intf
->ip
.ip
.v6
.l
[0] ^ addr
->ip
.v6
.l
[0]) & intf
->mask
.ip
.v6
.l
[0]) == 0) &&
2127 (((intf
->ip
.ip
.v6
.l
[1] ^ addr
->ip
.v6
.l
[1]) & intf
->mask
.ip
.v6
.l
[1]) == 0) &&
2128 (((intf
->ip
.ip
.v6
.l
[2] ^ addr
->ip
.v6
.l
[2]) & intf
->mask
.ip
.v6
.l
[2]) == 0) &&
2129 (((intf
->ip
.ip
.v6
.l
[3] ^ addr
->ip
.v6
.l
[3]) & intf
->mask
.ip
.v6
.l
[3]) == 0))
2136 // Set up a AuthRecord with sensible default values.
2137 // These defaults may be overwritten with new values before mDNS_Register is called
2138 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
2139 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
2141 mDNSPlatformMemZero(&rr
->uDNS_info
, sizeof(uDNS_RegInfo
));
2142 // Don't try to store a TTL bigger than we can represent in platform time units
2143 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
2144 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
2145 else if (ttl
== 0) // And Zero TTL is illegal
2146 ttl
= DefaultTTLforRRType(rrtype
);
2148 // Field Group 1: The actual information pertaining to this resource record
2149 rr
->resrec
.RecordType
= RecordType
;
2150 rr
->resrec
.InterfaceID
= InterfaceID
;
2151 rr
->resrec
.name
= &rr
->namestorage
;
2152 rr
->resrec
.rrtype
= rrtype
;
2153 rr
->resrec
.rrclass
= kDNSClass_IN
;
2154 rr
->resrec
.rroriginalttl
= ttl
;
2155 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
2156 // rr->resrec.rdestimate = set in mDNS_Register_internal
2157 // rr->resrec.rdata = MUST be set by client
2160 rr
->resrec
.rdata
= RDataStorage
;
2163 rr
->resrec
.rdata
= &rr
->rdatastorage
;
2164 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
2167 // Field Group 2: Persistent metadata for Authoritative Records
2168 rr
->Additional1
= mDNSNULL
;
2169 rr
->Additional2
= mDNSNULL
;
2170 rr
->DependentOn
= mDNSNULL
;
2171 rr
->RRSet
= mDNSNULL
;
2172 rr
->RecordCallback
= Callback
;
2173 rr
->RecordContext
= Context
;
2175 rr
->HostTarget
= mDNSfalse
;
2176 rr
->AllowRemoteQuery
= mDNSfalse
;
2177 rr
->ForceMCast
= mDNSfalse
;
2179 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
2181 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
2184 // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
2185 // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
2186 mDNSlocal
void AnswerLocalOnlyQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, AuthRecord
*rr
, mDNSBool AddRecord
)
2188 // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
2189 if (AddRecord
) rr
->LocalAnswer
= mDNStrue
;
2190 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2191 if (q
->QuestionCallback
)
2192 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
2193 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2196 // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers to each,
2197 // stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
2198 // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
2199 // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
2200 mDNSlocal
void AnswerLocalQuestions(mDNS
*const m
, AuthRecord
*rr
, mDNSBool AddRecord
)
2202 if (m
->CurrentQuestion
) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2204 m
->CurrentQuestion
= m
->LocalOnlyQuestions
;
2205 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewLocalOnlyQuestions
)
2207 DNSQuestion
*q
= m
->CurrentQuestion
;
2208 m
->CurrentQuestion
= q
->next
;
2209 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2210 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
2213 // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
2214 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
2216 m
->CurrentQuestion
= m
->Questions
;
2217 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2219 DNSQuestion
*q
= m
->CurrentQuestion
;
2220 m
->CurrentQuestion
= q
->next
;
2221 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2222 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
2226 m
->CurrentQuestion
= mDNSNULL
;
2229 // ***************************************************************************
2230 #if COMPILER_LIKES_PRAGMA_MARK
2232 #pragma mark - Resource Record Utility Functions
2235 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
2237 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
2238 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2239 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2240 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
2242 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2243 (ResourceRecordIsValidAnswer(RR) && \
2244 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
2246 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2247 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
2249 #define InitialAnnounceCount ((mDNSu8)10)
2251 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
2252 // This means that because the announce interval is doubled after sending the first packet, the first
2253 // observed on-the-wire inter-packet interval between announcements is actually one second.
2254 // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
2255 #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
2256 #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
2257 #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
2259 #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
2260 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
2261 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
2263 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
2264 #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
2265 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
2266 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
2268 #define MaxUnansweredQueries 4
2270 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
2271 // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
2272 // TTL and rdata may differ.
2273 // This is used for cache flush management:
2274 // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
2275 // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
2276 mDNSlocal mDNSBool
SameResourceRecordSignature(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
2278 if (!r1
) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse
); }
2279 if (!r2
) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse
); }
2280 if (r1
->InterfaceID
&&
2282 r1
->InterfaceID
!= r2
->InterfaceID
) return(mDNSfalse
);
2283 return(mDNSBool
)(r1
->rrtype
== r2
->rrtype
&& r1
->rrclass
== r2
->rrclass
&& r1
->namehash
== r2
->namehash
&& SameDomainName(r1
->name
, r2
->name
));
2286 // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
2287 // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
2288 // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
2289 // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
2290 // so a response of any type should match, even if it is not actually the type the client plans to use.
2291 mDNSlocal mDNSBool
PacketRRMatchesSignature(const CacheRecord
*const pktrr
, const AuthRecord
*const authrr
)
2293 if (!pktrr
) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse
); }
2294 if (!authrr
) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse
); }
2295 if (pktrr
->resrec
.InterfaceID
&&
2296 authrr
->resrec
.InterfaceID
&&
2297 pktrr
->resrec
.InterfaceID
!= authrr
->resrec
.InterfaceID
) return(mDNSfalse
);
2298 if (!(authrr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) && pktrr
->resrec
.rrtype
!= authrr
->resrec
.rrtype
) return(mDNSfalse
);
2299 return(mDNSBool
)(pktrr
->resrec
.rrclass
== authrr
->resrec
.rrclass
&& pktrr
->resrec
.namehash
== authrr
->resrec
.namehash
&& SameDomainName(pktrr
->resrec
.name
, authrr
->resrec
.name
));
2302 // IdenticalResourceRecord returns true if two resources records have
2303 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
2304 mDNSlocal mDNSBool
IdenticalResourceRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
2306 if (!r1
) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse
); }
2307 if (!r2
) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse
); }
2308 if (r1
->rrtype
!= r2
->rrtype
|| r1
->rrclass
!= r2
->rrclass
|| r1
->namehash
!= r2
->namehash
|| !SameDomainName(r1
->name
, r2
->name
)) return(mDNSfalse
);
2309 return(SameRData(r1
, r2
));
2312 // CacheRecord *ks is the CacheRecord from the known answer list in the query.
2313 // This is the information that the requester believes to be correct.
2314 // AuthRecord *rr is the answer we are proposing to give, if not suppressed.
2315 // This is the information that we believe to be correct.
2316 // We've already determined that we plan to give this answer on this interface
2317 // (either the record is non-specific, or it is specific to this interface)
2318 // so now we just need to check the name, type, class, rdata and TTL.
2319 mDNSlocal mDNSBool
ShouldSuppressKnownAnswer(const CacheRecord
*const ka
, const AuthRecord
*const rr
)
2321 // If RR signature is different, or data is different, then don't suppress our answer
2322 if (!IdenticalResourceRecord(&ka
->resrec
, &rr
->resrec
)) return(mDNSfalse
);
2324 // If the requester's indicated TTL is less than half the real TTL,
2325 // we need to give our answer before the requester's copy expires.
2326 // If the requester's indicated TTL is at least half the real TTL,
2327 // then we can suppress our answer this time.
2328 // If the requester's indicated TTL is greater than the TTL we believe,
2329 // then that's okay, and we don't need to do anything about it.
2330 // (If two responders on the network are offering the same information,
2331 // that's okay, and if they are offering the information with different TTLs,
2332 // the one offering the lower TTL should defer to the one offering the higher TTL.)
2333 return(mDNSBool
)(ka
->resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/ 2);
2336 mDNSlocal
void SetNextAnnounceProbeTime(mDNS
*const m
, const AuthRecord
*const rr
)
2338 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
2340 //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
2341 if (m
->NextScheduledProbe
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
2342 m
->NextScheduledProbe
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
2344 else if (rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
))
2346 if (m
->NextScheduledResponse
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
2347 m
->NextScheduledResponse
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
2351 mDNSlocal
void InitializeLastAPTime(mDNS
*const m
, AuthRecord
*const rr
)
2353 // To allow us to aggregate probes when a group of services are registered together,
2354 // the first probe is delayed 1/4 second. This means the common-case behaviour is:
2355 // 1/4 second wait; probe
2356 // 1/4 second wait; probe
2357 // 1/4 second wait; probe
2358 // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
2360 // If we have no probe suppression time set, or it is in the past, set it now
2361 if (m
->SuppressProbes
== 0 || m
->SuppressProbes
- m
->timenow
< 0)
2363 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ DefaultProbeIntervalForTypeUnique
);
2364 // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
2365 if (m
->SuppressProbes
- m
->NextScheduledProbe
>= 0)
2366 m
->SuppressProbes
= m
->NextScheduledProbe
;
2367 // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
2368 if (m
->SuppressProbes
- m
->NextScheduledQuery
>= 0)
2369 m
->SuppressProbes
= m
->NextScheduledQuery
;
2372 // We announce to flush stale data from other caches. It is a reasonable assumption that any
2373 // old stale copies will probably have the same TTL we're using, so announcing longer than
2374 // this serves no purpose -- any stale copies of that record will have expired by then anyway.
2375 rr
->AnnounceUntil
= m
->timenow
+ TicksTTL(rr
);
2376 rr
->LastAPTime
= m
->SuppressProbes
- rr
->ThisAPInterval
;
2377 // Set LastMCTime to now, to inhibit multicast responses
2378 // (no need to send additional multicast responses when we're announcing anyway)
2379 rr
->LastMCTime
= m
->timenow
;
2380 rr
->LastMCInterface
= mDNSInterfaceMark
;
2382 // If this is a record type that's not going to probe, then delay its first announcement so that
2383 // it will go out synchronized with the first announcement for the other records that *are* probing.
2384 // This is a minor performance tweak that helps keep groups of related records synchronized together.
2385 // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
2386 // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
2387 // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
2388 // because they will meet the criterion of being at least half-way to their scheduled announcement time.
2389 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
)
2390 rr
->LastAPTime
+= DefaultProbeIntervalForTypeUnique
* DefaultProbeCountForTypeUnique
+ rr
->ThisAPInterval
/ 2;
2392 SetNextAnnounceProbeTime(m
, rr
);
2395 #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
2397 mDNSlocal
void SetTargetToHostName(mDNS
*const m
, AuthRecord
*const rr
)
2399 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
2401 if (!target
) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr
->resrec
.rrtype
);
2403 if (target
&& SameDomainName(target
, &m
->MulticastHostname
))
2404 debugf("SetTargetToHostName: Target of %##s is already %##s", rr
->resrec
.name
->c
, target
->c
);
2406 if (target
&& !SameDomainName(target
, &m
->MulticastHostname
))
2408 AssignDomainName(target
, &m
->MulticastHostname
);
2409 SetNewRData(&rr
->resrec
, mDNSNULL
, 0);
2411 // If we're in the middle of probing this record, we need to start again,
2412 // because changing its rdata may change the outcome of the tie-breaker.
2413 // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
2414 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
2416 // If we've announced this record, we really should send a goodbye packet for the old rdata before
2417 // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
2418 // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
2419 if (rr
->RequireGoodbye
&& rr
->resrec
.RecordType
== kDNSRecordTypeShared
)
2420 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
));
2422 rr
->AnnounceCount
= InitialAnnounceCount
;
2423 rr
->RequireGoodbye
= mDNSfalse
;
2424 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
2425 InitializeLastAPTime(m
,rr
);
2429 mDNSlocal
void AcknowledgeRecord(mDNS
*const m
, AuthRecord
*const rr
)
2431 if (!rr
->Acknowledged
&& rr
->RecordCallback
)
2433 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2434 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2435 rr
->Acknowledged
= mDNStrue
;
2436 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2437 rr
->RecordCallback(m
, rr
, mStatus_NoError
);
2438 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2442 // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
2443 #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
2444 #define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
2446 mDNSlocal mStatus
mDNS_Register_internal(mDNS
*const m
, AuthRecord
*const rr
)
2448 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
2450 AuthRecord
**p
= &m
->ResourceRecords
;
2451 AuthRecord
**d
= &m
->DuplicateRecords
;
2453 mDNSPlatformMemZero(&rr
->uDNS_info
, sizeof(uDNS_RegInfo
));
2455 if ((mDNSs32
)rr
->resrec
.rroriginalttl
<= 0)
2456 { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m
, rr
)); return(mStatus_BadParamErr
); }
2458 #ifndef UNICAST_DISABLED
2459 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| rr
->ForceMCast
|| IsLocalDomain(rr
->resrec
.name
))
2460 rr
->uDNS_info
.id
= zeroID
;
2461 else return uDNS_RegisterRecord(m
, rr
);
2464 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2465 while (*d
&& *d
!= rr
) d
=&(*d
)->next
;
2468 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
));
2469 return(mStatus_AlreadyRegistered
);
2472 if (rr
->DependentOn
)
2474 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
2475 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
2478 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
2479 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2480 return(mStatus_Invalid
);
2482 if (!(rr
->DependentOn
->resrec
.RecordType
& (kDNSRecordTypeUnique
| kDNSRecordTypeVerified
)))
2484 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
2485 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->DependentOn
->resrec
.RecordType
);
2486 return(mStatus_Invalid
);
2490 // If this resource record is referencing a specific interface, make sure it exists
2491 if (rr
->resrec
.InterfaceID
&& rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
2493 NetworkInterfaceInfo
*intf
;
2494 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2495 if (intf
->InterfaceID
== rr
->resrec
.InterfaceID
) break;
2498 debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr
->resrec
.InterfaceID
);
2499 return(mStatus_BadReferenceErr
);
2503 rr
->next
= mDNSNULL
;
2505 // Field Group 1: Persistent metadata for Authoritative Records
2506 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2507 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2508 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2509 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2510 // rr->Callback = already set in mDNS_SetupResourceRecord
2511 // rr->Context = already set in mDNS_SetupResourceRecord
2512 // rr->RecordType = already set in mDNS_SetupResourceRecord
2513 // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2514 // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2515 // Make sure target is not uninitialized data, or we may crash writing debugging log messages
2516 if (rr
->HostTarget
&& target
) target
->c
[0] = 0;
2518 // Field Group 2: Transient state for Authoritative Records
2519 rr
->Acknowledged
= mDNSfalse
;
2520 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
2521 rr
->AnnounceCount
= InitialAnnounceCount
;
2522 rr
->RequireGoodbye
= mDNSfalse
;
2523 rr
->LocalAnswer
= mDNSfalse
;
2524 rr
->IncludeInProbe
= mDNSfalse
;
2525 rr
->ImmedAnswer
= mDNSNULL
;
2526 rr
->ImmedUnicast
= mDNSfalse
;
2527 rr
->ImmedAdditional
= mDNSNULL
;
2528 rr
->SendRNow
= mDNSNULL
;
2529 rr
->v4Requester
= zerov4Addr
;
2530 rr
->v6Requester
= zerov6Addr
;
2531 rr
->NextResponse
= mDNSNULL
;
2532 rr
->NR_AnswerTo
= mDNSNULL
;
2533 rr
->NR_AdditionalTo
= mDNSNULL
;
2534 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
2535 if (!rr
->HostTarget
) InitializeLastAPTime(m
, rr
);
2536 // rr->AnnounceUntil = Set for us in InitializeLastAPTime()
2537 // rr->LastAPTime = Set for us in InitializeLastAPTime()
2538 // rr->LastMCTime = Set for us in InitializeLastAPTime()
2539 // rr->LastMCInterface = Set for us in InitializeLastAPTime()
2540 rr
->NewRData
= mDNSNULL
;
2541 rr
->newrdlength
= 0;
2542 rr
->UpdateCallback
= mDNSNULL
;
2543 rr
->UpdateCredits
= kMaxUpdateCredits
;
2544 rr
->NextUpdateCredit
= 0;
2545 rr
->UpdateBlocked
= 0;
2547 // rr->resrec.interface = already set in mDNS_SetupResourceRecord
2548 // rr->resrec.name->c = MUST be set by client
2549 // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
2550 // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
2551 // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
2552 // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
2555 SetTargetToHostName(m
, rr
); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
2558 rr
->resrec
.rdlength
= GetRDLength(&rr
->resrec
, mDNSfalse
);
2559 rr
->resrec
.rdestimate
= GetRDLength(&rr
->resrec
, mDNStrue
);
2562 if (!ValidateDomainName(rr
->resrec
.name
))
2563 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
2565 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
2566 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
2567 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
2568 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& rr
->resrec
.rdlength
== 0) { rr
->resrec
.rdlength
= 1; rr
->resrec
.rdata
->u
.txt
.c
[0] = 0; }
2570 // Don't do this until *after* we've set rr->resrec.rdlength
2571 if (!ValidateRData(rr
->resrec
.rrtype
, rr
->resrec
.rdlength
, rr
->resrec
.rdata
))
2572 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
2574 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2575 rr
->resrec
.rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->resrec
.rdlength
, &rr
->resrec
.rdata
->u
);
2577 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
2579 // If this is supposed to be unique, make sure we don't have any name conflicts
2580 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2582 const AuthRecord
*s1
= rr
->RRSet
? rr
->RRSet
: rr
;
2583 for (r
= m
->ResourceRecords
; r
; r
=r
->next
)
2585 const AuthRecord
*s2
= r
->RRSet
? r
->RRSet
: r
;
2586 if (s1
!= s2
&& SameResourceRecordSignature(&r
->resrec
, &rr
->resrec
) && !SameRData(&r
->resrec
, &rr
->resrec
))
2589 if (r
) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
2591 debugf("Name conflict %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2592 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
2593 rr
->resrec
.rroriginalttl
= 0;
2594 rr
->ImmedAnswer
= mDNSInterfaceMark
;
2595 m
->NextScheduledResponse
= m
->timenow
;
2600 // Now that we've finished building our new record, make sure it's not identical to one we already have
2601 for (r
= m
->ResourceRecords
; r
; r
=r
->next
) if (RecordIsLocalDuplicate(r
, rr
)) break;
2605 debugf("Adding to duplicate list %p %s", rr
, ARDisplayString(m
,rr
));
2607 // If the previous copy of this record is already verified unique,
2608 // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
2609 // Setting ProbeCount to zero will cause SendQueries() to advance this record to
2610 // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
2611 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& r
->resrec
.RecordType
== kDNSRecordTypeVerified
)
2616 debugf("Adding to active record list %p %s", rr
, ARDisplayString(m
,rr
));
2617 if (!m
->NewLocalRecords
) m
->NewLocalRecords
= rr
;
2621 // For records that are not going to probe, acknowledge them right away
2622 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
&& rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
2623 AcknowledgeRecord(m
, rr
);
2625 return(mStatus_NoError
);
2628 mDNSlocal
void RecordProbeFailure(mDNS
*const m
, const AuthRecord
*const rr
)
2630 m
->ProbeFailTime
= m
->timenow
;
2631 m
->NumFailedProbes
++;
2632 // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
2633 // If a bunch of hosts have all been configured with the same name, then they'll all
2634 // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
2635 // up to name-10. After that they'll start adding random increments in the range 1-100,
2636 // so they're more likely to branch out in the available namespace and settle on a set of
2637 // unique names quickly. If after five more tries the host is still conflicting, then we
2638 // may have a serious problem, so we start rate-limiting so we don't melt down the network.
2639 if (m
->NumFailedProbes
>= 15)
2641 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
* 5);
2642 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
2643 m
->NumFailedProbes
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2647 mDNSlocal
void CompleteRDataUpdate(mDNS
*const m
, AuthRecord
*const rr
)
2649 RData
*OldRData
= rr
->resrec
.rdata
;
2650 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
); // Update our rdata
2651 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
2652 if (rr
->UpdateCallback
)
2653 rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
2656 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
2657 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
2658 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
2659 typedef enum { mDNS_Dereg_normal
, mDNS_Dereg_conflict
, mDNS_Dereg_repeat
} mDNS_Dereg_type
;
2661 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
2662 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2663 mDNSlocal mStatus
mDNS_Deregister_internal(mDNS
*const m
, AuthRecord
*const rr
, mDNS_Dereg_type drt
)
2666 mDNSu8 RecordType
= rr
->resrec
.RecordType
;
2667 AuthRecord
**p
= &m
->ResourceRecords
; // Find this record in our list of active records
2669 #ifndef UNICAST_DISABLED
2670 if (!(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| rr
->ForceMCast
|| IsLocalDomain(rr
->resrec
.name
)))
2671 return uDNS_DeregisterRecord(m
, rr
);
2674 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2678 // We found our record on the main list. See if there are any duplicates that need special handling.
2679 if (drt
== mDNS_Dereg_conflict
) // If this was a conflict, see that all duplicates get the same treatment
2681 // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
2682 // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
2683 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
) if (RecordIsLocalDuplicate(r2
, rr
)) r2
->ProbeCount
= 0xFF;
2687 // Before we delete the record (and potentially send a goodbye packet)
2688 // first see if we have a record on the duplicate list ready to take over from it.
2689 AuthRecord
**d
= &m
->DuplicateRecords
;
2690 while (*d
&& !RecordIsLocalDuplicate(*d
, rr
)) d
=&(*d
)->next
;
2693 AuthRecord
*dup
= *d
;
2694 debugf("Duplicate record %p taking over from %p %##s (%s)", dup
, rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2695 *d
= dup
->next
; // Cut replacement record from DuplicateRecords list
2696 dup
->next
= rr
->next
; // And then...
2697 rr
->next
= dup
; // ... splice it in right after the record we're about to delete
2698 dup
->resrec
.RecordType
= rr
->resrec
.RecordType
;
2699 dup
->ProbeCount
= rr
->ProbeCount
;
2700 dup
->AnnounceCount
= rr
->AnnounceCount
;
2701 dup
->RequireGoodbye
= rr
->RequireGoodbye
;
2702 dup
->ImmedAnswer
= rr
->ImmedAnswer
;
2703 dup
->ImmedUnicast
= rr
->ImmedUnicast
;
2704 dup
->ImmedAdditional
= rr
->ImmedAdditional
;
2705 dup
->v4Requester
= rr
->v4Requester
;
2706 dup
->v6Requester
= rr
->v6Requester
;
2707 dup
->ThisAPInterval
= rr
->ThisAPInterval
;
2708 dup
->AnnounceUntil
= rr
->AnnounceUntil
;
2709 dup
->LastAPTime
= rr
->LastAPTime
;
2710 dup
->LastMCTime
= rr
->LastMCTime
;
2711 dup
->LastMCInterface
= rr
->LastMCInterface
;
2712 rr
->RequireGoodbye
= mDNSfalse
;
2718 // We didn't find our record on the main list; try the DuplicateRecords list instead.
2719 p
= &m
->DuplicateRecords
;
2720 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2721 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
2722 if (*p
) rr
->RequireGoodbye
= mDNSfalse
;
2723 if (*p
) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2728 // No need to log an error message if we already know this is a potentially repeated deregistration
2729 if (drt
!= mDNS_Dereg_repeat
)
2730 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2731 return(mStatus_BadReferenceErr
);
2734 // If this is a shared record and we've announced it at least once,
2735 // we need to retract that announcement before we delete the record
2736 if (RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
2738 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2739 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
2740 rr
->resrec
.rroriginalttl
= 0;
2741 rr
->ImmedAnswer
= mDNSInterfaceMark
;
2742 if (m
->NextScheduledResponse
- (m
->timenow
+ mDNSPlatformOneSecond
/10) >= 0)
2743 m
->NextScheduledResponse
= (m
->timenow
+ mDNSPlatformOneSecond
/10);
2747 *p
= rr
->next
; // Cut this record from the list
2748 // If someone is about to look at this, bump the pointer forward
2749 if (m
->CurrentRecord
== rr
) m
->CurrentRecord
= rr
->next
;
2750 if (m
->NewLocalRecords
== rr
) m
->NewLocalRecords
= rr
->next
;
2751 rr
->next
= mDNSNULL
;
2753 if (RecordType
== kDNSRecordTypeUnregistered
)
2754 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
2755 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2756 else if (RecordType
== kDNSRecordTypeDeregistering
)
2757 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
2758 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2761 verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2762 rr
->resrec
.RecordType
= kDNSRecordTypeUnregistered
;
2765 if ((drt
== mDNS_Dereg_conflict
|| drt
== mDNS_Dereg_repeat
) && RecordType
== kDNSRecordTypeShared
)
2766 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
2767 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2769 // If we have an update queued up which never executed, give the client a chance to free that memory
2770 if (rr
->NewRData
) CompleteRDataUpdate(m
, rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
2772 if (rr
->LocalAnswer
) AnswerLocalQuestions(m
, rr
, mDNSfalse
);
2774 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2775 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2776 // In this case the likely client action to the mStatus_MemFree message is to free the memory,
2777 // so any attempt to touch rr after this is likely to lead to a crash.
2778 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2779 if (drt
!= mDNS_Dereg_conflict
)
2781 if (rr
->RecordCallback
) rr
->RecordCallback(m
, rr
, mStatus_MemFree
); // MUST NOT touch rr after this
2785 RecordProbeFailure(m
, rr
);
2786 if (rr
->RecordCallback
) rr
->RecordCallback(m
, rr
, mStatus_NameConflict
); // MUST NOT touch rr after this
2787 // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
2788 // Note that with all the client callbacks going on, by the time we get here all the
2789 // records we marked may have been explicitly deregistered by the client anyway.
2790 r2
= m
->DuplicateRecords
;
2793 if (r2
->ProbeCount
!= 0xFF) r2
= r2
->next
;
2794 else { mDNS_Deregister_internal(m
, r2
, mDNS_Dereg_conflict
); r2
= m
->DuplicateRecords
; }
2797 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2799 return(mStatus_NoError
);
2802 // ***************************************************************************
2803 #if COMPILER_LIKES_PRAGMA_MARK
2806 #pragma mark - Packet Sending Functions
2809 mDNSlocal
void AddRecordToResponseList(AuthRecord
***nrpp
, AuthRecord
*rr
, AuthRecord
*add
)
2811 if (rr
->NextResponse
== mDNSNULL
&& *nrpp
!= &rr
->NextResponse
)
2814 // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
2815 // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
2816 // The referenced record will definitely be acceptable (by recursive application of this rule)
2817 if (add
&& add
->NR_AdditionalTo
) add
= add
->NR_AdditionalTo
;
2818 rr
->NR_AdditionalTo
= add
;
2819 *nrpp
= &rr
->NextResponse
;
2821 debugf("AddRecordToResponseList: %##s (%s) already in list", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2824 mDNSlocal
void AddAdditionalsToResponseList(mDNS
*const m
, AuthRecord
*ResponseRecords
, AuthRecord
***nrpp
, const mDNSInterfaceID InterfaceID
)
2826 AuthRecord
*rr
, *rr2
;
2827 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // For each record we plan to put
2829 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
2830 // later in the "for" loop, and we will follow further "additional" links then.)
2831 if (rr
->Additional1
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional1
, InterfaceID
))
2832 AddRecordToResponseList(nrpp
, rr
->Additional1
, rr
);
2834 if (rr
->Additional2
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional2
, InterfaceID
))
2835 AddRecordToResponseList(nrpp
, rr
->Additional2
, rr
);
2837 // For SRV records, automatically add the Address record(s) for the target host
2838 if (rr
->resrec
.rrtype
== kDNSType_SRV
)
2839 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
2840 if (RRTypeIsAddressType(rr2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
2841 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceID
) && // ... which are valid for answer ...
2842 rr
->resrec
.rdatahash
== rr2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
2843 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, rr2
->resrec
.name
))
2844 AddRecordToResponseList(nrpp
, rr2
, rr
);
2848 mDNSlocal
void SendDelayedUnicastResponse(mDNS
*const m
, const mDNSAddr
*const dest
, const mDNSInterfaceID InterfaceID
)
2851 AuthRecord
*ResponseRecords
= mDNSNULL
;
2852 AuthRecord
**nrp
= &ResponseRecords
;
2854 // Make a list of all our records that need to be unicast to this destination
2855 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2857 // If we find we can no longer unicast this answer, clear ImmedUnicast
2858 if (rr
->ImmedAnswer
== mDNSInterfaceMark
||
2859 mDNSSameIPv4Address(rr
->v4Requester
, onesIPv4Addr
) ||
2860 mDNSSameIPv6Address(rr
->v6Requester
, onesIPv6Addr
) )
2861 rr
->ImmedUnicast
= mDNSfalse
;
2863 if (rr
->ImmedUnicast
&& rr
->ImmedAnswer
== InterfaceID
)
2864 if ((dest
->type
== mDNSAddrType_IPv4
&& mDNSSameIPv4Address(rr
->v4Requester
, dest
->ip
.v4
)) ||
2865 (dest
->type
== mDNSAddrType_IPv6
&& mDNSSameIPv6Address(rr
->v6Requester
, dest
->ip
.v6
)))
2867 rr
->ImmedAnswer
= mDNSNULL
; // Clear the state fields
2868 rr
->ImmedUnicast
= mDNSfalse
;
2869 rr
->v4Requester
= zerov4Addr
;
2870 rr
->v6Requester
= zerov6Addr
;
2871 if (rr
->NextResponse
== mDNSNULL
&& nrp
!= &rr
->NextResponse
) // rr->NR_AnswerTo
2872 { rr
->NR_AnswerTo
= (mDNSu8
*)~0; *nrp
= rr
; nrp
= &rr
->NextResponse
; }
2876 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
2878 while (ResponseRecords
)
2880 mDNSu8
*responseptr
= m
->omsg
.data
;
2882 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
2884 // Put answers in the packet
2885 while (ResponseRecords
&& ResponseRecords
->NR_AnswerTo
)
2887 rr
= ResponseRecords
;
2888 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2889 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
2890 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
2891 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
2892 if (!newptr
&& m
->omsg
.h
.numAnswers
) break; // If packet full, send it now
2893 if (newptr
) responseptr
= newptr
;
2894 ResponseRecords
= rr
->NextResponse
;
2895 rr
->NextResponse
= mDNSNULL
;
2896 rr
->NR_AnswerTo
= mDNSNULL
;
2897 rr
->NR_AdditionalTo
= mDNSNULL
;
2898 rr
->RequireGoodbye
= mDNStrue
;
2901 // Add additionals, if there's space
2902 while (ResponseRecords
&& !ResponseRecords
->NR_AnswerTo
)
2904 rr
= ResponseRecords
;
2905 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2906 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
2907 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
2908 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
2910 if (newptr
) responseptr
= newptr
;
2911 if (newptr
&& m
->omsg
.h
.numAnswers
) rr
->RequireGoodbye
= mDNStrue
;
2912 else if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) rr
->ImmedAnswer
= mDNSInterfaceMark
;
2913 ResponseRecords
= rr
->NextResponse
;
2914 rr
->NextResponse
= mDNSNULL
;
2915 rr
->NR_AnswerTo
= mDNSNULL
;
2916 rr
->NR_AdditionalTo
= mDNSNULL
;
2919 if (m
->omsg
.h
.numAnswers
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, mDNSInterface_Any
, dest
, MulticastDNSPort
, -1, mDNSNULL
);
2923 mDNSlocal
void CompleteDeregistration(mDNS
*const m
, AuthRecord
*rr
)
2925 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal()
2926 // that it should go ahead and immediately dispose of this registration
2927 rr
->resrec
.RecordType
= kDNSRecordTypeShared
;
2928 rr
->RequireGoodbye
= mDNSfalse
;
2929 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
); // Don't touch rr after this
2932 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
2933 // the record list and/or question list.
2934 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2935 mDNSlocal
void DiscardDeregistrations(mDNS
*const m
)
2937 if (m
->CurrentRecord
) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
2938 m
->CurrentRecord
= m
->ResourceRecords
;
2940 while (m
->CurrentRecord
)
2942 AuthRecord
*rr
= m
->CurrentRecord
;
2943 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
2944 CompleteDeregistration(m
, rr
); // Don't touch rr after this
2946 m
->CurrentRecord
= rr
->next
;
2950 mDNSlocal
void GrantUpdateCredit(AuthRecord
*rr
)
2952 if (++rr
->UpdateCredits
>= kMaxUpdateCredits
) rr
->NextUpdateCredit
= 0;
2953 else rr
->NextUpdateCredit
= NonZeroTime(rr
->NextUpdateCredit
+ kUpdateCreditRefreshInterval
);
2956 // Note about acceleration of announcements to facilitate automatic coalescing of
2957 // multiple independent threads of announcements into a single synchronized thread:
2958 // The announcements in the packet may be at different stages of maturity;
2959 // One-second interval, two-second interval, four-second interval, and so on.
2960 // After we've put in all the announcements that are due, we then consider
2961 // whether there are other nearly-due announcements that are worth accelerating.
2962 // To be eligible for acceleration, a record MUST NOT be older (further along
2963 // its timeline) than the most mature record we've already put in the packet.
2964 // In other words, younger records can have their timelines accelerated to catch up
2965 // with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
2966 // Older records cannot have their timelines accelerated; this would just widen
2967 // the gap between them and their younger bretheren and get them even more out of sync.
2969 // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
2970 // the record list and/or question list.
2971 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2972 mDNSlocal
void SendResponses(mDNS
*const m
)
2975 AuthRecord
*rr
, *r2
;
2976 mDNSs32 maxExistingAnnounceInterval
= 0;
2977 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
2979 m
->NextScheduledResponse
= m
->timenow
+ 0x78000000;
2981 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2982 if (rr
->ImmedUnicast
)
2984 mDNSAddr v4
= { mDNSAddrType_IPv4
, {{{0}}} };
2985 mDNSAddr v6
= { mDNSAddrType_IPv6
, {{{0}}} };
2986 v4
.ip
.v4
= rr
->v4Requester
;
2987 v6
.ip
.v6
= rr
->v6Requester
;
2988 if (!mDNSIPv4AddressIsZero(rr
->v4Requester
)) SendDelayedUnicastResponse(m
, &v4
, rr
->ImmedAnswer
);
2989 if (!mDNSIPv6AddressIsZero(rr
->v6Requester
)) SendDelayedUnicastResponse(m
, &v6
, rr
->ImmedAnswer
);
2990 if (rr
->ImmedUnicast
)
2992 LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m
, rr
));
2993 rr
->ImmedUnicast
= mDNSfalse
;
2998 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
3001 // Run through our list of records, and decide which ones we're going to announce on all interfaces
3002 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3004 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
3005 if (TimeToAnnounceThisRecord(rr
, m
->timenow
) && ResourceRecordIsValidAnswer(rr
))
3007 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
3008 if (maxExistingAnnounceInterval
< rr
->ThisAPInterval
)
3009 maxExistingAnnounceInterval
= rr
->ThisAPInterval
;
3010 if (rr
->UpdateBlocked
) rr
->UpdateBlocked
= 0;
3014 // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
3015 // Eligible records that are more than half-way to their announcement time are accelerated
3016 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3017 if ((rr
->resrec
.InterfaceID
&& rr
->ImmedAnswer
) ||
3018 (rr
->ThisAPInterval
<= maxExistingAnnounceInterval
&&
3019 TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2) &&
3020 ResourceRecordIsValidAnswer(rr
)))
3021 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
3023 // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
3024 // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
3025 // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
3026 // then all that means is that it won't get sent -- which would not be the end of the world.
3027 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3028 if (rr
->ImmedAnswer
&& rr
->resrec
.rrtype
== kDNSType_SRV
)
3029 for (r2
=m
->ResourceRecords
; r2
; r2
=r2
->next
) // Scan list of resource records
3030 if (RRTypeIsAddressType(r2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
3031 ResourceRecordIsValidAnswer(r2
) && // ... which are valid for answer ...
3032 rr
->LastMCTime
- r2
->LastMCTime
>= 0 && // ... which we have not sent recently ...
3033 rr
->resrec
.rdatahash
== r2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
3034 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, r2
->resrec
.name
) &&
3035 (rr
->ImmedAnswer
== mDNSInterfaceMark
|| rr
->ImmedAnswer
== r2
->resrec
.InterfaceID
))
3036 r2
->ImmedAdditional
= r2
->resrec
.InterfaceID
; // ... then mark this address record for sending too
3038 // If there's a record which is supposed to be unique that we're going to send, then make sure that we give
3039 // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
3040 // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
3041 // record, then other RRSet members that have not been sent recently will get flushed out of client caches.
3042 // -- 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
3043 // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
3044 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3045 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3047 if (rr
->ImmedAnswer
) // If we're sending this as answer, see that its whole RRSet is similarly marked
3049 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
3050 if (ResourceRecordIsValidAnswer(r2
))
3051 if (r2
->ImmedAnswer
!= mDNSInterfaceMark
&& r2
->ImmedAnswer
!= rr
->ImmedAnswer
&& SameResourceRecordSignature(&r2
->resrec
, &rr
->resrec
))
3052 r2
->ImmedAnswer
= rr
->ImmedAnswer
;
3054 else if (rr
->ImmedAdditional
) // If we're sending this as additional, see that its whole RRSet is similarly marked
3056 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
3057 if (ResourceRecordIsValidAnswer(r2
))
3058 if (r2
->ImmedAdditional
!= rr
->ImmedAdditional
&& SameResourceRecordSignature(&r2
->resrec
, &rr
->resrec
))
3059 r2
->ImmedAdditional
= rr
->ImmedAdditional
;
3063 // Now set SendRNow state appropriately
3064 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3066 if (rr
->ImmedAnswer
== mDNSInterfaceMark
) // Sending this record on all appropriate interfaces
3068 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
3069 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional if sending as answer
3070 rr
->LastMCTime
= m
->timenow
;
3071 rr
->LastMCInterface
= rr
->ImmedAnswer
;
3072 // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
3073 if (TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2))
3075 rr
->AnnounceCount
--;
3076 rr
->ThisAPInterval
*= 2;
3077 rr
->LastAPTime
= m
->timenow
;
3078 if (rr
->LastAPTime
+ rr
->ThisAPInterval
- rr
->AnnounceUntil
>= 0) rr
->AnnounceCount
= 0;
3079 debugf("Announcing %##s (%s) %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->AnnounceCount
);
3082 else if (rr
->ImmedAnswer
) // Else, just respond to a single query on single interface:
3084 rr
->SendRNow
= rr
->ImmedAnswer
; // Just respond on that interface
3085 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional too
3086 rr
->LastMCTime
= m
->timenow
;
3087 rr
->LastMCInterface
= rr
->ImmedAnswer
;
3089 SetNextAnnounceProbeTime(m
, rr
);
3090 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
3094 // *** 2. Loop through interface list, sending records as appropriate
3100 int numAnnounce
= 0;
3102 mDNSu8
*responseptr
= m
->omsg
.data
;
3104 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
3106 // First Pass. Look for:
3107 // 1. Deregistering records that need to send their goodbye packet
3108 // 2. Updated records that need to retract their old data
3109 // 3. Answers and announcements we need to send
3110 // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
3111 // send this packet and then try again.
3112 // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
3113 // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
3114 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3115 if (rr
->SendRNow
== intf
->InterfaceID
)
3117 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
3119 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
3120 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3122 responseptr
= newptr
;
3124 else if (rr
->NewRData
) // If we have new data for this record
3126 RData
*OldRData
= rr
->resrec
.rdata
;
3127 mDNSu16 oldrdlength
= rr
->resrec
.rdlength
;
3128 // See if we should send a courtesy "goodbye" for the old data before we replace it.
3129 if (ResourceRecordIsValidAnswer(rr
) && rr
->RequireGoodbye
)
3131 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
3132 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3134 responseptr
= newptr
;
3136 // Now try to see if we can fit the update in the same packet (not fatal if we can't)
3137 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
);
3138 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3139 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3140 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
3141 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3142 if (newptr
) responseptr
= newptr
;
3143 SetNewRData(&rr
->resrec
, OldRData
, oldrdlength
);
3147 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3148 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3149 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, m
->SleepState
? 0 : rr
->resrec
.rroriginalttl
);
3150 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3151 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3152 rr
->RequireGoodbye
= (mDNSu8
) (!m
->SleepState
);
3153 if (rr
->LastAPTime
== m
->timenow
) numAnnounce
++; else numAnswer
++;
3154 responseptr
= newptr
;
3156 // If sending on all interfaces, go to next interface; else we're finished now
3157 if (rr
->ImmedAnswer
== mDNSInterfaceMark
&& rr
->resrec
.InterfaceID
== mDNSInterface_Any
)
3158 rr
->SendRNow
= GetNextActiveInterfaceID(intf
);
3160 rr
->SendRNow
= mDNSNULL
;
3163 // Second Pass. Add additional records, if there's space.
3164 newptr
= responseptr
;
3165 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3166 if (rr
->ImmedAdditional
== intf
->InterfaceID
)
3167 if (ResourceRecordIsValidAnswer(rr
))
3169 // If we have at least one answer already in the packet, then plan to add additionals too
3170 mDNSBool SendAdditional
= (m
->omsg
.h
.numAnswers
> 0);
3172 // If we're not planning to send any additionals, but this record is a unique one, then
3173 // make sure we haven't already sent any other members of its RRSet -- if we have, then they
3174 // will have had the cache flush bit set, so now we need to finish the job and send the rest.
3175 if (!SendAdditional
&& (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
))
3177 const AuthRecord
*a
;
3178 for (a
= m
->ResourceRecords
; a
; a
=a
->next
)
3179 if (a
->LastMCTime
== m
->timenow
&&
3180 a
->LastMCInterface
== intf
->InterfaceID
&&
3181 SameResourceRecordSignature(&a
->resrec
, &rr
->resrec
)) { SendAdditional
= mDNStrue
; break; }
3183 if (!SendAdditional
) // If we don't want to send this after all,
3184 rr
->ImmedAdditional
= mDNSNULL
; // then cancel its ImmedAdditional field
3185 else if (newptr
) // Else, try to add it if we can
3187 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3188 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3189 newptr
= PutResourceRecord(&m
->omsg
, newptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
3190 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3193 responseptr
= newptr
;
3194 rr
->ImmedAdditional
= mDNSNULL
;
3195 rr
->RequireGoodbye
= mDNStrue
;
3196 // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
3197 // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
3198 // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
3199 // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
3200 rr
->LastMCTime
= m
->timenow
;
3201 rr
->LastMCInterface
= intf
->InterfaceID
;
3206 if (m
->omsg
.h
.numAnswers
> 0 || m
->omsg
.h
.numAdditionals
)
3208 debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
3209 numDereg
, numDereg
== 1 ? "" : "s",
3210 numAnnounce
, numAnnounce
== 1 ? "" : "s",
3211 numAnswer
, numAnswer
== 1 ? "" : "s",
3212 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s", intf
->InterfaceID
);
3213 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, -1, mDNSNULL
);
3214 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, -1, mDNSNULL
);
3215 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
3216 if (++pktcount
>= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount
); break; }
3217 // There might be more things to send on this interface, so go around one more time and try again.
3219 else // Nothing more to send on this interface; go to next
3221 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
3222 #if MDNS_DEBUGMSGS && 0
3223 const char *const msg
= next
? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
3224 debugf(msg
, intf
, next
);
3231 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3234 if (m
->CurrentRecord
) LogMsg("SendResponses: ERROR m->CurrentRecord already set");
3235 m
->CurrentRecord
= m
->ResourceRecords
;
3236 while (m
->CurrentRecord
)
3238 rr
= m
->CurrentRecord
;
3239 m
->CurrentRecord
= rr
->next
;
3243 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
3244 LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m
, rr
));
3245 rr
->SendRNow
= mDNSNULL
;
3248 if (rr
->ImmedAnswer
)
3250 if (rr
->NewRData
) CompleteRDataUpdate(m
,rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
3252 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
3253 CompleteDeregistration(m
, rr
); // Don't touch rr after this
3256 rr
->ImmedAnswer
= mDNSNULL
;
3257 rr
->ImmedUnicast
= mDNSfalse
;
3258 rr
->v4Requester
= zerov4Addr
;
3259 rr
->v6Requester
= zerov6Addr
;
3263 verbosedebugf("SendResponses: Next in %ld ticks", m
->NextScheduledResponse
- m
->timenow
);
3266 // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
3267 // so we want to be lazy about how frequently we do it.
3268 // 1. If a cache record is currently referenced by *no* active questions,
3269 // then we don't mind expiring it up to a minute late (who will know?)
3270 // 2. Else, if a cache record is due for some of its final expiration queries,
3271 // we'll allow them to be late by up to 2% of the TTL
3272 // 3. Else, if a cache record has completed all its final expiration queries without success,
3273 // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
3274 // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
3275 // so allow at most 1/10 second lateness
3276 #define CacheCheckGracePeriod(RR) ( \
3277 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
3278 ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \
3279 ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \
3280 ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10))
3282 // Note: MUST call SetNextCacheCheckTime any time we change:
3284 // rr->DelayDelivery
3285 // rr->resrec.rroriginalttl
3286 // rr->UnansweredQueries
3287 // rr->CRActiveQuestion
3288 mDNSlocal
void SetNextCacheCheckTime(mDNS
*const m
, CacheRecord
*const rr
)
3290 rr
->NextRequiredQuery
= RRExpireTime(rr
);
3292 // If we have an active question, then see if we want to schedule a refresher query for this record.
3293 // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
3294 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
3296 rr
->NextRequiredQuery
-= TicksTTL(rr
)/20 * (MaxUnansweredQueries
- rr
->UnansweredQueries
);
3297 rr
->NextRequiredQuery
+= mDNSRandom((mDNSu32
)TicksTTL(rr
)/50);
3298 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec",
3299 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), (rr
->NextRequiredQuery
- m
->timenow
) / mDNSPlatformOneSecond
);
3302 if (m
->NextCacheCheck
- (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
)) > 0)
3303 m
->NextCacheCheck
= (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
));
3305 if (rr
->DelayDelivery
)
3306 if (m
->NextCacheCheck
- rr
->DelayDelivery
> 0)
3307 m
->NextCacheCheck
= rr
->DelayDelivery
;
3310 #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
3311 #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
3312 #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
3314 mDNSlocal mStatus
mDNS_Reconfirm_internal(mDNS
*const m
, CacheRecord
*const rr
, mDNSu32 interval
)
3316 if (interval
< kMinimumReconfirmTime
)
3317 interval
= kMinimumReconfirmTime
;
3318 if (interval
> 0x10000000) // Make sure interval doesn't overflow when we multiply by four below
3319 interval
= 0x10000000;
3321 // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
3322 if (RRExpireTime(rr
) - m
->timenow
> (mDNSs32
)((interval
* 4) / 3))
3324 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
3325 interval
+= mDNSRandom(interval
/3);
3326 rr
->TimeRcvd
= m
->timenow
- (mDNSs32
)interval
* 3;
3327 rr
->resrec
.rroriginalttl
= interval
* 4 / mDNSPlatformOneSecond
;
3328 SetNextCacheCheckTime(m
, rr
);
3330 debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr
) - m
->timenow
, CRDisplayString(m
, rr
));
3331 return(mStatus_NoError
);
3334 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
3336 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
3337 // It also appends to the list of known answer records that need to be included,
3338 // and updates the forcast for the size of the known answer section.
3339 mDNSlocal mDNSBool
BuildQuestion(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
, DNSQuestion
*q
,
3340 CacheRecord
***kalistptrptr
, mDNSu32
*answerforecast
)
3342 mDNSBool ucast
= (q
->LargeAnswers
|| q
->RequestUnicast
) && m
->CanReceiveUnicastOn5353
;
3343 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
3344 const mDNSu8
*const limit
= query
->data
+ NormalMaxDNSMessageData
;
3345 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &q
->qname
, q
->qtype
, (mDNSu16
)(q
->qclass
| ucbit
));
3348 debugf("BuildQuestion: No more space in this packet for question %##s", q
->qname
.c
);
3351 else if (newptr
+ *answerforecast
>= limit
)
3353 verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q
->qname
.c
, newptr
+ *answerforecast
- query
->data
);
3354 query
->h
.numQuestions
--;
3359 mDNSu32 forecast
= *answerforecast
;
3360 const mDNSu32 slot
= HashSlot(&q
->qname
);
3361 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
3363 CacheRecord
**ka
= *kalistptrptr
; // Make a working copy of the pointer we're going to update
3365 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
3366 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
3367 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not already in the known answer list
3368 rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
3369 ResourceRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
3370 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
>= 0) // and it is less than half-way to expiry
3372 *ka
= rr
; // Link this record into our known answer chain
3373 ka
= &rr
->NextInKAList
;
3374 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3375 forecast
+= 12 + rr
->resrec
.rdestimate
;
3376 // If we're trying to put more than one question in this packet, and it doesn't fit
3377 // then undo that last question and try again next time
3378 if (query
->h
.numQuestions
> 1 && newptr
+ forecast
>= limit
)
3380 debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
3381 q
->qname
.c
, DNSTypeName(q
->qtype
), newptr
+ forecast
- query
->data
);
3382 query
->h
.numQuestions
--;
3383 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
3384 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
3385 return(mDNSfalse
); // Return false, so we'll try again in the next packet
3389 // Traffic reduction:
3390 // If we already have at least one unique answer in the cache,
3391 // OR we have so many shared answers that the KA list is too big to fit in one packet
3392 // The we suppress queries number 3 and 5:
3393 // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies)
3394 // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally)
3395 // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress)
3396 // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally)
3397 // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress)
3398 // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally)
3399 if (q
->UniqueAnswers
|| newptr
+ forecast
>= limit
)
3400 if (q
->ThisQInterval
== InitialQuestionInterval
* 8 || q
->ThisQInterval
== InitialQuestionInterval
* 32)
3402 query
->h
.numQuestions
--;
3403 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
3404 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
3405 return(mDNStrue
); // Return true: pretend we succeeded, even though we actually suppressed this question
3408 // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
3409 *queryptr
= newptr
; // Update the packet pointer
3410 *answerforecast
= forecast
; // Update the forecast
3411 *kalistptrptr
= ka
; // Update the known answer list pointer
3412 if (ucast
) m
->ExpectUnicastResponse
= m
->timenow
;
3414 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // For every 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 in the known answer list
3417 ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) // which answers our question
3419 rr
->UnansweredQueries
++; // indicate that we're expecting a response
3420 rr
->LastUnansweredTime
= m
->timenow
;
3421 SetNextCacheCheckTime(m
, rr
);
3428 mDNSlocal
void ReconfirmAntecedents(mDNS
*const m
, DNSQuestion
*q
)
3434 FORALL_CACHERECORDS(slot
, cg
, rr
)
3435 if ((target
= GetRRDomainNameTarget(&rr
->resrec
)) && rr
->resrec
.rdatahash
== q
->qnamehash
&& SameDomainName(target
, &q
->qname
))
3436 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
3439 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
3440 mDNSlocal
void ExpireDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
)
3443 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
3446 mDNSlocal
void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
, mDNSInterfaceID InterfaceID
)
3449 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
3452 mDNSlocal mDNSBool
SuppressOnThisInterface(const DupSuppressInfo ds
[DupSuppressInfoSize
], const NetworkInterfaceInfo
* const intf
)
3455 mDNSBool v4
= !intf
->IPv4Available
; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
3456 mDNSBool v6
= !intf
->IPv6Available
; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
3457 for (i
=0; i
<DupSuppressInfoSize
; i
++)
3458 if (ds
[i
].InterfaceID
== intf
->InterfaceID
)
3460 if (ds
[i
].Type
== mDNSAddrType_IPv4
) v4
= mDNStrue
;
3461 else if (ds
[i
].Type
== mDNSAddrType_IPv6
) v6
= mDNStrue
;
3462 if (v4
&& v6
) return(mDNStrue
);
3467 mDNSlocal
int RecordDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 Time
, mDNSInterfaceID InterfaceID
, mDNSs32 Type
)
3471 // See if we have this one in our list somewhere already
3472 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Type
== Type
) break;
3474 // If not, find a slot we can re-use
3475 if (i
>= DupSuppressInfoSize
)
3478 for (j
=1; j
<DupSuppressInfoSize
&& ds
[i
].InterfaceID
; j
++)
3479 if (!ds
[j
].InterfaceID
|| ds
[j
].Time
- ds
[i
].Time
< 0)
3483 // Record the info about this query we saw
3485 ds
[i
].InterfaceID
= InterfaceID
;
3491 mDNSlocal mDNSBool
AccelerateThisQuery(mDNS
*const m
, DNSQuestion
*q
)
3493 // If more than 90% of the way to the query time, we should unconditionally accelerate it
3494 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/10))
3497 // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
3498 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/2))
3500 // We forecast: qname (n) type (2) class (2)
3501 mDNSu32 forecast
= (mDNSu32
)DomainNameLength(&q
->qname
) + 4;
3502 const mDNSu32 slot
= HashSlot(&q
->qname
);
3503 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
3505 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
3506 if (rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
3507 ResourceRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
3508 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
>= 0 && // and it is less than half-way to expiry
3509 rr
->NextRequiredQuery
- (m
->timenow
+ q
->ThisQInterval
) > 0)// and we'll ask at least once again before NextRequiredQuery
3511 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3512 forecast
+= 12 + rr
->resrec
.rdestimate
;
3513 if (forecast
>= 512) return(mDNSfalse
); // If this would add 512 bytes or more to the packet, don't accelerate
3521 // How Standard Queries are generated:
3522 // 1. The Question Section contains the question
3523 // 2. The Additional Section contains answers we already know, to suppress duplicate responses
3525 // How Probe Queries are generated:
3526 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
3527 // if some other host is already using *any* records with this name, we want to know about it.
3528 // 2. The Authority Section contains the proposed values we intend to use for one or more
3529 // of our records with that name (analogous to the Update section of DNS Update packets)
3530 // because if some other host is probing at the same time, we each want to know what the other is
3531 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
3533 mDNSlocal
void SendQueries(mDNS
*const m
)
3537 // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
3538 mDNSs32 maxExistingQuestionInterval
= 0;
3539 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
3540 CacheRecord
*KnownAnswerList
= mDNSNULL
;
3542 // 1. If time for a query, work out what we need to do
3543 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3548 m
->NextScheduledQuery
= m
->timenow
+ 0x78000000;
3550 // We're expecting to send a query anyway, so see if any expiring cache records are close enough
3551 // to their NextRequiredQuery to be worth batching them together with this one
3552 FORALL_CACHERECORDS(slot
, cg
, rr
)
3553 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
3554 if (m
->timenow
+ TicksTTL(rr
)/50 - rr
->NextRequiredQuery
>= 0)
3556 q
= rr
->CRActiveQuestion
;
3557 ExpireDupSuppressInfoOnInterface(q
->DupSuppress
, m
->timenow
- TicksTTL(rr
)/20, rr
->resrec
.InterfaceID
);
3558 if (q
->Target
.type
) q
->SendQNow
= mDNSInterfaceMark
; // If unicast query, mark it
3559 else if (q
->SendQNow
== mDNSNULL
) q
->SendQNow
= rr
->resrec
.InterfaceID
;
3560 else if (q
->SendQNow
!= rr
->resrec
.InterfaceID
) q
->SendQNow
= mDNSInterfaceMark
;
3563 // Scan our list of questions to see which *unicast* queries need to be sent
3564 for (q
= m
->Questions
; q
; q
=q
->next
)
3565 if (q
->Target
.type
&& (q
->SendQNow
|| TimeToSendThisQuestion(q
, m
->timenow
)))
3567 mDNSu8
*qptr
= m
->omsg
.data
;
3568 const mDNSu8
*const limit
= m
->omsg
.data
+ sizeof(m
->omsg
.data
);
3569 InitializeDNSMessage(&m
->omsg
.h
, q
->TargetQID
, QueryFlags
);
3570 qptr
= putQuestion(&m
->omsg
, qptr
, limit
, &q
->qname
, q
->qtype
, q
->qclass
);
3571 mDNSSendDNSMessage(m
, &m
->omsg
, qptr
, mDNSInterface_Any
, &q
->Target
, q
->TargetPort
, -1, mDNSNULL
);
3572 q
->ThisQInterval
*= 2;
3573 if (q
->ThisQInterval
> MaxQuestionInterval
)
3574 q
->ThisQInterval
= MaxQuestionInterval
;
3575 q
->LastQTime
= m
->timenow
;
3576 q
->LastQTxTime
= m
->timenow
;
3577 q
->RecentAnswerPkts
= 0;
3578 q
->SendQNow
= mDNSNULL
;
3579 m
->ExpectUnicastResponse
= m
->timenow
;
3582 // Scan our list of questions to see which *multicast* queries we're definitely going to send
3583 for (q
= m
->Questions
; q
; q
=q
->next
)
3584 if (!q
->Target
.type
&& TimeToSendThisQuestion(q
, m
->timenow
))
3586 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
3587 if (maxExistingQuestionInterval
< q
->ThisQInterval
)
3588 maxExistingQuestionInterval
= q
->ThisQInterval
;
3591 // Scan our list of questions
3592 // (a) to see if there are any more that are worth accelerating, and
3593 // (b) to update the state variables for *all* the questions we're going to send
3594 for (q
= m
->Questions
; q
; q
=q
->next
)
3597 (!q
->Target
.type
&& ActiveQuestion(q
) && q
->ThisQInterval
<= maxExistingQuestionInterval
&& AccelerateThisQuery(m
,q
)))
3599 // If at least halfway to next query time, advance to next interval
3600 // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval
3601 if (m
->timenow
- (q
->LastQTime
+ q
->ThisQInterval
/2) >= 0)
3603 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
3604 q
->ThisQInterval
*= 2;
3605 if (q
->ThisQInterval
> MaxQuestionInterval
)
3606 q
->ThisQInterval
= MaxQuestionInterval
;
3607 else if (q
->CurrentAnswers
== 0 && q
->ThisQInterval
== InitialQuestionInterval
* 8)
3609 debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q
->qname
.c
, DNSTypeName(q
->qtype
));
3610 ReconfirmAntecedents(m
, q
); // If sending third query, and no answers yet, time to begin doubting the source
3614 // Mark for sending. (If no active interfaces, then don't even try.)
3615 q
->SendOnAll
= (q
->SendQNow
== mDNSInterfaceMark
);
3618 q
->SendQNow
= !intf
? mDNSNULL
: (q
->InterfaceID
) ? q
->InterfaceID
: intf
->InterfaceID
;
3619 q
->LastQTime
= m
->timenow
;
3622 // If we recorded a duplicate suppression for this question less than half an interval ago,
3623 // then we consider it recent enough that we don't need to do an identical query ourselves.
3624 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
- q
->ThisQInterval
/2);
3626 q
->LastQTxTime
= m
->timenow
;
3627 q
->RecentAnswerPkts
= 0;
3628 if (q
->RequestUnicast
) q
->RequestUnicast
--;
3630 // For all questions (not just the ones we're sending) check what the next scheduled event will be
3631 SetNextQueryTime(m
,q
);
3635 // 2. Scan our authoritative RR list to see what probes we might need to send
3636 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3638 m
->NextScheduledProbe
= m
->timenow
+ 0x78000000;
3640 if (m
->CurrentRecord
) LogMsg("SendQueries: ERROR m->CurrentRecord already set");
3641 m
->CurrentRecord
= m
->ResourceRecords
;
3642 while (m
->CurrentRecord
)
3644 AuthRecord
*rr
= m
->CurrentRecord
;
3645 m
->CurrentRecord
= rr
->next
;
3646 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
) // For all records that are still probing...
3648 // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
3649 if (m
->timenow
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) < 0)
3651 SetNextAnnounceProbeTime(m
, rr
);
3653 // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
3654 else if (rr
->ProbeCount
)
3656 // Mark for sending. (If no active interfaces, then don't even try.)
3657 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
3658 rr
->LastAPTime
= m
->timenow
;
3660 SetNextAnnounceProbeTime(m
, rr
);
3662 // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
3666 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
3667 rr
->ThisAPInterval
= DefaultAnnounceIntervalForTypeUnique
;
3668 rr
->LastAPTime
= m
->timenow
- DefaultAnnounceIntervalForTypeUnique
;
3669 SetNextAnnounceProbeTime(m
, rr
);
3670 // If we have any records on our duplicate list that match this one, they have now also completed probing
3671 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
)
3672 if (r2
->resrec
.RecordType
== kDNSRecordTypeUnique
&& RecordIsLocalDuplicate(r2
, rr
))
3674 AcknowledgeRecord(m
, rr
);
3678 m
->CurrentRecord
= m
->DuplicateRecords
;
3679 while (m
->CurrentRecord
)
3681 AuthRecord
*rr
= m
->CurrentRecord
;
3682 m
->CurrentRecord
= rr
->next
;
3683 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& rr
->ProbeCount
== 0)
3684 AcknowledgeRecord(m
, rr
);
3688 // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
3692 mDNSu8
*queryptr
= m
->omsg
.data
;
3693 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, QueryFlags
);
3694 if (KnownAnswerList
) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet");
3695 if (!KnownAnswerList
)
3697 // Start a new known-answer list
3698 CacheRecord
**kalistptr
= &KnownAnswerList
;
3699 mDNSu32 answerforecast
= 0;
3701 // Put query questions in this packet
3702 for (q
= m
->Questions
; q
; q
=q
->next
)
3703 if (q
->SendQNow
== intf
->InterfaceID
)
3705 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
3706 SuppressOnThisInterface(q
->DupSuppress
, intf
) ? "Suppressing" : "Putting ",
3707 q
->qname
.c
, DNSTypeName(q
->qtype
), queryptr
- m
->omsg
.data
, queryptr
+ answerforecast
- m
->omsg
.data
);
3708 // If we're suppressing this question, or we successfully put it, update its SendQNow state
3709 if (SuppressOnThisInterface(q
->DupSuppress
, intf
) ||
3710 BuildQuestion(m
, &m
->omsg
, &queryptr
, q
, &kalistptr
, &answerforecast
))
3711 q
->SendQNow
= (q
->InterfaceID
|| !q
->SendOnAll
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
3714 // Put probe questions in this packet
3715 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3716 if (rr
->SendRNow
== intf
->InterfaceID
)
3718 mDNSBool ucast
= (rr
->ProbeCount
>= DefaultProbeCountForTypeUnique
-1) && m
->CanReceiveUnicastOn5353
;
3719 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
3720 const mDNSu8
*const limit
= m
->omsg
.data
+ ((m
->omsg
.h
.numQuestions
) ? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
3721 mDNSu8
*newptr
= putQuestion(&m
->omsg
, queryptr
, limit
, rr
->resrec
.name
, kDNSQType_ANY
, (mDNSu16
)(rr
->resrec
.rrclass
| ucbit
));
3722 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3723 mDNSu32 forecast
= answerforecast
+ 12 + rr
->resrec
.rdestimate
;
3724 if (newptr
&& newptr
+ forecast
< limit
)
3727 answerforecast
= forecast
;
3728 rr
->SendRNow
= (rr
->resrec
.InterfaceID
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
3729 rr
->IncludeInProbe
= mDNStrue
;
3730 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->ProbeCount
);
3734 verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3735 m
->omsg
.h
.numQuestions
--;
3740 // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
3741 while (KnownAnswerList
)
3743 CacheRecord
*rr
= KnownAnswerList
;
3744 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
3745 mDNSu8
*newptr
= PutResourceRecordTTL(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, rr
->resrec
.rroriginalttl
- SecsSinceRcvd
);
3748 verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), queryptr
- m
->omsg
.data
, newptr
- m
->omsg
.data
);
3750 KnownAnswerList
= rr
->NextInKAList
;
3751 rr
->NextInKAList
= mDNSNULL
;
3755 // If we ran out of space and we have more than one question in the packet, that's an error --
3756 // we shouldn't have put more than one question if there was a risk of us running out of space.
3757 if (m
->omsg
.h
.numQuestions
> 1)
3758 LogMsg("SendQueries: Put %d answers; No more space for known answers", m
->omsg
.h
.numAnswers
);
3759 m
->omsg
.h
.flags
.b
[0] |= kDNSFlag0_TC
;
3764 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3765 if (rr
->IncludeInProbe
)
3767 mDNSu8
*newptr
= PutResourceRecord(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAuthorities
, &rr
->resrec
);
3768 rr
->IncludeInProbe
= mDNSfalse
;
3769 if (newptr
) queryptr
= newptr
;
3770 else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?",
3771 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3774 if (queryptr
> m
->omsg
.data
)
3776 if ((m
->omsg
.h
.flags
.b
[0] & kDNSFlag0_TC
) && m
->omsg
.h
.numQuestions
> 1)
3777 LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m
->omsg
.h
.numQuestions
);
3778 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
3779 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
3780 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
3781 m
->omsg
.h
.numAuthorities
, m
->omsg
.h
.numAuthorities
== 1 ? "" : "s", intf
->InterfaceID
);
3782 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, -1, mDNSNULL
);
3783 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, -1, mDNSNULL
);
3784 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
3785 if (++pktcount
>= 1000)
3786 { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount
); break; }
3787 // There might be more records left in the known answer list, or more questions to send
3788 // on this interface, so go around one more time and try again.
3790 else // Nothing more to send on this interface; go to next
3792 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
3793 #if MDNS_DEBUGMSGS && 0
3794 const char *const msg
= next
? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p";
3795 debugf(msg
, intf
, next
);
3801 // Final sanity check for debugging purposes
3804 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3807 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
3808 LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m
, rr
));
3809 rr
->SendRNow
= mDNSNULL
;
3814 // ***************************************************************************
3815 #if COMPILER_LIKES_PRAGMA_MARK
3817 #pragma mark - RR List Management & Task Management
3820 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
3821 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3822 mDNSlocal
void AnswerQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, CacheRecord
*rr
, mDNSBool AddRecord
)
3824 verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
3825 q
->CurrentAnswers
, AddRecord
? "Add" : "Rmv", rr
->resrec
.rroriginalttl
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3827 // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue)
3828 // may be called twice, once when the record is received, and again when it's time to notify local clients.
3829 // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
3831 rr
->LastUsed
= m
->timenow
;
3832 if (ActiveQuestion(q
) && rr
->CRActiveQuestion
!= q
)
3834 if (!rr
->CRActiveQuestion
) m
->rrcache_active
++; // If not previously active, increment rrcache_active count
3835 rr
->CRActiveQuestion
= q
; // We know q is non-null
3836 SetNextCacheCheckTime(m
, rr
);
3840 // (a) a no-cache add, where we've already done at least one 'QM' query, or
3841 // (b) a normal add, where we have at least one unique-type answer,
3842 // then there's no need to keep polling the network.
3843 // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
3844 if ((AddRecord
== 2 && !q
->RequestUnicast
) ||
3845 (AddRecord
== 1 && (q
->ExpectUnique
|| (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))))
3846 if (ActiveQuestion(q
))
3848 q
->LastQTime
= m
->timenow
;
3849 q
->LastQTxTime
= m
->timenow
;
3850 q
->RecentAnswerPkts
= 0;
3851 q
->ThisQInterval
= MaxQuestionInterval
;
3852 q
->RequestUnicast
= mDNSfalse
;
3855 if (rr
->DelayDelivery
) return; // We'll come back later when CacheRecordDeferredAdd() calls us
3857 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
3858 if (q
->QuestionCallback
)
3859 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
3860 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
3861 // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function
3862 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
3863 // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv()
3864 // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions
3865 // being deleted out from under them.
3868 mDNSlocal
void CacheRecordDeferredAdd(mDNS
*const m
, CacheRecord
*rr
)
3870 rr
->DelayDelivery
= 0;
3871 if (m
->CurrentQuestion
) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set");
3872 m
->CurrentQuestion
= m
->Questions
;
3873 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3875 DNSQuestion
*q
= m
->CurrentQuestion
;
3876 m
->CurrentQuestion
= q
->next
;
3877 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3878 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3880 m
->CurrentQuestion
= mDNSNULL
;
3883 mDNSlocal mDNSs32
CheckForSoonToExpireRecords(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const mDNSu32 slot
)
3885 const mDNSs32 threshhold
= m
->timenow
+ mDNSPlatformOneSecond
; // See if there are any records expiring within one second
3886 const mDNSs32 start
= m
->timenow
- 0x10000000;
3887 mDNSs32 delay
= start
;
3888 CacheGroup
*cg
= CacheGroupForName(m
, slot
, namehash
, name
);
3890 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3891 if (rr
->resrec
.namehash
== namehash
&& SameDomainName(rr
->resrec
.name
, name
))
3892 if (threshhold
- RRExpireTime(rr
) >= 0) // If we have records about to expire within a second
3893 if (delay
- RRExpireTime(rr
) < 0) // then delay until after they've been deleted
3894 delay
= RRExpireTime(rr
);
3895 if (delay
- start
> 0) return(delay
? delay
: 1); // Make sure we return non-zero if we want to delay
3899 // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
3900 // If new questions are created as a result of invoking client callbacks, they will be added to
3901 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3902 // rr is a new CacheRecord just received into our cache
3903 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
3904 // NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback,
3905 // which may change the record list and/or question list.
3906 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3907 mDNSlocal
void CacheRecordAdd(mDNS
*const m
, CacheRecord
*rr
)
3909 if (m
->CurrentQuestion
) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3910 m
->CurrentQuestion
= m
->Questions
;
3911 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3913 DNSQuestion
*q
= m
->CurrentQuestion
;
3914 m
->CurrentQuestion
= q
->next
;
3915 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3917 // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last
3918 // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start.
3919 // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less,
3920 // there's not much benefit accelerating because we will anyway send another query within a few seconds.
3921 // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines.
3922 if (q
->LastAnswerPktNum
!= m
->PktNum
)
3924 q
->LastAnswerPktNum
= m
->PktNum
;
3925 if (ActiveQuestion(q
) && ++q
->RecentAnswerPkts
>= 10 &&
3926 q
->ThisQInterval
> InitialQuestionInterval
*32 && m
->timenow
- q
->LastQTxTime
< mDNSPlatformOneSecond
)
3928 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
3929 q
->qname
.c
, DNSTypeName(q
->qtype
));
3930 q
->LastQTime
= m
->timenow
- InitialQuestionInterval
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*4);
3931 q
->ThisQInterval
= InitialQuestionInterval
;
3932 SetNextQueryTime(m
,q
);
3935 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->resrec
.rroriginalttl
);
3936 q
->CurrentAnswers
++;
3937 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
3938 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
3939 if (q
->CurrentAnswers
> 4000)
3941 static int msgcount
= 0;
3942 if (msgcount
++ < 10)
3943 LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
3944 q
->qname
.c
, DNSTypeName(q
->qtype
), q
->CurrentAnswers
);
3945 rr
->resrec
.rroriginalttl
= 1;
3946 rr
->UnansweredQueries
= MaxUnansweredQueries
;
3948 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3949 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3952 m
->CurrentQuestion
= mDNSNULL
;
3955 // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
3956 // If new questions are created as a result of invoking client callbacks, they will be added to
3957 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3958 // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
3959 // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
3960 // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
3961 // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
3962 // NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback,
3963 // which may change the record list and/or question list.
3964 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3965 mDNSlocal
void NoCacheAnswer(mDNS
*const m
, CacheRecord
*rr
)
3967 LogMsg("No cache space: Delivering non-cached result for %##s", m
->rec
.r
.resrec
.name
->c
);
3968 if (m
->CurrentQuestion
) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3969 m
->CurrentQuestion
= m
->Questions
;
3970 while (m
->CurrentQuestion
)
3972 DNSQuestion
*q
= m
->CurrentQuestion
;
3973 m
->CurrentQuestion
= q
->next
;
3974 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3975 AnswerQuestionWithResourceRecord(m
, q
, rr
, 2); // Value '2' indicates "don't expect 'remove' events for this"
3976 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3978 m
->CurrentQuestion
= mDNSNULL
;
3981 // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
3982 // If new questions are created as a result of invoking client callbacks, they will be added to
3983 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3984 // rr is an existing cache CacheRecord that just expired and is being deleted
3985 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
3986 // NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback,
3987 // which may change the record list and/or question list.
3988 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3989 mDNSlocal
void CacheRecordRmv(mDNS
*const m
, CacheRecord
*rr
)
3991 if (m
->CurrentQuestion
) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set");
3992 m
->CurrentQuestion
= m
->Questions
;
3993 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3995 DNSQuestion
*q
= m
->CurrentQuestion
;
3996 m
->CurrentQuestion
= q
->next
;
3997 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3999 verbosedebugf("CacheRecordRmv %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4000 if (q
->CurrentAnswers
== 0)
4001 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q
, q
->qname
.c
, DNSTypeName(q
->qtype
));
4004 q
->CurrentAnswers
--;
4005 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
--;
4006 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
--;
4008 if (q
->CurrentAnswers
== 0)
4010 debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q
->qname
.c
, DNSTypeName(q
->qtype
));
4011 ReconfirmAntecedents(m
, q
);
4013 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNSfalse
);
4014 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4017 m
->CurrentQuestion
= mDNSNULL
;
4020 mDNSlocal
void ReleaseCacheEntity(mDNS
*const m
, CacheEntity
*e
)
4022 #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
4024 for (i
=0; i
<sizeof(*e
); i
++) ((char*)e
)[i
] = 0xFF;
4026 e
->next
= m
->rrcache_free
;
4027 m
->rrcache_free
= e
;
4028 m
->rrcache_totalused
--;
4031 mDNSlocal
void ReleaseCacheGroup(mDNS
*const m
, CacheGroup
**cp
)
4033 CacheEntity
*e
= (CacheEntity
*)(*cp
);
4034 //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
4035 if ((*cp
)->rrcache_tail
!= &(*cp
)->members
) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
4036 //if ((*cp)->name != (domainname*)((*cp)->namestorage)) LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
4037 if ((*cp
)->name
!= (domainname
*)((*cp
)->namestorage
)) mDNSPlatformMemFree((*cp
)->name
);
4038 (*cp
)->name
= mDNSNULL
;
4039 *cp
= (*cp
)->next
; // Cut record from list
4040 ReleaseCacheEntity(m
, e
);
4043 mDNSlocal
void ReleaseCacheRecord(mDNS
*const m
, CacheRecord
*r
)
4045 if (r
->resrec
.rdata
&& r
->resrec
.rdata
!= (RData
*)&r
->rdatastorage
) mDNSPlatformMemFree(r
->resrec
.rdata
);
4046 r
->resrec
.rdata
= mDNSNULL
;
4047 ReleaseCacheEntity(m
, (CacheEntity
*)r
);
4050 // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls
4051 // The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records
4052 mDNSlocal
void CheckCacheExpiration(mDNS
*const m
, CacheGroup
*cg
)
4054 CacheRecord
**rp
= &cg
->members
;
4056 if (m
->lock_rrcache
) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
4057 m
->lock_rrcache
= 1;
4061 CacheRecord
*const rr
= *rp
;
4062 mDNSs32 event
= RRExpireTime(rr
);
4063 if (m
->timenow
- event
>= 0) // If expired, delete it
4065 *rp
= rr
->next
; // Cut it from the list
4066 verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m
, rr
));
4067 if (rr
->CRActiveQuestion
) // If this record has one or more active questions, tell them it's going away
4069 CacheRecordRmv(m
, rr
);
4070 m
->rrcache_active
--;
4072 ReleaseCacheRecord(m
, rr
);
4074 else // else, not expired; see if we need to query
4076 if (rr
->DelayDelivery
&& rr
->DelayDelivery
- m
->timenow
> 0)
4077 event
= rr
->DelayDelivery
;
4080 if (rr
->DelayDelivery
) CacheRecordDeferredAdd(m
, rr
);
4081 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
4083 if (m
->timenow
- rr
->NextRequiredQuery
< 0) // If not yet time for next query
4084 event
= rr
->NextRequiredQuery
; // then just record when we want the next query
4085 else // else trigger our question to go out now
4087 // Set NextScheduledQuery to timenow so that SendQueries() will run.
4088 // SendQueries() will see that we have records close to expiration, and send FEQs for them.
4089 m
->NextScheduledQuery
= m
->timenow
;
4090 // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(),
4091 // which will correctly update m->NextCacheCheck for us
4092 event
= m
->timenow
+ 0x3FFFFFFF;
4096 if (m
->NextCacheCheck
- (event
+ CacheCheckGracePeriod(rr
)) > 0)
4097 m
->NextCacheCheck
= (event
+ CacheCheckGracePeriod(rr
));
4101 if (cg
->rrcache_tail
!= rp
) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg
->rrcache_tail
, rp
);
4102 cg
->rrcache_tail
= rp
;
4103 m
->lock_rrcache
= 0;
4106 mDNSlocal
void AnswerNewQuestion(mDNS
*const m
)
4108 mDNSBool ShouldQueryImmediately
= mDNStrue
;
4110 DNSQuestion
*q
= m
->NewQuestions
; // Grab the question we're going to answer
4111 const mDNSu32 slot
= HashSlot(&q
->qname
);
4112 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
4114 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4116 if (cg
) CheckCacheExpiration(m
, cg
);
4117 m
->NewQuestions
= q
->next
; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
4119 if (m
->lock_rrcache
) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
4120 // This should be safe, because calling the client's question callback may cause the
4121 // question list to be modified, but should not ever cause the rrcache list to be modified.
4122 // If the client's question callback deletes the question, then m->CurrentQuestion will
4123 // be advanced, and we'll exit out of the loop
4124 m
->lock_rrcache
= 1;
4125 if (m
->CurrentQuestion
) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set");
4126 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
4128 if (q
->InterfaceID
== mDNSInterface_Any
) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
4130 if (m
->CurrentRecord
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4131 m
->CurrentRecord
= m
->ResourceRecords
;
4132 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
4134 AuthRecord
*rr
= m
->CurrentRecord
;
4135 m
->CurrentRecord
= rr
->next
;
4136 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
4137 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4139 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4140 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4141 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4144 m
->CurrentRecord
= mDNSNULL
;
4147 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4148 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4150 // SecsSinceRcvd is whole number of elapsed seconds, rounded down
4151 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
4152 if (rr
->resrec
.rroriginalttl
<= SecsSinceRcvd
)
4154 LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)",
4155 rr
->resrec
.rroriginalttl
, SecsSinceRcvd
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4156 continue; // Go to next one in loop
4159 // If this record set is marked unique, then that means we can reasonably assume we have the whole set
4160 // -- we don't need to rush out on the network and query immediately to see if there are more answers out there
4161 if ((rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) || (q
->ExpectUnique
))
4162 ShouldQueryImmediately
= mDNSfalse
;
4163 q
->CurrentAnswers
++;
4164 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
4165 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
4166 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4167 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4168 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4170 else if (RRTypeIsAddressType(rr
->resrec
.rrtype
) && RRTypeIsAddressType(q
->qtype
))
4171 if (rr
->resrec
.namehash
== q
->qnamehash
&& SameDomainName(rr
->resrec
.name
, &q
->qname
))
4172 ShouldQueryImmediately
= mDNSfalse
;
4174 if (ShouldQueryImmediately
&& m
->CurrentQuestion
== q
)
4176 q
->ThisQInterval
= InitialQuestionInterval
;
4177 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
4178 m
->NextScheduledQuery
= m
->timenow
;
4180 m
->CurrentQuestion
= mDNSNULL
;
4181 m
->lock_rrcache
= 0;
4184 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any appropriate answers,
4185 // stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions
4186 mDNSlocal
void AnswerNewLocalOnlyQuestion(mDNS
*const m
)
4188 DNSQuestion
*q
= m
->NewLocalOnlyQuestions
; // Grab the question we're going to answer
4189 m
->NewLocalOnlyQuestions
= q
->next
; // Advance NewQuestions to the next (if any)
4191 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4193 if (m
->CurrentQuestion
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set");
4194 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
4196 if (m
->CurrentRecord
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4197 m
->CurrentRecord
= m
->ResourceRecords
;
4198 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
4200 AuthRecord
*rr
= m
->CurrentRecord
;
4201 m
->CurrentRecord
= rr
->next
;
4202 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4204 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4205 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4206 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4210 m
->CurrentQuestion
= mDNSNULL
;
4211 m
->CurrentRecord
= mDNSNULL
;
4214 mDNSlocal CacheEntity
*GetCacheEntity(mDNS
*const m
, const CacheGroup
*const PreserveCG
)
4216 CacheEntity
*e
= mDNSNULL
;
4218 if (m
->lock_rrcache
) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL
); }
4219 m
->lock_rrcache
= 1;
4221 // If we have no free records, ask the client layer to give us some more memory
4222 if (!m
->rrcache_free
&& m
->MainCallback
)
4224 if (m
->rrcache_totalused
!= m
->rrcache_size
)
4225 LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
4226 m
->rrcache_totalused
, m
->rrcache_size
);
4228 // We don't want to be vulnerable to a malicious attacker flooding us with an infinite
4229 // number of bogus records so that we keep growing our cache until the machine runs out of memory.
4230 // To guard against this, if we're actively using less than 1/32 of our cache, then we
4231 // purge all the unused records and recycle them, instead of allocating more memory.
4232 if (m
->rrcache_size
>= 512 && m
->rrcache_size
/ 32 > m
->rrcache_active
)
4233 debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
4234 m
->rrcache_size
, m
->rrcache_active
);
4237 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
4238 m
->MainCallback(m
, mStatus_GrowCache
);
4239 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
4243 // If we still have no free records, recycle all the records we can.
4244 // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
4245 if (!m
->rrcache_free
)
4248 mDNSu32 oldtotalused
= m
->rrcache_totalused
;
4251 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
4253 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
4256 CacheRecord
**rp
= &(*cp
)->members
;
4259 // Records that answer still-active questions are not candidates for recycling
4260 // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
4261 if ((*rp
)->CRActiveQuestion
|| (*rp
)->NextInCFList
)
4265 CacheRecord
*rr
= *rp
;
4266 *rp
= (*rp
)->next
; // Cut record from list
4267 ReleaseCacheRecord(m
, rr
);
4270 if ((*cp
)->rrcache_tail
!= rp
) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot
, (*cp
)->rrcache_tail
, rp
);
4271 (*cp
)->rrcache_tail
= rp
;
4272 if ((*cp
)->members
|| (*cp
)==PreserveCG
) cp
=&(*cp
)->next
;
4273 else ReleaseCacheGroup(m
, cp
);
4277 debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused
, m
->rrcache_totalused
);
4281 if (m
->rrcache_free
) // If there are records in the free list, take one
4283 e
= m
->rrcache_free
;
4284 m
->rrcache_free
= e
->next
;
4285 if (++m
->rrcache_totalused
>= m
->rrcache_report
)
4287 debugf("RR Cache now using %ld objects", m
->rrcache_totalused
);
4288 if (m
->rrcache_report
< 100) m
->rrcache_report
+= 10;
4289 else m
->rrcache_report
+= 100;
4291 mDNSPlatformMemZero(e
, sizeof(*e
));
4294 m
->lock_rrcache
= 0;
4299 mDNSlocal CacheRecord
*GetCacheRecord(mDNS
*const m
, CacheGroup
*cg
, mDNSu16 RDLength
)
4301 CacheRecord
*r
= (CacheRecord
*)GetCacheEntity(m
, cg
);
4304 r
->resrec
.rdata
= (RData
*)&r
->rdatastorage
; // By default, assume we're usually going to be using local storage
4305 if (RDLength
> InlineCacheRDSize
) // If RDLength is too big, allocate extra storage
4307 r
->resrec
.rdata
= (RData
*)mDNSPlatformMemAllocate(sizeofRDataHeader
+ RDLength
);
4308 if (r
->resrec
.rdata
) r
->resrec
.rdata
->MaxRDLength
= r
->resrec
.rdlength
= RDLength
;
4309 else { ReleaseCacheEntity(m
, (CacheEntity
*)r
); r
= mDNSNULL
; }
4315 mDNSlocal CacheGroup
*GetCacheGroup(mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
4317 mDNSu16 namelen
= DomainNameLength(rr
->name
);
4318 CacheGroup
*cg
= (CacheGroup
*)GetCacheEntity(m
, mDNSNULL
);
4319 if (!cg
) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr
->name
->c
); return(mDNSNULL
); }
4320 cg
->next
= m
->rrcache_hash
[slot
];
4321 cg
->namehash
= rr
->namehash
;
4322 cg
->members
= mDNSNULL
;
4323 cg
->rrcache_tail
= &cg
->members
;
4324 cg
->name
= (domainname
*)cg
->namestorage
;
4325 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
4326 if (namelen
> InlineCacheGroupNameSize
) cg
->name
= mDNSPlatformMemAllocate(namelen
);
4329 LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr
->name
->c
);
4330 ReleaseCacheEntity(m
, (CacheEntity
*)cg
);
4333 AssignDomainName(cg
->name
, rr
->name
);
4335 if (CacheGroupForRecord(m
, slot
, rr
)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr
->name
->c
);
4336 m
->rrcache_hash
[slot
] = cg
;
4337 if (CacheGroupForRecord(m
, slot
, rr
) != cg
) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr
->name
->c
);
4342 mDNSlocal
void PurgeCacheResourceRecord(mDNS
*const m
, CacheRecord
*rr
)
4344 // Make sure we mark this record as thoroughly expired -- we don't ever want to give
4345 // a positive answer using an expired record (e.g. from an interface that has gone away).
4346 // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
4347 // summary deletion without giving the proper callback to any questions that are monitoring it.
4348 // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
4349 rr
->TimeRcvd
= m
->timenow
- mDNSPlatformOneSecond
* 60;
4350 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4351 rr
->resrec
.rroriginalttl
= 0;
4352 SetNextCacheCheckTime(m
, rr
);
4355 mDNSexport mDNSs32
mDNS_TimeNow(const mDNS
*const m
)
4358 mDNSPlatformLock(m
);
4361 LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
4362 if (!m
->timenow
) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
4365 if (m
->timenow
) time
= m
->timenow
;
4366 else time
= mDNS_TimeNow_NoLock(m
);
4367 mDNSPlatformUnlock(m
);
4371 mDNSexport mDNSs32
mDNS_Execute(mDNS
*const m
)
4373 mDNS_Lock(m
); // Must grab lock before trying to read m->timenow
4375 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
4379 verbosedebugf("mDNS_Execute");
4380 if (m
->CurrentQuestion
) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
4382 // 1. If we're past the probe suppression time, we can clear it
4383 if (m
->SuppressProbes
&& m
->timenow
- m
->SuppressProbes
>= 0) m
->SuppressProbes
= 0;
4385 // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
4386 if (m
->NumFailedProbes
&& m
->timenow
- m
->ProbeFailTime
>= mDNSPlatformOneSecond
* 10) m
->NumFailedProbes
= 0;
4388 // 3. Purge our cache of stale old records
4389 if (m
->rrcache_size
&& m
->timenow
- m
->NextCacheCheck
>= 0)
4392 m
->NextCacheCheck
= m
->timenow
+ 0x3FFFFFFF;
4393 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
4395 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
4398 CheckCacheExpiration(m
, *cp
);
4399 if ((*cp
)->members
) cp
=&(*cp
)->next
;
4400 else ReleaseCacheGroup(m
, cp
);
4405 // 4. See if we can answer any of our new local questions from the cache
4406 for (i
=0; m
->NewQuestions
&& i
<1000; i
++)
4408 if (m
->NewQuestions
->DelayAnswering
&& m
->timenow
- m
->NewQuestions
->DelayAnswering
< 0) break;
4409 AnswerNewQuestion(m
);
4411 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
4413 for (i
=0; m
->NewLocalOnlyQuestions
&& i
<1000; i
++) AnswerNewLocalOnlyQuestion(m
);
4414 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4416 for (i
=0; i
<1000 && m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
); i
++)
4418 AuthRecord
*rr
= m
->NewLocalRecords
;
4419 m
->NewLocalRecords
= m
->NewLocalRecords
->next
;
4420 AnswerLocalQuestions(m
, rr
, mDNStrue
);
4422 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
4424 // 5. See what packets we need to send
4425 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) DiscardDeregistrations(m
);
4426 else if (m
->SuppressSending
== 0 || m
->timenow
- m
->SuppressSending
>= 0)
4428 // If the platform code is ready, and we're not suppressing packet generation right now
4429 // then send our responses, probes, and questions.
4430 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them
4431 // We send queries next, because there might be final-stage probes that complete their probing here, causing
4432 // them to advance to announcing state, and we want those to be included in any announcements we send out.
4433 // Finally, we send responses, including the previously mentioned records that just completed probing
4434 m
->SuppressSending
= 0;
4436 // 6. Send Query packets. This may cause some probing records to advance to announcing state
4437 if (m
->timenow
- m
->NextScheduledQuery
>= 0 || m
->timenow
- m
->NextScheduledProbe
>= 0) SendQueries(m
);
4438 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
4440 LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
4441 m
->NextScheduledQuery
= m
->timenow
+ mDNSPlatformOneSecond
;
4443 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
4445 LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
4446 m
->NextScheduledProbe
= m
->timenow
+ mDNSPlatformOneSecond
;
4449 // 7. Send Response packets, including probing records just advanced to announcing state
4450 if (m
->timenow
- m
->NextScheduledResponse
>= 0) SendResponses(m
);
4451 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
4453 LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
4454 m
->NextScheduledResponse
= m
->timenow
+ mDNSPlatformOneSecond
;
4458 m
->RandomQueryDelay
= 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
4461 // Note about multi-threaded systems:
4462 // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
4463 // performing mDNS API operations that change our next scheduled event time.
4465 // On multi-threaded systems (like the current Windows implementation) that have a single main thread
4466 // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
4467 // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
4468 // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
4469 // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
4470 // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
4471 // does, the state of the signal will be noticed, causing the blocking primitive to return immediately
4472 // without blocking. This avoids the race condition between the signal from the other thread arriving
4473 // just *before* or just *after* the main thread enters the blocking primitive.
4475 // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
4476 // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
4477 // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
4478 // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
4479 // by the time it gets to the timer callback function).
4481 #ifndef UNICAST_DISABLED
4484 mDNS_Unlock(m
); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4485 return(m
->NextScheduledEvent
);
4488 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
4489 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
4490 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
4491 // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
4492 // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
4493 // found itself in a new network environment. For example, if the Ethernet hardware indicates that the
4494 // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
4495 // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
4496 // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
4497 // traffic, so it should only be called when there is legitimate reason to believe the machine
4498 // may have become attached to a new network.
4499 mDNSexport
void mDNSCoreMachineSleep(mDNS
*const m
, mDNSBool sleepstate
)
4505 m
->SleepState
= sleepstate
;
4506 LogOperation("%s at %ld", sleepstate
? "Sleeping" : "Waking", m
->timenow
);
4510 #ifndef UNICAST_DISABLED
4513 // Mark all the records we need to deregister and send them
4514 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4515 if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
4516 rr
->ImmedAnswer
= mDNSInterfaceMark
;
4526 #ifndef UNICAST_DISABLED
4529 // 1. Retrigger all our questions
4530 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
4531 if (ActiveQuestion(q
))
4533 q
->ThisQInterval
= InitialQuestionInterval
; // MUST be > zero for an active question
4534 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
4535 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
4536 q
->RecentAnswerPkts
= 0;
4537 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
);
4538 m
->NextScheduledQuery
= m
->timenow
;
4541 // 2. Re-validate our cache records
4542 m
->NextCacheCheck
= m
->timenow
;
4543 FORALL_CACHERECORDS(slot
, cg
, cr
)
4544 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForCableDisconnect
);
4546 // 3. Retrigger probing and announcing for all our authoritative records
4547 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4549 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
4550 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
4551 rr
->AnnounceCount
= InitialAnnounceCount
;
4552 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
4553 InitializeLastAPTime(m
, rr
);
4560 // ***************************************************************************
4561 #if COMPILER_LIKES_PRAGMA_MARK
4563 #pragma mark - Packet Reception Functions
4566 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4568 mDNSlocal mDNSu8
*GenerateUnicastResponse(const DNSMessage
*const query
, const mDNSu8
*const end
,
4569 const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, DNSMessage
*const response
, AuthRecord
*ResponseRecords
)
4571 mDNSu8
*responseptr
= response
->data
;
4572 const mDNSu8
*const limit
= response
->data
+ sizeof(response
->data
);
4573 const mDNSu8
*ptr
= query
->data
;
4575 mDNSu32 maxttl
= 0x70000000;
4578 // Initialize the response fields so we can answer the questions
4579 InitializeDNSMessage(&response
->h
, query
->h
.id
, ResponseFlags
);
4582 // *** 1. Write out the list of questions we are actually going to answer with this packet
4587 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
4590 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &q
); // get the question...
4591 if (!ptr
) return(mDNSNULL
);
4593 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // and search our list of proposed answers
4595 if (rr
->NR_AnswerTo
== ptr
) // If we're going to generate a record answering this question
4596 { // then put the question in the question section
4597 responseptr
= putQuestion(response
, responseptr
, limit
, &q
.qname
, q
.qtype
, q
.qclass
);
4598 if (!responseptr
) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL
); }
4599 break; // break out of the ResponseRecords loop, and go on to the next question
4604 if (response
->h
.numQuestions
== 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL
); }
4608 // *** 2. Write Answers
4610 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4611 if (rr
->NR_AnswerTo
)
4613 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAnswers
, &rr
->resrec
, maxttl
);
4614 if (p
) responseptr
= p
;
4615 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response
->h
.flags
.b
[0] |= kDNSFlag0_TC
; }
4619 // *** 3. Write Additionals
4621 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4622 if (rr
->NR_AdditionalTo
&& !rr
->NR_AnswerTo
)
4624 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAdditionals
, &rr
->resrec
, maxttl
);
4625 if (p
) responseptr
= p
;
4626 else debugf("GenerateUnicastResponse: No more space for additionals");
4629 return(responseptr
);
4632 // AuthRecord *our is our Resource Record
4633 // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
4634 // Returns 0 if there is no conflict
4635 // Returns +1 if there was a conflict and we won
4636 // Returns -1 if there was a conflict and we lost and have to rename
4637 mDNSlocal
int CompareRData(AuthRecord
*our
, CacheRecord
*pkt
)
4639 mDNSu8 ourdata
[256], *ourptr
= ourdata
, *ourend
;
4640 mDNSu8 pktdata
[256], *pktptr
= pktdata
, *pktend
;
4641 if (!our
) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
4642 if (!pkt
) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
4644 ourend
= putRData(mDNSNULL
, ourdata
, ourdata
+ sizeof(ourdata
), &our
->resrec
);
4645 pktend
= putRData(mDNSNULL
, pktdata
, pktdata
+ sizeof(pktdata
), &pkt
->resrec
);
4646 while (ourptr
< ourend
&& pktptr
< pktend
&& *ourptr
== *pktptr
) { ourptr
++; pktptr
++; }
4647 if (ourptr
>= ourend
&& pktptr
>= pktend
) return(0); // If data identical, not a conflict
4649 if (ourptr
>= ourend
) return(-1); // Our data ran out first; We lost
4650 if (pktptr
>= pktend
) return(+1); // Packet data ran out first; We won
4651 if (*pktptr
> *ourptr
) return(-1); // Our data is numerically lower; We lost
4652 if (*pktptr
< *ourptr
) return(+1); // Packet data is numerically lower; We won
4654 debugf("CompareRData: How did we get here?");
4658 // See if we have an authoritative record that's identical to this packet record,
4659 // whose canonical DependentOn record is the specified master record.
4660 // The DependentOn pointer is typically used for the TXT record of service registrations
4661 // It indicates that there is no inherent conflict detection for the TXT record
4662 // -- it depends on the SRV record to resolve name conflicts
4663 // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
4664 // pointer chain (if any) to make sure we reach the canonical DependentOn record
4665 // If the record has no DependentOn, then just return that record's pointer
4666 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4667 mDNSlocal mDNSBool
MatchDependentOn(const mDNS
*const m
, const CacheRecord
*const pktrr
, const AuthRecord
*const master
)
4669 const AuthRecord
*r1
;
4670 for (r1
= m
->ResourceRecords
; r1
; r1
=r1
->next
)
4672 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
4674 const AuthRecord
*r2
= r1
;
4675 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
4676 if (r2
== master
) return(mDNStrue
);
4679 for (r1
= m
->DuplicateRecords
; r1
; r1
=r1
->next
)
4681 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
4683 const AuthRecord
*r2
= r1
;
4684 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
4685 if (r2
== master
) return(mDNStrue
);
4691 // Find the canonical RRSet pointer for this RR received in a packet.
4692 // If we find any identical AuthRecord in our authoritative list, then follow its RRSet
4693 // pointers (if any) to make sure we return the canonical member of this name/type/class
4694 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4695 mDNSlocal
const AuthRecord
*FindRRSet(const mDNS
*const m
, const CacheRecord
*const pktrr
)
4697 const AuthRecord
*rr
;
4698 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4700 if (IdenticalResourceRecord(&rr
->resrec
, &pktrr
->resrec
))
4702 while (rr
->RRSet
&& rr
!= rr
->RRSet
) rr
= rr
->RRSet
;
4709 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
4710 // as one of our records (our) but different rdata.
4711 // 1. If our record is not a type that's supposed to be unique, we don't care.
4712 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
4713 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
4714 // points to our record, ignore this conflict (e.g. the packet record matches one of our
4715 // TXT records, and that record is marked as dependent on 'our', its SRV record).
4716 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
4717 // are members of the same RRSet, then this is not a conflict.
4718 mDNSlocal mDNSBool
PacketRRConflict(const mDNS
*const m
, const AuthRecord
*const our
, const CacheRecord
*const pktrr
)
4720 const AuthRecord
*ourset
= our
->RRSet
? our
->RRSet
: our
;
4722 // If not supposed to be unique, not a conflict
4723 if (!(our
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)) return(mDNSfalse
);
4725 // If a dependent record, not a conflict
4726 if (our
->DependentOn
|| MatchDependentOn(m
, pktrr
, our
)) return(mDNSfalse
);
4728 // If the pktrr matches a member of ourset, not a conflict
4729 if (FindRRSet(m
, pktrr
) == ourset
) return(mDNSfalse
);
4731 // Okay, this is a conflict
4735 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
4736 // the record list and/or question list.
4737 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4738 mDNSlocal
void ResolveSimultaneousProbe(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
4739 DNSQuestion
*q
, AuthRecord
*our
)
4742 const mDNSu8
*ptr
= LocateAuthorities(query
, end
);
4743 mDNSBool FoundUpdate
= mDNSfalse
;
4745 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
4747 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, kDNSRecordTypePacketAuth
, &m
->rec
);
4749 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
4751 FoundUpdate
= mDNStrue
;
4752 if (PacketRRConflict(m
, our
, &m
->rec
.r
))
4754 int result
= (int)our
->resrec
.rrclass
- (int)m
->rec
.r
.resrec
.rrclass
;
4755 if (!result
) result
= (int)our
->resrec
.rrtype
- (int)m
->rec
.r
.resrec
.rrtype
;
4756 if (!result
) result
= CompareRData(our
, &m
->rec
.r
);
4759 case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4762 case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4763 mDNS_Deregister_internal(m
, our
, mDNS_Dereg_conflict
);
4768 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4771 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4773 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4776 mDNSlocal CacheRecord
*FindIdenticalRecordInCache(const mDNS
*const m
, ResourceRecord
*pktrr
)
4778 mDNSu32 slot
= HashSlot(pktrr
->name
);
4779 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, pktrr
);
4781 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4782 if (pktrr
->InterfaceID
== rr
->resrec
.InterfaceID
&& IdenticalResourceRecord(pktrr
, &rr
->resrec
)) break;
4786 // ProcessQuery examines a received query to see if we have any answers to give
4787 mDNSlocal mDNSu8
*ProcessQuery(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
4788 const mDNSAddr
*srcaddr
, const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, mDNSBool QueryWasMulticast
, mDNSBool QueryWasLocalUnicast
,
4789 DNSMessage
*const response
)
4791 mDNSBool FromLocalSubnet
= AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
4792 AuthRecord
*ResponseRecords
= mDNSNULL
;
4793 AuthRecord
**nrp
= &ResponseRecords
;
4794 CacheRecord
*ExpectedAnswers
= mDNSNULL
; // Records in our cache we expect to see updated
4795 CacheRecord
**eap
= &ExpectedAnswers
;
4796 DNSQuestion
*DupQuestions
= mDNSNULL
; // Our questions that are identical to questions in this packet
4797 DNSQuestion
**dqp
= &DupQuestions
;
4798 mDNSs32 delayresponse
= 0;
4799 mDNSBool SendLegacyResponse
= mDNSfalse
;
4800 const mDNSu8
*ptr
= query
->data
;
4801 mDNSu8
*responseptr
= mDNSNULL
;
4806 // *** 1. Parse Question Section and mark potential answers
4808 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
4810 mDNSBool QuestionNeedsMulticastResponse
;
4811 int NumAnswersForThisQuestion
= 0;
4812 DNSQuestion pktq
, *q
;
4813 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &pktq
); // get the question...
4814 if (!ptr
) goto exit
;
4816 // The only queries that *need* a multicast response are:
4817 // * Queries sent via multicast
4819 // * that don't have the kDNSQClass_UnicastResponse bit set
4820 // These queries need multicast responses because other clients will:
4821 // * suppress their own identical questions when they see these questions, and
4822 // * expire their cache records if they don't see the expected responses
4823 // For other queries, we may still choose to send the occasional multicast response anyway,
4824 // to keep our neighbours caches warm, and for ongoing conflict detection.
4825 QuestionNeedsMulticastResponse
= QueryWasMulticast
&& !LegacyQuery
&& !(pktq
.qclass
& kDNSQClass_UnicastResponse
);
4826 // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
4827 pktq
.qclass
&= ~kDNSQClass_UnicastResponse
;
4829 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
4830 // can result in user callbacks which may change the record list and/or question list.
4831 // Also note: we just mark potential answer records here, without trying to build the
4832 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
4833 // from that list while we're in the middle of trying to build it.
4834 if (m
->CurrentRecord
) LogMsg("ProcessQuery ERROR m->CurrentRecord already set");
4835 m
->CurrentRecord
= m
->ResourceRecords
;
4836 while (m
->CurrentRecord
)
4838 rr
= m
->CurrentRecord
;
4839 m
->CurrentRecord
= rr
->next
;
4840 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && (QueryWasMulticast
|| QueryWasLocalUnicast
|| rr
->AllowRemoteQuery
))
4842 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
4843 ResolveSimultaneousProbe(m
, query
, end
, &pktq
, rr
);
4844 else if (ResourceRecordIsValidAnswer(rr
))
4846 NumAnswersForThisQuestion
++;
4848 // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
4849 // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
4850 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
4851 // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
4852 // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link)
4853 // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
4854 if (QuestionNeedsMulticastResponse
|| (!FromLocalSubnet
&& QueryWasMulticast
&& !LegacyQuery
))
4856 // We only mark this question for sending if it is at least one second since the last time we multicast it
4857 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
4858 // This is to guard against the case where someone blasts us with queries as fast as they can.
4859 if (m
->timenow
- (rr
->LastMCTime
+ mDNSPlatformOneSecond
) >= 0 ||
4860 (rr
->LastMCInterface
!= mDNSInterfaceMark
&& rr
->LastMCInterface
!= InterfaceID
))
4861 rr
->NR_AnswerTo
= (mDNSu8
*)~0;
4863 else if (!rr
->NR_AnswerTo
) rr
->NR_AnswerTo
= LegacyQuery
? ptr
: (mDNSu8
*)~1;
4868 // If we couldn't answer this question, someone else might be able to,
4869 // so use random delay on response to reduce collisions
4870 if (NumAnswersForThisQuestion
== 0) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
4872 // We only do the following accelerated cache expiration processing and duplicate question suppression processing
4873 // for multicast queries with multicast responses.
4874 // For any query generating a unicast response we don't do this because we can't assume we will see the response
4875 if (QuestionNeedsMulticastResponse
)
4877 const mDNSu32 slot
= HashSlot(&pktq
.qname
);
4878 CacheGroup
*cg
= CacheGroupForName(m
, slot
, pktq
.qnamehash
, &pktq
.qname
);
4881 // Make a list indicating which of our own cache records we expect to see updated as a result of this query
4882 // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
4883 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4884 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && rr
->resrec
.rdlength
<= SmallRecordLimit
)
4885 if (!rr
->NextInKAList
&& eap
!= &rr
->NextInKAList
)
4888 eap
= &rr
->NextInKAList
;
4889 if (rr
->MPUnansweredQ
== 0 || m
->timenow
- rr
->MPLastUnansweredQT
>= mDNSPlatformOneSecond
)
4891 // Although MPUnansweredQ is only really used for multi-packet query processing,
4892 // we increment it for both single-packet and multi-packet queries, so that it stays in sync
4893 // with the MPUnansweredKA value, which by necessity is incremented for both query types.
4894 rr
->MPUnansweredQ
++;
4895 rr
->MPLastUnansweredQT
= m
->timenow
;
4896 rr
->MPExpectingKA
= mDNStrue
;
4900 // Check if this question is the same as any of mine.
4901 // We only do this for non-truncated queries. Right now it would be too complicated to try
4902 // to keep track of duplicate suppression state between multiple packets, especially when we
4903 // can't guarantee to receive all of the Known Answer packets that go with a particular query.
4904 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
4905 for (q
= m
->Questions
; q
; q
=q
->next
)
4906 if (!q
->Target
.type
&& ActiveQuestion(q
) && m
->timenow
- q
->LastQTxTime
> mDNSPlatformOneSecond
/ 4)
4907 if (!q
->InterfaceID
|| q
->InterfaceID
== InterfaceID
)
4908 if (q
->NextInDQList
== mDNSNULL
&& dqp
!= &q
->NextInDQList
)
4909 if (q
->qtype
== pktq
.qtype
&& q
->qclass
== pktq
.qclass
&& q
->qnamehash
== pktq
.qnamehash
&& SameDomainName(&q
->qname
, &pktq
.qname
))
4910 { *dqp
= q
; dqp
= &q
->NextInDQList
; }
4915 // *** 2. Now we can safely build the list of marked answers
4917 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Now build our list of potential answers
4918 if (rr
->NR_AnswerTo
) // If we marked the record...
4919 AddRecordToResponseList(&nrp
, rr
, mDNSNULL
); // ... add it to the list
4922 // *** 3. Add additional records
4924 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
4927 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
4929 for (i
=0; i
<query
->h
.numAnswers
; i
++) // For each record in the query's answer section...
4931 // Get the record...
4933 CacheRecord
*ourcacherr
;
4934 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &m
->rec
);
4935 if (!ptr
) goto exit
;
4937 // See if this Known-Answer suppresses any of our currently planned answers
4938 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4939 if (MustSendRecord(rr
) && ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
4940 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
4942 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
4943 for (rr
=m
->ResourceRecords
; rr
; rr
=rr
->next
)
4945 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
4946 if (rr
->ImmedAnswer
== InterfaceID
&& ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
4948 if (srcaddr
->type
== mDNSAddrType_IPv4
)
4950 if (mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= zerov4Addr
;
4952 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
4954 if (mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= zerov6Addr
;
4956 if (mDNSIPv4AddressIsZero(rr
->v4Requester
) && mDNSIPv6AddressIsZero(rr
->v6Requester
))
4958 rr
->ImmedAnswer
= mDNSNULL
;
4959 rr
->ImmedUnicast
= mDNSfalse
;
4960 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4961 LogMsg("Suppressed after%4d: %s", m
->timenow
- rr
->ImmedAnswerMarkTime
, ARDisplayString(m
, rr
));
4967 // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
4968 // 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).
4969 ourcacherr
= FindIdenticalRecordInCache(m
, &m
->rec
.r
.resrec
);
4970 if (ourcacherr
&& ourcacherr
->MPExpectingKA
&& m
->timenow
- ourcacherr
->MPLastUnansweredQT
< mDNSPlatformOneSecond
)
4972 ourcacherr
->MPUnansweredKA
++;
4973 ourcacherr
->MPExpectingKA
= mDNSfalse
;
4976 // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
4977 // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
4978 // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
4979 eap
= &ExpectedAnswers
;
4982 CacheRecord
*rr
= *eap
;
4983 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
4984 { *eap
= rr
->NextInKAList
; rr
->NextInKAList
= mDNSNULL
; }
4985 else eap
= &rr
->NextInKAList
;
4988 // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
4991 dqp
= &DupQuestions
;
4994 DNSQuestion
*q
= *dqp
;
4995 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
4996 { *dqp
= q
->NextInDQList
; q
->NextInDQList
= mDNSNULL
; }
4997 else dqp
= &q
->NextInDQList
;
5000 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5004 // *** 5. Cancel any additionals that were added because of now-deleted records
5006 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
5007 if (rr
->NR_AdditionalTo
&& !MustSendRecord(rr
->NR_AdditionalTo
))
5008 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
5011 // *** 6. Mark the send flags on the records we plan to send
5013 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
5015 if (rr
->NR_AnswerTo
)
5017 mDNSBool SendMulticastResponse
= mDNSfalse
; // Send modern multicast response
5018 mDNSBool SendUnicastResponse
= mDNSfalse
; // Send modern unicast response (not legacy unicast response)
5020 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
5021 if (m
->timenow
- (rr
->LastMCTime
+ TicksTTL(rr
)/4) >= 0)
5023 SendMulticastResponse
= mDNStrue
;
5024 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
5025 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
5026 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
5027 if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) rr
->NR_AnswerTo
= (mDNSu8
*)~0;
5030 // If the client insists on a multicast response, then we'd better send one
5031 if (rr
->NR_AnswerTo
== (mDNSu8
*)~0) SendMulticastResponse
= mDNStrue
;
5032 else if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) SendUnicastResponse
= mDNStrue
;
5033 else if (rr
->NR_AnswerTo
) SendLegacyResponse
= mDNStrue
;
5035 if (SendMulticastResponse
|| SendUnicastResponse
)
5037 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5038 rr
->ImmedAnswerMarkTime
= m
->timenow
;
5040 m
->NextScheduledResponse
= m
->timenow
;
5041 // If we're already planning to send this on another interface, just send it on all interfaces
5042 if (rr
->ImmedAnswer
&& rr
->ImmedAnswer
!= InterfaceID
)
5043 rr
->ImmedAnswer
= mDNSInterfaceMark
;
5046 rr
->ImmedAnswer
= InterfaceID
; // Record interface to send it on
5047 if (SendUnicastResponse
) rr
->ImmedUnicast
= mDNStrue
;
5048 if (srcaddr
->type
== mDNSAddrType_IPv4
)
5050 if (mDNSIPv4AddressIsZero(rr
->v4Requester
)) rr
->v4Requester
= srcaddr
->ip
.v4
;
5051 else if (!mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= onesIPv4Addr
;
5053 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
5055 if (mDNSIPv6AddressIsZero(rr
->v6Requester
)) rr
->v6Requester
= srcaddr
->ip
.v6
;
5056 else if (!mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= onesIPv6Addr
;
5060 // If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
5061 // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
5062 // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
5063 // else, for a simple unique record reply, we can reply immediately; no need for delay
5064 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
) delayresponse
= mDNSPlatformOneSecond
* 20; // Divided by 50 = 400ms
5065 else if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
5067 else if (rr
->NR_AdditionalTo
&& rr
->NR_AdditionalTo
->NR_AnswerTo
== (mDNSu8
*)~0)
5069 // Since additional records are an optimization anyway, we only ever send them on one interface at a time
5070 // If two clients on different interfaces do queries that invoke the same optional additional answer,
5071 // then the earlier client is out of luck
5072 rr
->ImmedAdditional
= InterfaceID
;
5073 // No need to set m->NextScheduledResponse here
5074 // We'll send these additional records when we send them, or not, as the case may be
5079 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5081 if (delayresponse
&& (!m
->SuppressSending
|| (m
->SuppressSending
- m
->timenow
) < (delayresponse
+ 49) / 50))
5083 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5084 mDNSs32 oldss
= m
->SuppressSending
;
5085 if (oldss
&& delayresponse
)
5086 LogMsg("Current SuppressSending delay%5ld; require%5ld", m
->SuppressSending
- m
->timenow
, (delayresponse
+ 49) / 50);
5088 // Pick a random delay:
5089 // We start with the base delay chosen above (typically either 1 second or 20 seconds),
5090 // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
5091 // This is an integer value, with resolution determined by the platform clock rate.
5092 // We then divide that by 50 to get the delay value in ticks. We defer the division until last
5093 // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
5094 // The +49 before dividing is to ensure we round up, not down, to ensure that even
5095 // on platforms where the native clock rate is less than fifty ticks per second,
5096 // we still guarantee that the final calculated delay is at least one platform tick.
5097 // We want to make sure we don't ever allow the delay to be zero ticks,
5098 // because if that happens we'll fail the Bonjour Conformance Test.
5099 // Our final computed delay is 20-120ms for normal delayed replies,
5100 // or 400-500ms in the case of multi-packet known-answer lists.
5101 m
->SuppressSending
= m
->timenow
+ (delayresponse
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*5) + 49) / 50;
5102 if (m
->SuppressSending
== 0) m
->SuppressSending
= 1;
5103 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5104 if (oldss
&& delayresponse
)
5105 LogMsg("Set SuppressSending to %5ld", m
->SuppressSending
- m
->timenow
);
5110 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
5112 if (SendLegacyResponse
)
5113 responseptr
= GenerateUnicastResponse(query
, end
, InterfaceID
, LegacyQuery
, response
, ResponseRecords
);
5116 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5119 // *** 9. Finally, clear our link chains ready for use next time
5121 while (ResponseRecords
)
5123 rr
= ResponseRecords
;
5124 ResponseRecords
= rr
->NextResponse
;
5125 rr
->NextResponse
= mDNSNULL
;
5126 rr
->NR_AnswerTo
= mDNSNULL
;
5127 rr
->NR_AdditionalTo
= mDNSNULL
;
5130 while (ExpectedAnswers
)
5133 rr
= ExpectedAnswers
;
5134 ExpectedAnswers
= rr
->NextInKAList
;
5135 rr
->NextInKAList
= mDNSNULL
;
5137 // For non-truncated queries, we can definitively say that we should expect
5138 // to be seeing a response for any records still left in the ExpectedAnswers list
5139 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
5140 if (rr
->UnansweredQueries
== 0 || m
->timenow
- rr
->LastUnansweredTime
>= mDNSPlatformOneSecond
)
5142 rr
->UnansweredQueries
++;
5143 rr
->LastUnansweredTime
= m
->timenow
;
5144 if (rr
->UnansweredQueries
> 1)
5145 debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
5146 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5147 SetNextCacheCheckTime(m
, rr
);
5150 // If we've seen multiple unanswered queries for this record,
5151 // then mark it to expire in five seconds if we don't get a response by then.
5152 if (rr
->UnansweredQueries
>= MaxUnansweredQueries
)
5154 // Only show debugging message if this record was not about to expire anyway
5155 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
5156 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5157 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5158 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
5160 // Make a guess, based on the multi-packet query / known answer counts, whether we think we
5161 // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
5162 // possible packet loss of up to 20% of the additional KA packets.)
5163 else if (rr
->MPUnansweredQ
* 4 > rr
->MPUnansweredKA
* 5 + 8)
5165 // We want to do this conservatively.
5166 // If there are so many machines on the network that they have to use multi-packet known-answer lists,
5167 // then we don't want them to all hit the network simultaneously with their final expiration queries.
5168 // By setting the record to expire in four minutes, we achieve two things:
5169 // (a) the 90-95% final expiration queries will be less bunched together
5170 // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
5171 mDNSu32 remain
= (mDNSu32
)(RRExpireTime(rr
) - m
->timenow
) / 4;
5172 if (remain
> 240 * (mDNSu32
)mDNSPlatformOneSecond
)
5173 remain
= 240 * (mDNSu32
)mDNSPlatformOneSecond
;
5175 // Only show debugging message if this record was not about to expire anyway
5176 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
5177 debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5178 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5180 if (remain
<= 60 * (mDNSu32
)mDNSPlatformOneSecond
)
5181 rr
->UnansweredQueries
++; // Treat this as equivalent to one definite unanswered query
5182 rr
->MPUnansweredQ
= 0; // Clear MPQ/MPKA statistics
5183 rr
->MPUnansweredKA
= 0;
5184 rr
->MPExpectingKA
= mDNSfalse
;
5186 if (remain
< kDefaultReconfirmTimeForNoAnswer
)
5187 remain
= kDefaultReconfirmTimeForNoAnswer
;
5188 mDNS_Reconfirm_internal(m
, rr
, remain
);
5192 while (DupQuestions
)
5195 DNSQuestion
*q
= DupQuestions
;
5196 DupQuestions
= q
->NextInDQList
;
5197 q
->NextInDQList
= mDNSNULL
;
5198 i
= RecordDupSuppressInfo(q
->DupSuppress
, m
->timenow
, InterfaceID
, srcaddr
->type
);
5199 debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q
->qname
.c
, DNSTypeName(q
->qtype
), InterfaceID
,
5200 srcaddr
->type
== mDNSAddrType_IPv4
? "v4" : "v6", i
);
5203 return(responseptr
);
5206 mDNSlocal
void mDNSCoreReceiveQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
5207 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
5208 const mDNSInterfaceID InterfaceID
)
5210 mDNSu8
*responseend
= mDNSNULL
;
5211 mDNSBool QueryWasLocalUnicast
= !mDNSAddrIsDNSMulticast(dstaddr
) && AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
5213 if (!InterfaceID
&& mDNSAddrIsDNSMulticast(dstaddr
))
5215 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)",
5216 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
5217 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
5218 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
5219 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5220 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
5224 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",
5225 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
5226 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
5227 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
5228 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5229 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
5231 responseend
= ProcessQuery(m
, msg
, end
, srcaddr
, InterfaceID
,
5232 (srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
), mDNSAddrIsDNSMulticast(dstaddr
), QueryWasLocalUnicast
, &m
->omsg
);
5234 if (responseend
) // If responseend is non-null, that means we built a unicast response packet
5236 debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
5237 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
5238 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
5239 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s",
5240 srcaddr
, mDNSVal16(srcport
), InterfaceID
, srcaddr
->type
);
5241 mDNSSendDNSMessage(m
, &m
->omsg
, responseend
, InterfaceID
, srcaddr
, srcport
, -1, mDNSNULL
);
5245 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
5246 // the record list and/or question list.
5247 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
5248 mDNSlocal
void mDNSCoreReceiveResponse(mDNS
*const m
,
5249 const DNSMessage
*const response
, const mDNSu8
*end
,
5250 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
5251 const mDNSInterfaceID InterfaceID
)
5254 const mDNSu8
*ptr
= LocateAnswers(response
, end
); // We ignore questions (if any) in a DNS response packet
5255 CacheRecord
*CacheFlushRecords
= (CacheRecord
*)1; // "(CacheRecord*)1" is special (non-zero) end-of-list marker
5256 CacheRecord
**cfp
= &CacheFlushRecords
;
5258 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
5259 // to guard against spoof responses, then the only credible protection against that is cryptographic
5260 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
5261 int totalrecords
= response
->h
.numAnswers
+ response
->h
.numAuthorities
+ response
->h
.numAdditionals
;
5263 (void)srcaddr
; // Currently used only for display in debugging message
5267 verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5268 srcaddr
, dstaddr
, InterfaceID
,
5269 response
->h
.numQuestions
, response
->h
.numQuestions
== 1 ? ", " : "s,",
5270 response
->h
.numAnswers
, response
->h
.numAnswers
== 1 ? ", " : "s,",
5271 response
->h
.numAuthorities
, response
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5272 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s");
5274 // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us
5275 if (!mDNSAddrIsDNSMulticast(dstaddr
))
5277 if (!AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
) || (mDNSu32
)(m
->timenow
- m
->ExpectUnicastResponse
) > (mDNSu32
)(mDNSPlatformOneSecond
*2))
5279 // For now we don't put standard wide-area unicast responses in our main cache
5280 // (Later we should fix this and cache all known results in a unified manner.)
5281 if (response
->h
.id
.NotAnInteger
!= 0 || srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
)
5285 for (i
= 0; i
< totalrecords
&& ptr
&& ptr
< end
; i
++)
5287 const mDNSu8 RecordType
= (mDNSu8
)((i
< response
->h
.numAnswers
) ? kDNSRecordTypePacketAns
: kDNSRecordTypePacketAdd
);
5288 ptr
= GetLargeResourceRecord(m
, response
, ptr
, end
, InterfaceID
, RecordType
, &m
->rec
);
5289 if (!ptr
) goto exit
; // Break out of the loop and clean up our CacheFlushRecords list before exiting
5291 // 1. Check that this packet resource record does not conflict with any of ours
5292 if (m
->CurrentRecord
) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
5293 m
->CurrentRecord
= m
->ResourceRecords
;
5294 while (m
->CurrentRecord
)
5296 AuthRecord
*rr
= m
->CurrentRecord
;
5297 m
->CurrentRecord
= rr
->next
;
5298 if (PacketRRMatchesSignature(&m
->rec
.r
, rr
)) // If interface, name, type (if shared record) and class match...
5300 // ... check to see if type and rdata are identical
5301 if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
&& SameRData(&m
->rec
.r
.resrec
, &rr
->resrec
))
5303 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
5304 if (m
->rec
.r
.resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/2 || m
->SleepState
)
5306 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
5307 if (rr
->ImmedAnswer
== InterfaceID
) { rr
->ImmedAnswer
= mDNSNULL
; rr
->ImmedUnicast
= mDNSfalse
; }
5311 if (rr
->ImmedAnswer
== mDNSNULL
) { rr
->ImmedAnswer
= InterfaceID
; m
->NextScheduledResponse
= m
->timenow
; }
5312 else if (rr
->ImmedAnswer
!= InterfaceID
) { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
5315 // else, the packet RR has different type or different rdata -- check to see if this is a conflict
5316 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0 && PacketRRConflict(m
, rr
, &m
->rec
.r
))
5318 debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr
-> resrec
.rdatahash
, ARDisplayString(m
, rr
));
5319 debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m
->rec
.r
.resrec
.rdatahash
, CRDisplayString(m
, &m
->rec
.r
));
5321 // If this record is marked DependentOn another record for conflict detection purposes,
5322 // then *that* record has to be bumped back to probing state to resolve the conflict
5323 while (rr
->DependentOn
) rr
= rr
->DependentOn
;
5325 // If we've just whacked this record's ProbeCount, don't need to do it again
5326 if (rr
->ProbeCount
<= DefaultProbeCountForTypeUnique
)
5328 // If we'd previously verified this record, put it back to probing state and try again
5329 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
)
5331 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5332 rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
5333 rr
->ProbeCount
= DefaultProbeCountForTypeUnique
+ 1;
5334 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(kDNSRecordTypeUnique
);
5335 InitializeLastAPTime(m
, rr
);
5336 RecordProbeFailure(m
, rr
); // Repeated late conflicts also cause us to back off to the slower probing rate
5338 // If we're probing for this record, we just failed
5339 else if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
5341 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5342 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
5344 // We assumed this record must be unique, but we were wrong.
5345 // (e.g. There are two mDNSResponders on the same machine giving
5346 // different answers for the reverse mapping record.)
5347 // This is simply a misconfiguration, and we don't try to recover from it.
5348 else if (rr
->resrec
.RecordType
== kDNSRecordTypeKnownUnique
)
5350 debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
5351 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5352 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
5355 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
5356 rr
->resrec
.RecordType
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5359 // Else, matching signature, different type or rdata, but not a considered a conflict.
5360 // If the packet record has the cache-flush bit set, then we check to see if we
5361 // have any record(s) of the same type that we should re-assert to rescue them
5362 // (see note about "multi-homing and bridged networks" at the end of this function).
5363 else if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
)
5364 if ((m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) && m
->timenow
- rr
->LastMCTime
> mDNSPlatformOneSecond
/2)
5365 { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
5369 // 2. See if we want to add this packet resource record to our cache
5370 if (m
->rrcache_size
) // Only try to cache answers if we have a cache to put them in
5372 const mDNSu32 slot
= HashSlot(m
->rec
.r
.resrec
.name
);
5373 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &m
->rec
.r
.resrec
);
5375 // 2a. Check if this packet resource record is already in our cache
5376 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5378 // If we found this exact resource record, refresh its TTL
5379 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
5381 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
5382 verbosedebugf("Found record size %5d interface %p already in cache: %s",
5383 m
->rec
.r
.resrec
.rdlength
, InterfaceID
, CRDisplayString(m
, &m
->rec
.r
));
5384 rr
->TimeRcvd
= m
->timenow
;
5386 if (m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
5388 // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
5389 if (rr
->NextInCFList
== mDNSNULL
&& cfp
!= &rr
->NextInCFList
)
5390 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
5392 // If this packet record is marked unique, and our previous cached copy was not, then fix it
5393 if (!(rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))
5396 for (q
= m
->Questions
; q
; q
=q
->next
) if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) q
->UniqueAnswers
++;
5397 rr
->resrec
.RecordType
= m
->rec
.r
.resrec
.RecordType
;
5401 if (!mDNSPlatformMemSame(m
->rec
.r
.resrec
.rdata
->u
.data
, rr
->resrec
.rdata
->u
.data
, m
->rec
.r
.resrec
.rdlength
))
5403 // If the rdata of the packet record differs in name capitalization from the record in our cache
5404 // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
5405 // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
5406 rr
->resrec
.rroriginalttl
= 0;
5407 rr
->UnansweredQueries
= MaxUnansweredQueries
;
5408 SetNextCacheCheckTime(m
, rr
);
5409 // DO NOT break out here -- we want to continue as if we never found it
5411 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0)
5413 rr
->resrec
.rroriginalttl
= m
->rec
.r
.resrec
.rroriginalttl
;
5414 rr
->UnansweredQueries
= 0;
5415 rr
->MPUnansweredQ
= 0;
5416 rr
->MPUnansweredKA
= 0;
5417 rr
->MPExpectingKA
= mDNSfalse
;
5418 SetNextCacheCheckTime(m
, rr
);
5423 // If the packet TTL is zero, that means we're deleting this record.
5424 // To give other hosts on the network a chance to protest, we push the deletion
5425 // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
5426 // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
5427 // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
5428 rr
->resrec
.rroriginalttl
= 1;
5429 rr
->UnansweredQueries
= MaxUnansweredQueries
;
5430 SetNextCacheCheckTime(m
, rr
);
5436 // If packet resource record not in our cache, add it now
5437 // (unless it is just a deletion of a record we never had, in which case we don't care)
5438 if (!rr
&& m
->rec
.r
.resrec
.rroriginalttl
> 0)
5440 // If we don't have a CacheGroup for this name, make one now
5441 if (!cg
) cg
= GetCacheGroup(m
, slot
, &m
->rec
.r
.resrec
);
5442 if (cg
) rr
= GetCacheRecord(m
, cg
, m
->rec
.r
.resrec
.rdlength
); // Make a cache record, being careful not to recycle cg
5443 if (!rr
) NoCacheAnswer(m
, &m
->rec
.r
);
5446 RData
*saveptr
= rr
->resrec
.rdata
; // Save the rr->resrec.rdata pointer
5447 *rr
= m
->rec
.r
; // Block copy the CacheRecord object
5448 rr
->resrec
.rdata
= saveptr
; // Restore rr->resrec.rdata after the structure assignment
5449 rr
->resrec
.name
= cg
->name
; // And set rr->resrec.name to point into our CacheGroup header
5450 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
5451 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
5452 // If this is an oversized record with external storage allocated, copy rdata to external storage
5453 if (rr
->resrec
.rdata
!= (RData
*)&rr
->rdatastorage
&& !(m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
))
5454 LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m
->rec
.r
.resrec
.name
->c
);
5455 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
5456 mDNSPlatformMemCopy(m
->rec
.r
.resrec
.rdata
, rr
->resrec
.rdata
, sizeofRDataHeader
+ m
->rec
.r
.resrec
.rdlength
);
5457 rr
->next
= mDNSNULL
; // Clear 'next' pointer
5458 *(cg
->rrcache_tail
) = rr
; // Append this record to tail of cache slot list
5459 cg
->rrcache_tail
= &(rr
->next
); // Advance tail pointer
5460 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) // If marked unique, assume we may have
5461 rr
->DelayDelivery
= m
->timenow
+ mDNSPlatformOneSecond
; // to delay delivery of this 'add' event
5463 rr
->DelayDelivery
= CheckForSoonToExpireRecords(m
, rr
->resrec
.name
, rr
->resrec
.namehash
, slot
);
5464 CacheRecordAdd(m
, rr
);
5465 // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us
5466 SetNextCacheCheckTime(m
, rr
);
5470 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5474 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5476 // If we've just received one or more records with their cache flush bits set,
5477 // then scan that cache slot to see if there are any old stale records we need to flush
5478 while (CacheFlushRecords
!= (CacheRecord
*)1)
5480 CacheRecord
*r1
= CacheFlushRecords
, *r2
;
5481 const mDNSu32 slot
= HashSlot(r1
->resrec
.name
);
5482 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &r1
->resrec
);
5483 CacheFlushRecords
= CacheFlushRecords
->NextInCFList
;
5484 r1
->NextInCFList
= mDNSNULL
;
5485 for (r2
= cg
? cg
->members
: mDNSNULL
; r2
; r2
=r2
->next
)
5486 if (SameResourceRecordSignature(&r1
->resrec
, &r2
->resrec
))
5488 // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
5489 // else, if record is old, mark it to be flushed
5490 if (m
->timenow
- r2
->TimeRcvd
< mDNSPlatformOneSecond
)
5491 r2
->resrec
.rroriginalttl
= r1
->resrec
.rroriginalttl
;
5494 verbosedebugf("Cache flush %p X %p %##s (%s)", r1
, r2
, r2
->resrec
.name
->c
, DNSTypeName(r2
->resrec
.rrtype
));
5495 // We set stale records to expire in one second.
5496 // This gives the owner a chance to rescue it if necessary.
5497 // This is important in the case of multi-homing and bridged networks:
5498 // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
5499 // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
5500 // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
5501 // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
5502 // By delaying the deletion by one second, we give X a change to notice that this bridging has
5503 // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
5504 // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
5505 // final expiration queries for this record.
5506 r2
->resrec
.rroriginalttl
= 1;
5507 r2
->TimeRcvd
= m
->timenow
;
5508 r2
->UnansweredQueries
= MaxUnansweredQueries
;
5509 SetNextCacheCheckTime(m
, r2
);
5512 if (r1
->DelayDelivery
) // If we were planning to delay delivery of this record, see if we still need to
5514 r1
->DelayDelivery
= CheckForSoonToExpireRecords(m
, r1
->resrec
.name
, r1
->resrec
.namehash
, slot
);
5515 if (!r1
->DelayDelivery
) CacheRecordDeferredAdd(m
, r1
);
5520 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, void *const pkt
, const mDNSu8
*const end
,
5521 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*const dstaddr
, const mDNSIPPort dstport
,
5522 const mDNSInterfaceID InterfaceID
)
5524 DNSMessage
*msg
= (DNSMessage
*)pkt
;
5525 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
5526 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
5528 mDNSu8
*ptr
= mDNSNULL
;
5529 const mDNSu8 UpdateR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
5531 #ifndef UNICAST_DISABLED
5532 mDNSIPPort NATPort
= mDNSOpaque16fromIntVal(NATMAP_PORT
);
5534 if (srcport
.NotAnInteger
== NATPort
.NotAnInteger
)
5537 uDNS_ReceiveNATMap(m
, pkt
, (mDNSu16
)(end
- (mDNSu8
*)pkt
));
5542 if ((unsigned)(end
- (mDNSu8
*)pkt
) < sizeof(DNSMessageHeader
)) { LogMsg("DNS Message too short"); return; }
5543 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
5544 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
5545 ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
5546 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
5547 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
5548 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
5549 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
5551 if (!m
) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
5553 // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
5554 // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
5555 if (!mDNSAddressIsValid(srcaddr
)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr
); return; }
5559 #ifndef UNICAST_DISABLED
5560 if (!mDNSAddressIsAllDNSLinkGroup(dstaddr
) && (QR_OP
== StdR
|| QR_OP
== UpdateR
))
5561 uDNS_ReceiveMsg(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5562 // Note: mDNSCore also needs to get access to received unicast responses
5564 if (QR_OP
== StdQ
) mDNSCoreReceiveQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5565 else if (QR_OP
== StdR
) mDNSCoreReceiveResponse(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5566 else if (QR_OP
!= UpdateR
)
5567 LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
5568 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1], srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
);
5570 // Packet reception often causes a change to the task list:
5571 // 1. Inbound queries can cause us to need to send responses
5572 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
5573 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
5574 // 4. Response packets that answer questions may cause our client to issue new questions
5578 // ***************************************************************************
5579 #if COMPILER_LIKES_PRAGMA_MARK
5582 #pragma mark - Searcher Functions
5585 #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5587 mDNSlocal DNSQuestion
*FindDuplicateQuestion(const mDNS
*const m
, const DNSQuestion
*const question
)
5590 // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
5591 // This prevents circular references, where two questions are each marked as a duplicate of the other.
5592 // Accordingly, we break out of the loop when we get to 'question', because there's no point searching
5593 // further in the list.
5594 for (q
= m
->Questions
; q
&& q
!= question
; q
=q
->next
) // Scan our list of questions
5595 if (q
->InterfaceID
== question
->InterfaceID
&& // for another question with the same InterfaceID,
5596 SameQTarget(q
, question
) && // and same unicast/multicast target settings
5597 q
->qtype
== question
->qtype
&& // type,
5598 q
->qclass
== question
->qclass
&& // class,
5599 q
->qnamehash
== question
->qnamehash
&&
5600 SameDomainName(&q
->qname
, &question
->qname
)) // and name
5605 // This is called after a question is deleted, in case other identical questions were being
5606 // suppressed as duplicates
5607 mDNSlocal
void UpdateQuestionDuplicates(mDNS
*const m
, const DNSQuestion
*const question
)
5610 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5611 if (q
->DuplicateOf
== question
) // To see if any questions were referencing this as their duplicate
5613 q
->ThisQInterval
= question
->ThisQInterval
;
5614 q
->RequestUnicast
= question
->RequestUnicast
;
5615 q
->LastQTime
= question
->LastQTime
;
5616 q
->RecentAnswerPkts
= 0;
5617 q
->DuplicateOf
= FindDuplicateQuestion(m
, q
);
5618 q
->LastQTxTime
= question
->LastQTxTime
;
5619 SetNextQueryTime(m
,q
);
5623 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
5624 ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger))
5626 mDNSlocal mStatus
mDNS_StartQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5628 if (question
->Target
.type
&& !ValidQuestionTarget(question
))
5630 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5631 question
->Target
.type
, mDNSVal16(question
->TargetPort
));
5632 question
->Target
.type
= mDNSAddrType_None
;
5635 if (!question
->Target
.type
) // No question->Target specified, so clear TargetPort and TargetQID
5637 question
->TargetPort
= zeroIPPort
;
5638 question
->TargetQID
= zeroID
;
5641 #ifndef UNICAST_DISABLED
5642 // If the client has specified 'kDNSServiceFlagsForceMulticast'
5643 // then we do a multicast query on that interface, even for unicast domains.
5644 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5645 question
->uDNS_info
.id
= zeroID
;
5646 else return uDNS_StartQuery(m
, question
);
5648 question
->uDNS_info
.id
= zeroID
;
5649 #endif // UNICAST_DISABLED
5651 //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5653 if (m
->rrcache_size
== 0) // Can't do queries if we have no cache space allocated
5654 return(mStatus_NoCache
);
5658 // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
5659 DNSQuestion
**q
= &m
->Questions
;
5660 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5661 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5665 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5666 question
->qname
.c
, DNSTypeName(question
->qtype
));
5667 return(mStatus_AlreadyRegistered
);
5670 // If this question is referencing a specific interface, make sure it exists
5671 if (question
->InterfaceID
&& question
->InterfaceID
!= mDNSInterface_LocalOnly
)
5673 NetworkInterfaceInfo
*intf
;
5674 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5675 if (intf
->InterfaceID
== question
->InterfaceID
) break;
5678 debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question
->qname
.c
, question
->InterfaceID
);
5679 return(mStatus_BadInterfaceErr
);
5683 if (!ValidateDomainName(&question
->qname
))
5685 LogMsg("Attempt to start query with invalid qname %##s (%s)", question
->qname
.c
, DNSTypeName(question
->qtype
));
5686 return(mStatus_Invalid
);
5689 // Note: In the case where we already have the answer to this question in our cache, that may be all the client
5690 // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
5691 // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds
5692 // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately.
5693 if (!m
->RandomQueryDelay
) m
->RandomQueryDelay
= 1 + (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
5695 question
->next
= mDNSNULL
;
5696 question
->qnamehash
= DomainNameHashValue(&question
->qname
); // MUST do this before FindDuplicateQuestion()
5697 question
->DelayAnswering
= CheckForSoonToExpireRecords(m
, &question
->qname
, question
->qnamehash
, HashSlot(&question
->qname
));
5698 question
->ThisQInterval
= InitialQuestionInterval
* 2; // MUST be > zero for an active question
5699 question
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
5700 question
->LastQTime
= m
->timenow
- m
->RandomQueryDelay
; // Avoid inter-machine synchronization
5701 question
->LastAnswerPktNum
= m
->PktNum
;
5702 question
->RecentAnswerPkts
= 0;
5703 question
->CurrentAnswers
= 0;
5704 question
->LargeAnswers
= 0;
5705 question
->UniqueAnswers
= 0;
5706 question
->DuplicateOf
= FindDuplicateQuestion(m
, question
);
5707 question
->NextInDQList
= mDNSNULL
;
5708 for (i
=0; i
<DupSuppressInfoSize
; i
++)
5709 question
->DupSuppress
[i
].InterfaceID
= mDNSNULL
;
5710 // question->InterfaceID must be already set by caller
5711 question
->SendQNow
= mDNSNULL
;
5712 question
->SendOnAll
= mDNSfalse
;
5713 question
->LastQTxTime
= m
->timenow
;
5715 if (!question
->DuplicateOf
)
5716 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started",
5717 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
, question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
);
5719 verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)",
5720 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
, question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
, question
->DuplicateOf
);
5723 if (question
->InterfaceID
== mDNSInterface_LocalOnly
)
5725 if (!m
->NewLocalOnlyQuestions
) m
->NewLocalOnlyQuestions
= question
;
5729 if (!m
->NewQuestions
) m
->NewQuestions
= question
;
5730 SetNextQueryTime(m
,question
);
5733 return(mStatus_NoError
);
5737 mDNSlocal mStatus
mDNS_StopQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5739 const mDNSu32 slot
= HashSlot(&question
->qname
);
5740 CacheGroup
*cg
= CacheGroupForName(m
, slot
, question
->qnamehash
, &question
->qname
);
5742 DNSQuestion
**q
= &m
->Questions
;
5744 if (uDNS_IsActiveQuery(question
, &m
->uDNS_info
)) return uDNS_StopQuery(m
, question
);
5746 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5747 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5748 if (*q
) *q
= (*q
)->next
;
5751 if (question
->ThisQInterval
>= 0) // Only log error message if the query was supposed to be active
5752 LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
5753 question
->qname
.c
, DNSTypeName(question
->qtype
));
5754 return(mStatus_BadReferenceErr
);
5757 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
5758 UpdateQuestionDuplicates(m
, question
);
5759 // But don't trash ThisQInterval until afterwards.
5760 question
->ThisQInterval
= -1;
5762 // If there are any cache records referencing this as their active question, then see if any other
5763 // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
5764 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5766 if (rr
->CRActiveQuestion
== question
)
5769 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5770 if (ActiveQuestion(q
) && ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
5772 verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), q
);
5773 rr
->CRActiveQuestion
= q
; // Question used to be active; new value may or may not be null
5774 if (!q
) m
->rrcache_active
--; // If no longer active, decrement rrcache_active count
5778 // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at,
5779 // bump its pointer forward one question.
5780 if (m
->CurrentQuestion
== question
)
5782 debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
5783 question
->qname
.c
, DNSTypeName(question
->qtype
));
5784 m
->CurrentQuestion
= question
->next
;
5787 if (m
->NewQuestions
== question
)
5789 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
5790 question
->qname
.c
, DNSTypeName(question
->qtype
));
5791 m
->NewQuestions
= question
->next
;
5794 if (m
->NewLocalOnlyQuestions
== question
) m
->NewLocalOnlyQuestions
= question
->next
;
5796 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5797 question
->next
= mDNSNULL
;
5798 return(mStatus_NoError
);
5801 mDNSexport mStatus
mDNS_StartQuery(mDNS
*const m
, DNSQuestion
*const question
)
5805 status
= mDNS_StartQuery_internal(m
, question
);
5810 mDNSexport mStatus
mDNS_StopQuery(mDNS
*const m
, DNSQuestion
*const question
)
5814 status
= mDNS_StopQuery_internal(m
, question
);
5819 mDNSexport mStatus
mDNS_Reconfirm(mDNS
*const m
, CacheRecord
*const rr
)
5823 status
= mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
5828 mDNSexport mStatus
mDNS_ReconfirmByValue(mDNS
*const m
, ResourceRecord
*const rr
)
5830 mStatus status
= mStatus_BadReferenceErr
;
5833 cr
= FindIdenticalRecordInCache(m
, rr
);
5834 if (cr
) status
= mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
5839 mDNSexport mStatus
mDNS_StartBrowse(mDNS
*const m
, DNSQuestion
*const question
,
5840 const domainname
*const srv
, const domainname
*const domain
,
5841 const mDNSInterfaceID InterfaceID
, mDNSBool ForceMCast
, mDNSQuestionCallback
*Callback
, void *Context
)
5843 question
->InterfaceID
= InterfaceID
;
5844 question
->Target
= zeroAddr
;
5845 question
->qtype
= kDNSType_PTR
;
5846 question
->qclass
= kDNSClass_IN
;
5847 question
->LongLived
= mDNSfalse
;
5848 question
->ExpectUnique
= mDNSfalse
;
5849 question
->ForceMCast
= ForceMCast
;
5850 question
->QuestionCallback
= Callback
;
5851 question
->QuestionContext
= Context
;
5852 if (!ConstructServiceName(&question
->qname
, mDNSNULL
, srv
, domain
)) return(mStatus_BadParamErr
);
5854 #ifndef UNICAST_DISABLED
5855 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5857 question
->LongLived
= mDNSfalse
;
5858 question
->uDNS_info
.id
= zeroID
;
5859 return(mDNS_StartQuery(m
, question
));
5864 // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5866 question
->LongLived
= mDNStrue
;
5867 status
= uDNS_StartQuery(m
, question
);
5872 return(mDNS_StartQuery(m
, question
));
5873 #endif // UNICAST_DISABLED
5876 mDNSlocal mDNSBool
MachineHasActiveIPv6(mDNS
*const m
)
5878 NetworkInterfaceInfo
*intf
;
5879 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5880 if (intf
->ip
.type
== mDNSAddrType_IPv6
) return(mDNStrue
);
5884 mDNSlocal
void FoundServiceInfoSRV(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
5886 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5887 mDNSBool PortChanged
= (mDNSBool
)(query
->info
->port
.NotAnInteger
!= answer
->rdata
->u
.srv
.port
.NotAnInteger
);
5888 if (!AddRecord
) return;
5889 if (answer
->rrtype
!= kDNSType_SRV
) return;
5891 query
->info
->port
= answer
->rdata
->u
.srv
.port
;
5893 // If this is our first answer, then set the GotSRV flag and start the address query
5896 query
->GotSRV
= mDNStrue
;
5897 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5898 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5899 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5900 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5901 mDNS_StartQuery(m
, &query
->qAv4
);
5902 // Only do the AAAA query if this machine actually has IPv6 active
5903 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5905 // If this is not our first answer, only re-issue the address query if the target host name has changed
5906 else if ((query
->qAv4
.InterfaceID
!= query
->qSRV
.InterfaceID
&& query
->qAv4
.InterfaceID
!= answer
->InterfaceID
) ||
5907 !SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
))
5909 mDNS_StopQuery(m
, &query
->qAv4
);
5910 if (query
->qAv6
.ThisQInterval
>= 0) mDNS_StopQuery(m
, &query
->qAv6
);
5911 if (SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
) && !PortChanged
)
5913 // If we get here, it means:
5914 // 1. This is not our first SRV answer
5915 // 2. The interface ID is different, but the target host and port are the same
5916 // This implies that we're seeing the exact same SRV record on more than one interface, so we should
5917 // make our address queries at least as broad as the original SRV query so that we catch all the answers.
5918 query
->qAv4
.InterfaceID
= query
->qSRV
.InterfaceID
; // Will be mDNSInterface_Any, or a specific interface
5919 query
->qAv6
.InterfaceID
= query
->qSRV
.InterfaceID
;
5923 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5924 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5925 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5926 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5928 debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query
->qAv4
.qname
.c
);
5929 mDNS_StartQuery(m
, &query
->qAv4
);
5930 // Only do the AAAA query if this machine actually has IPv6 active
5931 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5933 else if (query
->ServiceInfoQueryCallback
&& query
->GotADD
&& query
->GotTXT
&& PortChanged
)
5935 if (++query
->Answers
>= 100)
5936 debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
5937 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.srv
.target
.c
,
5938 mDNSVal16(answer
->rdata
->u
.srv
.port
));
5939 query
->ServiceInfoQueryCallback(m
, query
);
5941 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5942 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5945 mDNSlocal
void FoundServiceInfoTXT(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
5947 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5948 if (!AddRecord
) return;
5949 if (answer
->rrtype
!= kDNSType_TXT
) return;
5950 if (answer
->rdlength
> sizeof(query
->info
->TXTinfo
)) return;
5952 query
->GotTXT
= mDNStrue
;
5953 query
->info
->TXTlen
= answer
->rdlength
;
5954 query
->info
->TXTinfo
[0] = 0; // In case answer->rdlength is zero
5955 mDNSPlatformMemCopy(answer
->rdata
->u
.txt
.c
, query
->info
->TXTinfo
, answer
->rdlength
);
5957 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query
->info
->name
.c
, query
->GotADD
);
5959 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5960 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5961 if (query
->ServiceInfoQueryCallback
&& query
->GotADD
)
5963 if (++query
->Answers
>= 100)
5964 debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
5965 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.txt
.c
);
5966 query
->ServiceInfoQueryCallback(m
, query
);
5970 mDNSlocal
void FoundServiceInfo(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
5972 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5973 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
5974 if (!AddRecord
) return;
5976 if (answer
->rrtype
== kDNSType_A
)
5978 query
->info
->ip
.type
= mDNSAddrType_IPv4
;
5979 query
->info
->ip
.ip
.v4
= answer
->rdata
->u
.ipv4
;
5981 else if (answer
->rrtype
== kDNSType_AAAA
)
5983 query
->info
->ip
.type
= mDNSAddrType_IPv6
;
5984 query
->info
->ip
.ip
.v6
= answer
->rdata
->u
.ipv6
;
5988 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer
->name
->c
, answer
->rrtype
, DNSTypeName(answer
->rrtype
));
5992 query
->GotADD
= mDNStrue
;
5993 query
->info
->InterfaceID
= answer
->InterfaceID
;
5995 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query
->info
->ip
.type
, query
->info
->name
.c
, query
->GotTXT
);
5997 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5998 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5999 if (query
->ServiceInfoQueryCallback
&& query
->GotTXT
)
6001 if (++query
->Answers
>= 100)
6003 if (answer
->rrtype
== kDNSType_A
)
6004 debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.ipv4
);
6006 debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.ipv6
);
6008 query
->ServiceInfoQueryCallback(m
, query
);
6012 // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
6013 // If the query is not interface-specific, then InterfaceID may be zero
6014 // Each time the Callback is invoked, the remainder of the fields will have been filled in
6015 // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
6016 mDNSexport mStatus
mDNS_StartResolveService(mDNS
*const m
,
6017 ServiceInfoQuery
*query
, ServiceInfo
*info
, mDNSServiceInfoQueryCallback
*Callback
, void *Context
)
6022 query
->qSRV
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6023 query
->qSRV
.InterfaceID
= info
->InterfaceID
;
6024 query
->qSRV
.Target
= zeroAddr
;
6025 AssignDomainName(&query
->qSRV
.qname
, &info
->name
);
6026 query
->qSRV
.qtype
= kDNSType_SRV
;
6027 query
->qSRV
.qclass
= kDNSClass_IN
;
6028 query
->qSRV
.LongLived
= mDNSfalse
;
6029 query
->qSRV
.ExpectUnique
= mDNStrue
;
6030 query
->qSRV
.ForceMCast
= mDNSfalse
;
6031 query
->qSRV
.QuestionCallback
= FoundServiceInfoSRV
;
6032 query
->qSRV
.QuestionContext
= query
;
6034 query
->qTXT
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6035 query
->qTXT
.InterfaceID
= info
->InterfaceID
;
6036 query
->qTXT
.Target
= zeroAddr
;
6037 AssignDomainName(&query
->qTXT
.qname
, &info
->name
);
6038 query
->qTXT
.qtype
= kDNSType_TXT
;
6039 query
->qTXT
.qclass
= kDNSClass_IN
;
6040 query
->qTXT
.LongLived
= mDNSfalse
;
6041 query
->qTXT
.ExpectUnique
= mDNStrue
;
6042 query
->qTXT
.ForceMCast
= mDNSfalse
;
6043 query
->qTXT
.QuestionCallback
= FoundServiceInfoTXT
;
6044 query
->qTXT
.QuestionContext
= query
;
6046 query
->qAv4
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6047 query
->qAv4
.InterfaceID
= info
->InterfaceID
;
6048 query
->qAv4
.Target
= zeroAddr
;
6049 query
->qAv4
.qname
.c
[0] = 0;
6050 query
->qAv4
.qtype
= kDNSType_A
;
6051 query
->qAv4
.qclass
= kDNSClass_IN
;
6052 query
->qAv4
.LongLived
= mDNSfalse
;
6053 query
->qAv4
.ExpectUnique
= mDNStrue
;
6054 query
->qAv4
.ForceMCast
= mDNSfalse
;
6055 query
->qAv4
.QuestionCallback
= FoundServiceInfo
;
6056 query
->qAv4
.QuestionContext
= query
;
6058 query
->qAv6
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6059 query
->qAv6
.InterfaceID
= info
->InterfaceID
;
6060 query
->qAv6
.Target
= zeroAddr
;
6061 query
->qAv6
.qname
.c
[0] = 0;
6062 query
->qAv6
.qtype
= kDNSType_AAAA
;
6063 query
->qAv6
.qclass
= kDNSClass_IN
;
6064 query
->qAv6
.LongLived
= mDNSfalse
;
6065 query
->qAv6
.ExpectUnique
= mDNStrue
;
6066 query
->qAv6
.ForceMCast
= mDNSfalse
;
6067 query
->qAv6
.QuestionCallback
= FoundServiceInfo
;
6068 query
->qAv6
.QuestionContext
= query
;
6070 query
->GotSRV
= mDNSfalse
;
6071 query
->GotTXT
= mDNSfalse
;
6072 query
->GotADD
= mDNSfalse
;
6076 query
->ServiceInfoQueryCallback
= Callback
;
6077 query
->ServiceInfoQueryContext
= Context
;
6079 // info->name = Must already be set up by client
6080 // info->interface = Must already be set up by client
6081 info
->ip
= zeroAddr
;
6082 info
->port
= zeroIPPort
;
6085 // We use mDNS_StartQuery_internal here because we're already holding the lock
6086 status
= mDNS_StartQuery_internal(m
, &query
->qSRV
);
6087 if (status
== mStatus_NoError
) status
= mDNS_StartQuery_internal(m
, &query
->qTXT
);
6088 if (status
!= mStatus_NoError
) mDNS_StopResolveService(m
, query
);
6094 mDNSexport
void mDNS_StopResolveService (mDNS
*const m
, ServiceInfoQuery
*q
)
6097 // We use mDNS_StopQuery_internal here because we're already holding the lock
6098 if (q
->qSRV
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qSRV
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qSRV
);
6099 if (q
->qTXT
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qTXT
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qTXT
);
6100 if (q
->qAv4
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qAv4
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qAv4
);
6101 if (q
->qAv6
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qAv6
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qAv6
);
6105 mDNSexport mStatus
mDNS_GetDomains(mDNS
*const m
, DNSQuestion
*const question
, mDNS_DomainType DomainType
, const domainname
*dom
,
6106 const mDNSInterfaceID InterfaceID
, mDNSQuestionCallback
*Callback
, void *Context
)
6108 question
->InterfaceID
= InterfaceID
;
6109 question
->Target
= zeroAddr
;
6110 question
->qtype
= kDNSType_PTR
;
6111 question
->qclass
= kDNSClass_IN
;
6112 question
->LongLived
= mDNSfalse
;
6113 question
->ExpectUnique
= mDNSfalse
;
6114 question
->ForceMCast
= mDNSfalse
;
6115 question
->QuestionCallback
= Callback
;
6116 question
->QuestionContext
= Context
;
6117 if (DomainType
> mDNS_DomainTypeMax
) return(mStatus_BadParamErr
);
6118 if (!MakeDomainNameFromDNSNameString(&question
->qname
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
6119 if (!dom
) dom
= &localdomain
;
6120 if (!AppendDomainName(&question
->qname
, dom
)) return(mStatus_BadParamErr
);
6121 return(mDNS_StartQuery(m
, question
));
6124 // ***************************************************************************
6125 #if COMPILER_LIKES_PRAGMA_MARK
6127 #pragma mark - Responder Functions
6130 mDNSexport mStatus
mDNS_Register(mDNS
*const m
, AuthRecord
*const rr
)
6134 status
= mDNS_Register_internal(m
, rr
);
6139 mDNSexport mStatus
mDNS_Update(mDNS
*const m
, AuthRecord
*const rr
, mDNSu32 newttl
,
6140 const mDNSu16 newrdlength
, RData
*const newrdata
, mDNSRecordUpdateCallback
*Callback
)
6142 #ifndef UNICAST_DISABLED
6143 mDNSBool unicast
= !(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(rr
->resrec
.name
));
6145 mDNSBool unicast
= mDNSfalse
;
6148 if (!ValidateRData(rr
->resrec
.rrtype
, newrdlength
, newrdata
))
6149 { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr
->resrec
, &newrdata
->u
, m
->MsgBuffer
)); return(mStatus_Invalid
); }
6153 // If TTL is unspecified, leave TTL unchanged
6154 if (newttl
== 0) newttl
= rr
->resrec
.rroriginalttl
;
6156 // If we already have an update queued up which has not gone through yet,
6157 // give the client a chance to free that memory
6158 if (!unicast
&& rr
->NewRData
)
6160 RData
*n
= rr
->NewRData
;
6161 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
6162 if (rr
->UpdateCallback
)
6163 rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
6166 rr
->NewRData
= newrdata
;
6167 rr
->newrdlength
= newrdlength
;
6168 rr
->UpdateCallback
= Callback
;
6170 if (unicast
) { mStatus status
= uDNS_UpdateRecord(m
, rr
); mDNS_Unlock(m
); return(status
); }
6172 if (rr
->resrec
.rroriginalttl
== newttl
&& rr
->resrec
.rdlength
== newrdlength
&& mDNSPlatformMemSame(rr
->resrec
.rdata
->u
.data
, newrdata
->u
.data
, newrdlength
))
6173 CompleteRDataUpdate(m
, rr
);
6177 domainname type
, domain
;
6178 DeconstructServiceName(rr
->resrec
.name
, &name
, &type
, &domain
);
6179 rr
->AnnounceCount
= InitialAnnounceCount
;
6180 // iChat often does suprious record updates where no data has changed. For the _presence service type, using
6181 // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
6182 // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
6183 // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
6184 // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
6185 if (SameDomainLabel(type
.c
, (mDNSu8
*)"\x6_ichat")) rr
->AnnounceCount
= 1;
6186 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6187 InitializeLastAPTime(m
, rr
);
6188 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
6189 if (!rr
->UpdateBlocked
&& rr
->UpdateCredits
) rr
->UpdateCredits
--;
6190 if (!rr
->NextUpdateCredit
) rr
->NextUpdateCredit
= NonZeroTime(m
->timenow
+ kUpdateCreditRefreshInterval
);
6191 if (rr
->AnnounceCount
> rr
->UpdateCredits
+ 1) rr
->AnnounceCount
= (mDNSu8
)(rr
->UpdateCredits
+ 1);
6192 if (rr
->UpdateCredits
<= 5)
6194 mDNSu32 delay
= 6 - rr
->UpdateCredits
; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
6195 if (!rr
->UpdateBlocked
) rr
->UpdateBlocked
= NonZeroTime(m
->timenow
+ (mDNSs32
)delay
* mDNSPlatformOneSecond
);
6196 rr
->ThisAPInterval
*= 4;
6197 rr
->LastAPTime
= rr
->UpdateBlocked
- rr
->ThisAPInterval
;
6198 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr
->resrec
.name
->c
, delay
, delay
> 1 ? "s" : "");
6200 rr
->resrec
.rroriginalttl
= newttl
;
6204 return(mStatus_NoError
);
6207 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
6208 // the record list and/or question list.
6209 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6210 mDNSexport mStatus
mDNS_Deregister(mDNS
*const m
, AuthRecord
*const rr
)
6214 status
= mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
6219 mDNSexport
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
);
6221 mDNSlocal NetworkInterfaceInfo
*FindFirstAdvertisedInterface(mDNS
*const m
)
6223 NetworkInterfaceInfo
*intf
;
6224 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6225 if (intf
->Advertise
) break;
6229 mDNSlocal
void AdvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6232 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
6233 if (!primary
) primary
= set
; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
6235 // Send dynamic update for non-linklocal IPv4 Addresses
6236 mDNS_SetupResourceRecord(&set
->RR_A
, mDNSNULL
, set
->InterfaceID
, kDNSType_A
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNS_HostNameCallback
, set
);
6237 mDNS_SetupResourceRecord(&set
->RR_PTR
, mDNSNULL
, set
->InterfaceID
, kDNSType_PTR
, kHostNameTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
6238 mDNS_SetupResourceRecord(&set
->RR_HINFO
, mDNSNULL
, set
->InterfaceID
, kDNSType_HINFO
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
6240 #if ANSWER_REMOTE_HOSTNAME_QUERIES
6241 set
->RR_A
.AllowRemoteQuery
= mDNStrue
;
6242 set
->RR_PTR
.AllowRemoteQuery
= mDNStrue
;
6243 set
->RR_HINFO
.AllowRemoteQuery
= mDNStrue
;
6245 // 1. Set up Address record to map from host name ("foo.local.") to IP address
6246 // 2. Set up reverse-lookup PTR record to map from our address back to our host name
6247 AssignDomainName(set
->RR_A
.resrec
.name
, &m
->MulticastHostname
);
6248 if (set
->ip
.type
== mDNSAddrType_IPv4
)
6250 set
->RR_A
.resrec
.rrtype
= kDNSType_A
;
6251 set
->RR_A
.resrec
.rdata
->u
.ipv4
= set
->ip
.ip
.v4
;
6252 // Note: This is reverse order compared to a normal dotted-decimal IP address
6253 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.",
6254 set
->ip
.ip
.v4
.b
[3], set
->ip
.ip
.v4
.b
[2], set
->ip
.ip
.v4
.b
[1], set
->ip
.ip
.v4
.b
[0]);
6256 else if (set
->ip
.type
== mDNSAddrType_IPv6
)
6259 set
->RR_A
.resrec
.rrtype
= kDNSType_AAAA
;
6260 set
->RR_A
.resrec
.rdata
->u
.ipv6
= set
->ip
.ip
.v6
;
6261 for (i
= 0; i
< 16; i
++)
6263 static const char hexValues
[] = "0123456789ABCDEF";
6264 buffer
[i
* 4 ] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] & 0x0F];
6265 buffer
[i
* 4 + 1] = '.';
6266 buffer
[i
* 4 + 2] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] >> 4];
6267 buffer
[i
* 4 + 3] = '.';
6269 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
6272 MakeDomainNameFromDNSNameString(set
->RR_PTR
.resrec
.name
, buffer
);
6273 set
->RR_PTR
.HostTarget
= mDNStrue
; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
6274 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
6276 set
->RR_A
.RRSet
= &primary
->RR_A
; // May refer to self
6278 mDNS_Register_internal(m
, &set
->RR_A
);
6279 mDNS_Register_internal(m
, &set
->RR_PTR
);
6281 if (m
->HIHardware
.c
[0] > 0 && m
->HISoftware
.c
[0] > 0 && m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0] <= 254)
6283 mDNSu8
*p
= set
->RR_HINFO
.resrec
.rdata
->u
.data
;
6284 AssignDomainName(set
->RR_HINFO
.resrec
.name
, &m
->MulticastHostname
);
6285 set
->RR_HINFO
.DependentOn
= &set
->RR_A
;
6286 mDNSPlatformMemCopy(&m
->HIHardware
, p
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
6288 mDNSPlatformMemCopy(&m
->HISoftware
, p
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
6289 mDNS_Register_internal(m
, &set
->RR_HINFO
);
6293 debugf("Not creating HINFO record: platform support layer provided no information");
6294 set
->RR_HINFO
.resrec
.RecordType
= kDNSRecordTypeUnregistered
;
6298 mDNSlocal
void DeadvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6300 NetworkInterfaceInfo
*intf
;
6302 // If we still have address records referring to this one, update them
6303 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
6304 AuthRecord
*A
= primary
? &primary
->RR_A
: mDNSNULL
;
6305 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6306 if (intf
->RR_A
.RRSet
== &set
->RR_A
)
6307 intf
->RR_A
.RRSet
= A
;
6309 // Unregister these records.
6310 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
6311 // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
6312 // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
6313 // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
6314 if (set
->RR_A
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_A
, mDNS_Dereg_normal
);
6315 if (set
->RR_PTR
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_PTR
, mDNS_Dereg_normal
);
6316 if (set
->RR_HINFO
.resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_HINFO
, mDNS_Dereg_normal
);
6319 mDNSexport
void mDNS_SetFQDN(mDNS
*const m
)
6321 domainname newmname
;
6322 NetworkInterfaceInfo
*intf
;
6326 if (!AppendDomainLabel(&newmname
, &m
->hostlabel
)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6327 if (!AppendLiteralLabelString(&newmname
, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6328 if (SameDomainName(&m
->MulticastHostname
, &newmname
)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
6331 AssignDomainName(&m
->MulticastHostname
, &newmname
);
6333 // 1. Stop advertising our address records on all interfaces
6334 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6335 if (intf
->Advertise
) DeadvertiseInterface(m
, intf
);
6337 // 2. Start advertising our address records using the new name
6338 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6339 if (intf
->Advertise
) AdvertiseInterface(m
, intf
);
6341 // 3. Make sure that any SRV records (and the like) that reference our
6342 // host name in their rdata get updated to reference this new host name
6343 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) if (rr
->HostTarget
) SetTargetToHostName(m
, rr
);
6344 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
) if (rr
->HostTarget
) SetTargetToHostName(m
, rr
);
6349 mDNSexport
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6351 (void)rr
; // Unused parameter
6355 char *msg
= "Unknown result";
6356 if (result
== mStatus_NoError
) msg
= "Name registered";
6357 else if (result
== mStatus_NameConflict
) msg
= "Name conflict";
6358 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6362 if (result
== mStatus_NoError
)
6364 // Notify the client that the host name is successfully registered
6365 if (m
->MainCallback
)
6367 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
6368 m
->MainCallback(m
, result
);
6369 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
6372 else if (result
== mStatus_NameConflict
)
6374 domainlabel oldlabel
= m
->hostlabel
;
6376 // 1. First give the client callback a chance to pick a new name
6377 if (m
->MainCallback
)
6379 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
6380 m
->MainCallback(m
, mStatus_NameConflict
);
6381 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
6384 // 2. If the client callback didn't do it, add (or increment) an index ourselves
6385 if (SameDomainLabel(m
->hostlabel
.c
, oldlabel
.c
))
6386 IncrementLabelSuffix(&m
->hostlabel
, mDNSfalse
);
6388 // 3. Generate the FQDNs from the hostlabel,
6389 // and make sure all SRV records, etc., are updated to reference our new hostname
6391 LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel
.c
, m
->hostlabel
.c
);
6393 else if (result
== mStatus_MemFree
)
6395 // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
6396 // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
6397 debugf("mDNS_HostNameCallback: MemFree (ignored)");
6400 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result
, rr
->resrec
.name
->c
);
6403 mDNSlocal
void UpdateInterfaceProtocols(mDNS
*const m
, NetworkInterfaceInfo
*active
)
6405 NetworkInterfaceInfo
*intf
;
6406 active
->IPv4Available
= mDNSfalse
;
6407 active
->IPv6Available
= mDNSfalse
;
6408 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6409 if (intf
->InterfaceID
== active
->InterfaceID
)
6411 if (intf
->ip
.type
== mDNSAddrType_IPv4
&& intf
->McastTxRx
) active
->IPv4Available
= mDNStrue
;
6412 if (intf
->ip
.type
== mDNSAddrType_IPv6
&& intf
->McastTxRx
) active
->IPv6Available
= mDNStrue
;
6416 mDNSexport mStatus
mDNS_RegisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, mDNSs32 delay
)
6418 mDNSBool FirstOfType
= mDNStrue
;
6419 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6421 if (!set
->InterfaceID
)
6422 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set
->ip
); return(mStatus_Invalid
); }
6424 if (!mDNSAddressIsValidNonZero(&set
->mask
))
6425 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set
->ip
, &set
->mask
); return(mStatus_Invalid
); }
6429 // Assume this interface will be active
6430 set
->InterfaceActive
= mDNStrue
;
6431 set
->IPv4Available
= (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
);
6432 set
->IPv6Available
= (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
);
6438 LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
6440 return(mStatus_AlreadyRegistered
);
6443 // This InterfaceID is already in the list, so mark this interface inactive for now
6444 if ((*p
)->InterfaceID
== set
->InterfaceID
)
6446 set
->InterfaceActive
= mDNSfalse
;
6447 if (set
->ip
.type
== (*p
)->ip
.type
) FirstOfType
= mDNSfalse
;
6448 if (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
) (*p
)->IPv4Available
= mDNStrue
;
6449 if (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
) (*p
)->IPv6Available
= mDNStrue
;
6455 set
->next
= mDNSNULL
;
6459 AdvertiseInterface(m
, set
);
6461 debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set
->InterfaceID
, &set
->ip
,
6462 set
->InterfaceActive
?
6463 "not represented in list; marking active and retriggering queries" :
6464 "already represented in list; marking inactive for now");
6466 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6467 // giving the false impression that there's an active representative of this interface when there really isn't.
6468 // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
6469 // even if we believe that we previously had an active representative of this interface.
6470 if (set
->McastTxRx
&& ((m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) || FirstOfType
|| set
->InterfaceActive
))
6474 mDNSs32 initial
= InitialQuestionInterval
;
6476 // Use a small amount of randomness:
6477 // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at
6478 // exactly the same time, we don't want them to all go and hit the network with identical queries at exactly the same moment.
6479 if (!m
->SuppressSending
) m
->SuppressSending
= m
->timenow
+ (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
6483 LogMsg("Repeated transitions for interface %s (%#a); delaying packets by %d seconds",
6484 set
->ifname
, &set
->ip
, delay
/mDNSPlatformOneSecond
);
6485 initial
= InitialQuestionInterval
* 8; // Delay between first and second queries is eight seconds
6486 if (!m
->SuppressProbes
||
6487 m
->SuppressProbes
- (m
->timenow
+ delay
) < 0)
6488 m
->SuppressProbes
= (m
->timenow
+ delay
);
6490 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
6491 if (!q
->InterfaceID
|| q
->InterfaceID
== set
->InterfaceID
) // If non-specific Q, or Q on this specific interface,
6492 { // then reactivate this question
6493 q
->ThisQInterval
= initial
; // MUST be > zero for an active question
6494 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
6495 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
+ delay
;
6496 q
->RecentAnswerPkts
= 0;
6497 SetNextQueryTime(m
,q
);
6500 // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
6501 // we now need them to re-probe if necessary, and then re-announce.
6502 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
6503 if (!rr
->resrec
.InterfaceID
|| rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6505 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
6506 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
6507 rr
->AnnounceCount
= delay
? (mDNSu8
)1 : InitialAnnounceCount
;
6508 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6509 InitializeLastAPTime(m
, rr
);
6514 return(mStatus_NoError
);
6517 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
6518 // the record list and/or question list.
6519 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6520 mDNSexport
void mDNS_DeregisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6522 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6524 mDNSBool revalidate
= mDNSfalse
;
6525 // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
6526 // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
6527 // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
6528 if (m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) revalidate
= mDNStrue
;
6532 // Find this record in our list
6533 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
6534 if (!*p
) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m
); return; }
6536 // Unlink this record from our list
6538 set
->next
= mDNSNULL
;
6540 if (!set
->InterfaceActive
)
6542 // If this interface not the active member of its set, update the v4/v6Available flags for the active member
6543 NetworkInterfaceInfo
*intf
;
6544 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6545 if (intf
->InterfaceActive
&& intf
->InterfaceID
== set
->InterfaceID
)
6546 UpdateInterfaceProtocols(m
, intf
);
6550 NetworkInterfaceInfo
*intf
;
6551 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6552 if (intf
->InterfaceID
== set
->InterfaceID
)
6556 debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
6558 intf
->InterfaceActive
= mDNStrue
;
6559 UpdateInterfaceProtocols(m
, intf
);
6561 // See if another representative *of the same type* exists. If not, we mave have gone from
6562 // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
6563 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6564 if (intf
->InterfaceID
== set
->InterfaceID
&& intf
->ip
.type
== set
->ip
.type
)
6566 if (!intf
) revalidate
= mDNStrue
;
6574 debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
6577 // 1. Deactivate any questions specific to this interface
6578 for (q
= m
->Questions
; q
; q
=q
->next
)
6579 if (q
->InterfaceID
== set
->InterfaceID
)
6580 q
->ThisQInterval
= 0;
6582 // 2. Flush any cache records received on this interface
6583 revalidate
= mDNSfalse
; // Don't revalidate if we're flushing the records
6584 FORALL_CACHERECORDS(slot
, cg
, rr
)
6585 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6586 PurgeCacheResourceRecord(m
, rr
);
6590 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6591 if (set
->Advertise
) DeadvertiseInterface(m
, set
);
6593 // If we have any cache records received on this interface that went away, then re-verify them.
6594 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6595 // giving the false impression that there's an active representative of this interface when there really isn't.
6596 // Don't need to do this when shutting down, because *all* interfaces are about to go away
6597 if (revalidate
&& !m
->mDNS_shutdown
)
6602 m
->NextCacheCheck
= m
->timenow
;
6603 FORALL_CACHERECORDS(slot
, cg
, rr
)
6604 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6605 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForCableDisconnect
);
6611 mDNSlocal
void ServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6613 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6614 (void)m
; // Unused parameter
6618 char *msg
= "Unknown result";
6619 if (result
== mStatus_NoError
) msg
= "Name Registered";
6620 else if (result
== mStatus_NameConflict
) msg
= "Name Conflict";
6621 else if (result
== mStatus_MemFree
) msg
= "Memory Free";
6622 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6626 // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
6627 if (result
== mStatus_NoError
&& rr
!= &sr
->RR_SRV
) return;
6629 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6630 if (result
== mStatus_NameConflict
)
6632 sr
->Conflict
= mDNStrue
; // Record that this service set had a conflict
6633 mDNS_DeregisterService(m
, sr
); // Unlink the records from our list
6637 if (result
== mStatus_MemFree
)
6639 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
6640 // don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
6642 if (sr
->RR_PTR
.resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6643 for (i
=0; i
<sr
->NumSubTypes
; i
++) if (sr
->SubTypes
[i
].resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6645 // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
6646 // then we can now report the NameConflict to the client
6647 if (sr
->Conflict
) result
= mStatus_NameConflict
;
6650 // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
6651 // function is allowed to do anything, including deregistering this service and freeing its memory.
6652 if (sr
->ServiceCallback
)
6653 sr
->ServiceCallback(m
, sr
, result
);
6656 mDNSlocal
void NSSCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6658 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6659 if (sr
->ServiceCallback
)
6660 sr
->ServiceCallback(m
, sr
, result
);
6664 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
6665 // Type is service type (e.g. "_ipp._tcp.")
6666 // Domain is fully qualified domain name (i.e. ending with a null label)
6667 // We always register a TXT, even if it is empty (so that clients are not
6668 // left waiting forever looking for a nonexistent record.)
6669 // If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6670 // then the default host name (m->MulticastHostname) is automatically used
6671 mDNSexport mStatus
mDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*sr
,
6672 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6673 const domainname
*const host
, mDNSIPPort port
, const mDNSu8 txtinfo
[], mDNSu16 txtlen
,
6674 AuthRecord
*SubTypes
, mDNSu32 NumSubTypes
,
6675 const mDNSInterfaceID InterfaceID
, mDNSServiceCallback Callback
, void *Context
)
6680 sr
->ServiceCallback
= Callback
;
6681 sr
->ServiceContext
= Context
;
6682 sr
->Extras
= mDNSNULL
;
6683 sr
->NumSubTypes
= NumSubTypes
;
6684 sr
->SubTypes
= SubTypes
;
6685 sr
->Conflict
= mDNSfalse
;
6686 if (host
&& host
->c
[0]) sr
->Host
= *host
;
6687 else sr
->Host
.c
[0] = 0;
6689 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6690 if (!port
.NotAnInteger
) return(mDNS_RegisterNoSuchService(m
, &sr
->RR_SRV
, name
, type
, domain
, mDNSNULL
, mDNSInterface_Any
, NSSCallback
, sr
));
6692 // Initialize the AuthRecord objects to sane values
6693 mDNS_SetupResourceRecord(&sr
->RR_ADV
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeAdvisory
, ServiceCallback
, sr
);
6694 mDNS_SetupResourceRecord(&sr
->RR_PTR
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6695 mDNS_SetupResourceRecord(&sr
->RR_SRV
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6696 mDNS_SetupResourceRecord(&sr
->RR_TXT
, mDNSNULL
, InterfaceID
, kDNSType_TXT
, kStandardTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6698 // If the client is registering an oversized TXT record,
6699 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
6700 if (sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
< txtlen
)
6701 sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
= txtlen
;
6703 // Set up the record names
6704 // For now we only create an advisory record for the main type, not for subtypes
6705 // We need to gain some operational experience before we decide if there's a need to create them for subtypes too
6706 if (ConstructServiceName(sr
->RR_ADV
.resrec
.name
, (domainlabel
*)"\x09_services", (domainname
*)"\x07_dns-sd\x04_udp", domain
) == mDNSNULL
)
6707 return(mStatus_BadParamErr
);
6708 if (ConstructServiceName(sr
->RR_PTR
.resrec
.name
, mDNSNULL
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6709 if (ConstructServiceName(sr
->RR_SRV
.resrec
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6710 AssignDomainName(sr
->RR_TXT
.resrec
.name
, sr
->RR_SRV
.resrec
.name
);
6712 // 1. Set up the ADV record rdata to advertise our service type
6713 AssignDomainName(&sr
->RR_ADV
.resrec
.rdata
->u
.name
, sr
->RR_PTR
.resrec
.name
);
6715 // 2. Set up the PTR record rdata to point to our service name
6716 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6717 AssignDomainName(&sr
->RR_PTR
.resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6718 sr
->RR_PTR
.Additional1
= &sr
->RR_SRV
;
6719 sr
->RR_PTR
.Additional2
= &sr
->RR_TXT
;
6721 // 2a. Set up any subtype PTRs to point to our service name
6722 // If the client is using subtypes, it is the client's responsibility to have
6723 // already set the first label of the record name to the subtype being registered
6724 for (i
=0; i
<NumSubTypes
; i
++)
6727 AssignDomainName(&st
, sr
->SubTypes
[i
].resrec
.name
);
6728 st
.c
[1+st
.c
[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
6729 AppendDomainName(&st
, type
);
6730 mDNS_SetupResourceRecord(&sr
->SubTypes
[i
], mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6731 if (ConstructServiceName(sr
->SubTypes
[i
].resrec
.name
, mDNSNULL
, &st
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6732 AssignDomainName(&sr
->SubTypes
[i
].resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6733 sr
->SubTypes
[i
].Additional1
= &sr
->RR_SRV
;
6734 sr
->SubTypes
[i
].Additional2
= &sr
->RR_TXT
;
6737 // 3. Set up the SRV record rdata.
6738 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.priority
= 0;
6739 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.weight
= 0;
6740 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
= port
;
6742 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6743 if (sr
->Host
.c
[0]) AssignDomainName(&sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
, &sr
->Host
);
6744 else { sr
->RR_SRV
.HostTarget
= mDNStrue
; sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
.c
[0] = '\0'; }
6746 // 4. Set up the TXT record rdata,
6747 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6748 if (txtinfo
== mDNSNULL
) sr
->RR_TXT
.resrec
.rdlength
= 0;
6749 else if (txtinfo
!= sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
)
6751 sr
->RR_TXT
.resrec
.rdlength
= txtlen
;
6752 if (sr
->RR_TXT
.resrec
.rdlength
> sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
) return(mStatus_BadParamErr
);
6753 mDNSPlatformMemCopy(txtinfo
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, txtlen
);
6755 sr
->RR_TXT
.DependentOn
= &sr
->RR_SRV
;
6757 #ifndef UNICAST_DISABLED
6758 // If the client has specified an explicit InterfaceID,
6759 // then we do a multicast registration on that interface, even for unicast domains.
6760 if (!(InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6764 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6765 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6766 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6767 // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
6768 if (!sr
->RR_TXT
.resrec
.rdlength
) { sr
->RR_TXT
.resrec
.rdlength
= 1; sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6769 status
= uDNS_RegisterService(m
, sr
);
6775 err
= mDNS_Register_internal(m
, &sr
->RR_SRV
);
6776 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_TXT
);
6777 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
6778 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
6779 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
6780 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
6781 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
6782 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_ADV
);
6783 for (i
=0; i
<NumSubTypes
; i
++) if (!err
) err
= mDNS_Register_internal(m
, &sr
->SubTypes
[i
]);
6784 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_PTR
);
6788 if (err
) mDNS_DeregisterService(m
, sr
);
6792 mDNSexport mStatus
mDNS_AddRecordToService(mDNS
*const m
, ServiceRecordSet
*sr
,
6793 ExtraResourceRecord
*extra
, RData
*rdata
, mDNSu32 ttl
)
6795 ExtraResourceRecord
**e
;
6798 extra
->next
= mDNSNULL
;
6799 mDNS_SetupResourceRecord(&extra
->r
, rdata
, sr
->RR_PTR
.resrec
.InterfaceID
, extra
->r
.resrec
.rrtype
, ttl
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6800 AssignDomainName(extra
->r
.resrec
.name
, sr
->RR_SRV
.resrec
.name
);
6802 #ifndef UNICAST_DISABLED
6803 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6806 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6807 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6808 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6809 // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck)
6810 if (extra
->r
.resrec
.rrtype
== kDNSType_TXT
&& extra
->r
.resrec
.rdlength
== 0)
6811 { extra
->r
.resrec
.rdlength
= 1; extra
->r
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6812 status
= uDNS_AddRecordToService(m
, sr
, extra
);
6820 while (*e
) e
= &(*e
)->next
;
6822 if (ttl
== 0) ttl
= kStandardTTL
;
6824 extra
->r
.DependentOn
= &sr
->RR_SRV
;
6826 debugf("mDNS_AddRecordToService adding record to %##s", extra
->r
.resrec
.name
->c
);
6828 status
= mDNS_Register_internal(m
, &extra
->r
);
6829 if (status
== mStatus_NoError
) *e
= extra
;
6834 mDNSexport mStatus
mDNS_RemoveRecordFromService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
, mDNSRecordCallback MemFreeCallback
, void *Context
)
6836 ExtraResourceRecord
**e
;
6841 while (*e
&& *e
!= extra
) e
= &(*e
)->next
;
6844 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra
->r
.resrec
.name
->c
);
6845 status
= mStatus_BadReferenceErr
;
6849 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra
->r
.resrec
.name
->c
);
6850 extra
->r
.RecordCallback
= MemFreeCallback
;
6851 extra
->r
.RecordContext
= Context
;
6853 #ifndef UNICAST_DISABLED
6854 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6855 status
= uDNS_DeregisterRecord(m
, &extra
->r
);
6858 status
= mDNS_Deregister_internal(m
, &extra
->r
, mDNS_Dereg_normal
);
6864 mDNSexport mStatus
mDNS_RenameAndReregisterService(mDNS
*const m
, ServiceRecordSet
*const sr
, const domainlabel
*newname
)
6866 // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
6867 // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
6868 domainlabel name1
, name2
;
6869 domainname type
, domain
;
6870 domainname
*host
= mDNSNULL
;
6871 ExtraResourceRecord
*extras
= sr
->Extras
;
6874 DeconstructServiceName(sr
->RR_SRV
.resrec
.name
, &name1
, &type
, &domain
);
6878 IncrementLabelSuffix(&name2
, mDNStrue
);
6881 LogMsg("Service \"%##s\" renamed to \"%#s\"", sr
->RR_SRV
.resrec
.name
->c
, newname
->c
);
6882 if (sr
->RR_SRV
.HostTarget
== mDNSfalse
&& sr
->Host
.c
[0]) host
= &sr
->Host
;
6884 err
= mDNS_RegisterService(m
, sr
, newname
, &type
, &domain
,
6885 host
, sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, sr
->RR_TXT
.resrec
.rdlength
,
6886 sr
->SubTypes
, sr
->NumSubTypes
,
6887 sr
->RR_PTR
.resrec
.InterfaceID
, sr
->ServiceCallback
, sr
->ServiceContext
);
6889 // mDNS_RegisterService() just reset sr->Extras to NULL.
6890 // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
6891 // through the old list of extra records, and re-add them to our freshly created service registration
6892 while (!err
&& extras
)
6894 ExtraResourceRecord
*e
= extras
;
6895 extras
= extras
->next
;
6896 err
= mDNS_AddRecordToService(m
, sr
, e
, e
->r
.resrec
.rdata
, e
->r
.resrec
.rroriginalttl
);
6902 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
6903 // which may change the record list and/or question list.
6904 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6905 mDNSexport mStatus
mDNS_DeregisterService(mDNS
*const m
, ServiceRecordSet
*sr
)
6907 // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
6908 if (!sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
.NotAnInteger
) return(mDNS_DeregisterNoSuchService(m
, &sr
->RR_SRV
));
6910 #ifndef UNICAST_DISABLED
6911 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6915 status
= uDNS_DeregisterService(m
, sr
);
6920 if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeUnregistered
)
6922 debugf("Service set for %##s already deregistered", sr
->RR_SRV
.resrec
.name
->c
);
6923 return(mStatus_BadReferenceErr
);
6925 else if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeDeregistering
)
6927 debugf("Service set for %##s already in the process of deregistering", sr
->RR_SRV
.resrec
.name
->c
);
6928 return(mStatus_NoError
);
6934 ExtraResourceRecord
*e
;
6938 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
6939 // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
6940 mDNS_Deregister_internal(m
, &sr
->RR_SRV
, mDNS_Dereg_repeat
);
6941 mDNS_Deregister_internal(m
, &sr
->RR_TXT
, mDNS_Dereg_repeat
);
6943 mDNS_Deregister_internal(m
, &sr
->RR_ADV
, mDNS_Dereg_normal
);
6945 // We deregister all of the extra records, but we leave the sr->Extras list intact
6946 // in case the client wants to do a RenameAndReregister and reinstate the registration
6949 mDNS_Deregister_internal(m
, &e
->r
, mDNS_Dereg_repeat
);
6953 for (i
=0; i
<sr
->NumSubTypes
; i
++)
6954 mDNS_Deregister_internal(m
, &sr
->SubTypes
[i
], mDNS_Dereg_normal
);
6956 // Be sure to deregister the PTR last!
6957 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
6958 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
6959 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
6960 // we've deregistered all our records and done any other necessary cleanup before that happens.
6961 status
= mDNS_Deregister_internal(m
, &sr
->RR_PTR
, mDNS_Dereg_normal
);
6967 // Create a registration that asserts that no such service exists with this name.
6968 // This can be useful where there is a given function is available through several protocols.
6969 // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
6970 // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
6971 // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
6972 // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
6973 mDNSexport mStatus
mDNS_RegisterNoSuchService(mDNS
*const m
, AuthRecord
*const rr
,
6974 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6975 const domainname
*const host
,
6976 const mDNSInterfaceID InterfaceID
, mDNSRecordCallback Callback
, void *Context
)
6978 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, Callback
, Context
);
6979 if (ConstructServiceName(rr
->resrec
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6980 rr
->resrec
.rdata
->u
.srv
.priority
= 0;
6981 rr
->resrec
.rdata
->u
.srv
.weight
= 0;
6982 rr
->resrec
.rdata
->u
.srv
.port
= zeroIPPort
;
6983 if (host
&& host
->c
[0]) AssignDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, host
);
6984 else rr
->HostTarget
= mDNStrue
;
6985 return(mDNS_Register(m
, rr
));
6988 mDNSexport mStatus
mDNS_AdvertiseDomains(mDNS
*const m
, AuthRecord
*rr
,
6989 mDNS_DomainType DomainType
, const mDNSInterfaceID InterfaceID
, char *domname
)
6991 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, mDNSNULL
, mDNSNULL
);
6992 if (!MakeDomainNameFromDNSNameString(rr
->resrec
.name
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
6993 if (!MakeDomainNameFromDNSNameString(&rr
->resrec
.rdata
->u
.name
, domname
)) return(mStatus_BadParamErr
);
6994 return(mDNS_Register(m
, rr
));
6997 // ***************************************************************************
6998 #if COMPILER_LIKES_PRAGMA_MARK
7001 #pragma mark - Startup and Shutdown
7004 mDNSlocal
void mDNS_GrowCache_internal(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
7006 if (storage
&& numrecords
)
7009 debugf("Adding cache storage for %d more records (%d bytes)", numrecords
, numrecords
*sizeof(CacheEntity
));
7010 for (i
=0; i
<numrecords
; i
++) storage
[i
].next
= &storage
[i
+1];
7011 storage
[numrecords
-1].next
= m
->rrcache_free
;
7012 m
->rrcache_free
= storage
;
7013 m
->rrcache_size
+= numrecords
;
7017 mDNSexport
void mDNS_GrowCache(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
7020 mDNS_GrowCache_internal(m
, storage
, numrecords
);
7024 mDNSexport mStatus
mDNS_Init(mDNS
*const m
, mDNS_PlatformSupport
*const p
,
7025 CacheEntity
*rrcachestorage
, mDNSu32 rrcachesize
,
7026 mDNSBool AdvertiseLocalAddresses
, mDNSCallback
*Callback
, void *Context
)
7032 if (!rrcachestorage
) rrcachesize
= 0;
7036 m
->CanReceiveUnicastOn5353
= mDNSfalse
; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
7037 m
->AdvertiseLocalAddresses
= AdvertiseLocalAddresses
;
7038 m
->mDNSPlatformStatus
= mStatus_Waiting
;
7039 m
->UnicastPort4
= zeroIPPort
;
7040 m
->UnicastPort6
= zeroIPPort
;
7041 m
->MainCallback
= Callback
;
7042 m
->MainContext
= Context
;
7043 m
->rec
.r
.resrec
.RecordType
= 0;
7045 // For debugging: To catch and report locking failures
7047 m
->mDNS_reentrancy
= 0;
7048 m
->mDNS_shutdown
= mDNSfalse
;
7049 m
->lock_rrcache
= 0;
7050 m
->lock_Questions
= 0;
7051 m
->lock_Records
= 0;
7053 // Task Scheduling variables
7054 result
= mDNSPlatformTimeInit();
7055 if (result
!= mStatus_NoError
) return(result
);
7056 m
->timenow_adjust
= (mDNSs32
)mDNSRandom(0xFFFFFFFF);
7057 timenow
= mDNS_TimeNow_NoLock(m
);
7059 m
->timenow
= 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
7060 m
->timenow_last
= timenow
;
7061 m
->NextScheduledEvent
= timenow
;
7062 m
->SuppressSending
= timenow
;
7063 m
->NextCacheCheck
= timenow
+ 0x78000000;
7064 m
->NextScheduledQuery
= timenow
+ 0x78000000;
7065 m
->NextScheduledProbe
= timenow
+ 0x78000000;
7066 m
->NextScheduledResponse
= timenow
+ 0x78000000;
7067 m
->ExpectUnicastResponse
= timenow
+ 0x78000000;
7068 m
->RandomQueryDelay
= 0;
7070 m
->SendDeregistrations
= mDNSfalse
;
7071 m
->SendImmediateAnswers
= mDNSfalse
;
7072 m
->SleepState
= mDNSfalse
;
7074 // These fields only required for mDNS Searcher...
7075 m
->Questions
= mDNSNULL
;
7076 m
->NewQuestions
= mDNSNULL
;
7077 m
->CurrentQuestion
= mDNSNULL
;
7078 m
->LocalOnlyQuestions
= mDNSNULL
;
7079 m
->NewLocalOnlyQuestions
= mDNSNULL
;
7080 m
->rrcache_size
= 0;
7081 m
->rrcache_totalused
= 0;
7082 m
->rrcache_active
= 0;
7083 m
->rrcache_report
= 10;
7084 m
->rrcache_free
= mDNSNULL
;
7086 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++) m
->rrcache_hash
[slot
] = mDNSNULL
;
7088 mDNS_GrowCache_internal(m
, rrcachestorage
, rrcachesize
);
7090 // Fields below only required for mDNS Responder...
7091 m
->hostlabel
.c
[0] = 0;
7092 m
->nicelabel
.c
[0] = 0;
7093 m
->MulticastHostname
.c
[0] = 0;
7094 m
->HIHardware
.c
[0] = 0;
7095 m
->HISoftware
.c
[0] = 0;
7096 m
->ResourceRecords
= mDNSNULL
;
7097 m
->DuplicateRecords
= mDNSNULL
;
7098 m
->NewLocalRecords
= mDNSNULL
;
7099 m
->CurrentRecord
= mDNSNULL
;
7100 m
->HostInterfaces
= mDNSNULL
;
7101 m
->ProbeFailTime
= 0;
7102 m
->NumFailedProbes
= 0;
7103 m
->SuppressProbes
= 0;
7105 #ifndef UNICAST_DISABLED
7107 m
->SuppressStdPort53Queries
= 0;
7109 result
= mDNSPlatformInit(m
);
7114 mDNSexport
void mDNSCoreInitComplete(mDNS
*const m
, mStatus result
)
7116 m
->mDNSPlatformStatus
= result
;
7117 if (m
->MainCallback
)
7120 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
7121 m
->MainCallback(m
, mStatus_NoError
);
7122 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
7127 mDNSexport
void mDNS_Close(mDNS
*const m
)
7129 mDNSu32 rrcache_active
= 0;
7130 mDNSu32 rrcache_totalused
= 0;
7132 NetworkInterfaceInfo
*intf
;
7135 m
->mDNS_shutdown
= mDNStrue
;
7137 #ifndef UNICAST_DISABLED
7140 rrcache_totalused
= m
->rrcache_totalused
;
7141 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
7143 while(m
->rrcache_hash
[slot
])
7145 CacheGroup
*cg
= m
->rrcache_hash
[slot
];
7148 CacheRecord
*rr
= cg
->members
;
7149 cg
->members
= cg
->members
->next
;
7150 if (rr
->CRActiveQuestion
) rrcache_active
++;
7151 ReleaseCacheRecord(m
, rr
);
7153 cg
->rrcache_tail
= &cg
->members
;
7154 ReleaseCacheGroup(m
, &m
->rrcache_hash
[slot
]);
7157 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused
, rrcache_active
);
7158 if (rrcache_active
!= m
->rrcache_active
)
7159 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active
, m
->rrcache_active
);
7161 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
7162 if (intf
->Advertise
)
7163 DeadvertiseInterface(m
, intf
);
7165 // Make sure there are nothing but deregistering records remaining in the list
7166 if (m
->CurrentRecord
) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
7167 m
->CurrentRecord
= m
->ResourceRecords
;
7168 while (m
->CurrentRecord
)
7170 AuthRecord
*rr
= m
->CurrentRecord
;
7171 if (rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
7173 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr
->resrec
.RecordType
, rr
->resrec
.name
->c
);
7174 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
7177 m
->CurrentRecord
= rr
->next
;
7180 if (m
->ResourceRecords
) debugf("mDNS_Close: Sending final packets for deregistering records");
7181 else debugf("mDNS_Close: No deregistering records remain");
7183 // If any deregistering records remain, send their deregistration announcements before we exit
7184 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) DiscardDeregistrations(m
);
7185 else if (m
->ResourceRecords
) SendResponses(m
);
7186 if (m
->ResourceRecords
) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m
, m
->ResourceRecords
));
7189 debugf("mDNS_Close: mDNSPlatformClose");
7190 mDNSPlatformClose(m
);
7191 debugf("mDNS_Close: done");