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.522 2005/03/04 21:48:12 cheshire
49 <rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity
51 Revision 1.521 2005/02/25 04:21:00 cheshire
52 <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
54 Revision 1.520 2005/02/16 01:14:11 cheshire
55 Convert RR Cache LogOperation() calls to debugf()
57 Revision 1.519 2005/02/15 01:57:20 cheshire
58 When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero
60 Revision 1.518 2005/02/10 22:35:17 cheshire
61 <rdar://problem/3727944> Update name
63 Revision 1.517 2005/02/03 00:21:21 cheshire
64 Update comments about BIND named and zero-length TXT records
66 Revision 1.516 2005/01/28 06:06:32 cheshire
69 Revision 1.515 2005/01/27 00:21:49 cheshire
70 <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
72 Revision 1.514 2005/01/21 01:33:45 cheshire
73 <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
75 Revision 1.513 2005/01/21 00:07:54 cheshire
76 <rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict
78 Revision 1.512 2005/01/20 00:37:45 cheshire
79 <rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse
80 Take care not to recycle records while they are on the CacheFlushRecords list
82 Revision 1.511 2005/01/19 22:48:53 cheshire
83 <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
85 Revision 1.510 2005/01/19 03:12:45 cheshire
86 Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
88 Revision 1.509 2005/01/19 03:08:49 cheshire
89 <rdar://problem/3961051> CPU Spin in mDNSResponder
90 Log messages to help catch and report CPU spins
92 Revision 1.508 2005/01/18 18:56:32 cheshire
93 <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
95 Revision 1.507 2005/01/18 01:12:07 cheshire
96 <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
98 Revision 1.506 2005/01/17 23:28:53 cheshire
101 Revision 1.505 2005/01/11 02:02:56 shersche
102 Move variable declaration to the beginning of statement block
104 Revision 1.504 2004/12/20 20:24:35 cheshire
105 <rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer
107 Revision 1.503 2004/12/20 18:41:47 cheshire
108 <rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space
110 Revision 1.502 2004/12/20 18:04:08 cheshire
111 <rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache
113 Revision 1.501 2004/12/19 23:50:18 cheshire
114 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
115 Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services
117 Revision 1.500 2004/12/18 03:13:46 cheshire
118 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
120 Revision 1.499 2004/12/17 23:37:45 cheshire
121 <rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
122 (and other repetitive configuration changes)
124 Revision 1.498 2004/12/17 05:25:46 cheshire
125 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
127 Revision 1.497 2004/12/17 03:20:58 cheshire
128 <rdar://problem/3925168> Don't send unicast replies we know will be ignored
130 Revision 1.496 2004/12/16 22:18:26 cheshire
131 Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
133 Revision 1.495 2004/12/16 21:27:37 ksekar
134 Fixed build failures when compiled with verbose debugging messages
136 Revision 1.494 2004/12/16 20:46:56 cheshire
137 Fix compiler warnings
139 Revision 1.493 2004/12/16 20:13:00 cheshire
140 <rdar://problem/3324626> Cache memory management improvements
142 Revision 1.492 2004/12/16 08:03:24 shersche
143 Fix compilation error when UNICAST_DISABLED is set
145 Revision 1.491 2004/12/11 01:52:11 cheshire
146 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
148 Revision 1.490 2004/12/10 20:06:25 cheshire
149 <rdar://problem/3915074> Reduce egregious stack space usage
150 Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes
152 Revision 1.489 2004/12/10 20:03:43 cheshire
153 <rdar://problem/3915074> Reduce egregious stack space usage
154 Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes
156 Revision 1.488 2004/12/10 19:50:41 cheshire
157 <rdar://problem/3915074> Reduce egregious stack space usage
158 Reduced SendResponses() stack frame from 9K to 176 bytes
160 Revision 1.487 2004/12/10 19:39:13 cheshire
161 <rdar://problem/3915074> Reduce egregious stack space usage
162 Reduced SendQueries() stack frame from 18K to 112 bytes
164 Revision 1.486 2004/12/10 14:16:17 cheshire
165 <rdar://problem/3889788> Relax update rate limiting
166 We now allow an average rate of ten updates per minute.
167 Updates in excess of that are rate limited, but more gently than before.
169 Revision 1.485 2004/12/10 02:09:24 cheshire
170 <rdar://problem/3898376> Modify default TTLs
172 Revision 1.484 2004/12/09 03:15:40 ksekar
173 <rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains
175 Revision 1.483 2004/12/07 23:00:14 ksekar
176 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration:
177 Call RecordProbeFailure even if there is no record callback
179 Revision 1.482 2004/12/07 22:49:06 cheshire
180 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
182 Revision 1.481 2004/12/07 21:26:04 ksekar
183 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
185 Revision 1.480 2004/12/07 20:42:33 cheshire
186 Add explicit context parameter to mDNS_RemoveRecordFromService()
188 Revision 1.479 2004/12/07 17:50:49 ksekar
189 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
191 Revision 1.478 2004/12/06 21:15:22 ksekar
192 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
194 Revision 1.477 2004/12/04 02:12:45 cheshire
195 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
197 Revision 1.476 2004/11/29 23:34:31 cheshire
198 On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
199 is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
200 only nudges the time value to 1 if the interval calculation happens to result in the value zero.
202 Revision 1.475 2004/11/29 23:13:31 cheshire
203 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
204 Additional check: Make sure we don't unnecessarily send packets containing only additionals.
205 (This could occur with multi-packet KA lists, if the answer and additionals were marked
206 by the query packet, and then the answer were later suppressed in a subsequent KA packet.)
208 Revision 1.474 2004/11/29 17:18:12 cheshire
209 Remove "Unknown DNS packet type" message for update responses
211 Revision 1.473 2004/11/25 01:57:52 cheshire
212 <rdar://problem/3484552> All unique records in a set should have the cache flush bit set
214 Revision 1.472 2004/11/25 01:28:09 cheshire
215 <rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too)
217 Revision 1.471 2004/11/25 01:10:13 cheshire
218 Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
220 Revision 1.470 2004/11/24 21:54:44 cheshire
221 <rdar://problem/3894475> mDNSCore not receiving unicast responses properly
223 Revision 1.469 2004/11/24 04:50:39 cheshire
226 Revision 1.468 2004/11/24 01:47:07 cheshire
227 <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
229 Revision 1.467 2004/11/24 01:41:28 cheshire
230 Rename CompleteProbing() to AcknowledgeRecord()
232 Revision 1.466 2004/11/23 21:08:07 ksekar
233 Don't use ID to demux multicast/unicast now that unicast uses random IDs
235 Revision 1.465 2004/11/15 20:09:21 ksekar
236 <rdar://problem/3719050> Wide Area support for Add/Remove record
238 Revision 1.464 2004/11/03 01:44:36 cheshire
239 Update debugging messages
241 Revision 1.463 2004/10/29 02:38:48 cheshire
242 Fix Windows compile errors
244 Revision 1.462 2004/10/28 19:21:07 cheshire
245 Guard against registering interface with zero InterfaceID
247 Revision 1.461 2004/10/28 19:02:16 cheshire
248 Remove \n from LogMsg() call
250 Revision 1.460 2004/10/28 03:24:40 cheshire
251 Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
253 Revision 1.459 2004/10/26 22:34:37 cheshire
254 <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
256 Revision 1.458 2004/10/26 20:45:28 cheshire
257 Show mask in "invalid mask" message
259 Revision 1.457 2004/10/26 06:28:36 cheshire
260 Now that we don't check IP TTL any more, remove associated log message
262 Revision 1.456 2004/10/26 06:21:42 cheshire
263 Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address)
265 Revision 1.455 2004/10/26 06:11:40 cheshire
266 Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
268 Revision 1.454 2004/10/23 01:16:00 cheshire
269 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
271 Revision 1.453 2004/10/22 20:52:06 ksekar
272 <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
274 Revision 1.452 2004/10/20 01:50:40 cheshire
275 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
276 Implemented ForceMCast mode for AuthRecords as well as for Questions
278 Revision 1.451 2004/10/19 21:33:15 cheshire
279 <rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
280 Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
281 doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
283 Revision 1.450 2004/10/19 17:42:59 ksekar
284 Fixed compiler warnings for non-debug builds.
286 Revision 1.449 2004/10/18 22:57:07 cheshire
287 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
289 Revision 1.448 2004/10/16 00:16:59 cheshire
290 <rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
292 Revision 1.447 2004/10/15 00:51:21 cheshire
293 <rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
295 Revision 1.446 2004/10/14 00:43:34 cheshire
296 <rdar://problem/3815984> Services continue to announce SRV and HINFO
298 Revision 1.445 2004/10/12 21:07:09 cheshire
299 Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
301 Revision 1.444 2004/10/11 17:54:16 ksekar
302 Changed hashtable pointer output from debugf to verbosedebugf.
304 Revision 1.443 2004/10/10 07:05:45 cheshire
305 For consistency, use symbol "localdomain" instead of literal string
307 Revision 1.442 2004/10/08 20:25:10 cheshire
308 Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time
310 Revision 1.441 2004/10/08 03:25:01 ksekar
311 <rdar://problem/3831716> domain enumeration should use LLQs
313 Revision 1.440 2004/10/06 01:44:19 cheshire
314 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
316 Revision 1.439 2004/10/03 23:14:11 cheshire
317 Add "mDNSEthAddr" type and "zeroEthAddr" constant
319 Revision 1.438 2004/09/29 23:07:04 cheshire
320 Patch from Pavel Repin to fix compile error on Windows
322 Revision 1.437 2004/09/28 02:23:50 cheshire
323 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
324 Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot
325 For records with the cache flush bit set, defer the decision until the end of the packet
327 Revision 1.436 2004/09/28 01:27:04 cheshire
328 Update incorrect log message
330 Revision 1.435 2004/09/25 02:41:39 cheshire
331 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
333 Revision 1.434 2004/09/25 02:32:06 cheshire
336 Revision 1.433 2004/09/25 02:24:27 cheshire
337 Removed unused rr->UseCount
339 Revision 1.432 2004/09/24 21:35:17 cheshire
340 <rdar://problem/3561220> Browses are no longer piggybacking on other browses
341 TargetPort and TargetQID are allowed to be undefined if no question->Target is set
343 Revision 1.431 2004/09/24 21:33:12 cheshire
346 Revision 1.430 2004/09/24 02:15:49 cheshire
347 <rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces
349 Revision 1.429 2004/09/24 00:20:21 cheshire
350 <rdar://problem/3483349> Any rrtype is a conflict for unique records
352 Revision 1.428 2004/09/24 00:12:25 cheshire
353 Get rid of unused RRUniqueOrKnownUnique(RR)
355 Revision 1.427 2004/09/23 20:44:11 cheshire
356 <rdar://problem/3813148> Reduce timeout before expiring records on failure
358 Revision 1.426 2004/09/23 20:21:07 cheshire
359 <rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
360 Associate a unique sequence number with each received packet, and only increment the count of recent answer
361 packets if the packet sequence number for this answer record is not one we've already seen and counted.
363 Revision 1.425 2004/09/23 20:14:38 cheshire
364 Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
366 Revision 1.424 2004/09/23 00:58:36 cheshire
367 <rdar://problem/3781269> Rate limiting interferes with updating TXT records
369 Revision 1.423 2004/09/23 00:50:53 cheshire
370 <rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep
372 Revision 1.422 2004/09/22 02:34:46 cheshire
373 Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
375 Revision 1.421 2004/09/21 23:29:49 cheshire
376 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
378 Revision 1.420 2004/09/21 23:01:42 cheshire
379 Update debugf messages
381 Revision 1.419 2004/09/21 19:51:14 cheshire
382 Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
384 Revision 1.418 2004/09/21 18:40:17 cheshire
385 <rdar://problem/3376752> Adjust default record TTLs
387 Revision 1.417 2004/09/21 17:32:16 cheshire
388 <rdar://problem/3809484> Rate limiting imposed too soon
390 Revision 1.416 2004/09/20 23:52:01 cheshire
391 CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
393 Revision 1.415 2004/09/18 01:14:09 cheshire
394 <rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces
396 Revision 1.414 2004/09/18 01:06:48 cheshire
399 Revision 1.413 2004/09/17 01:08:48 cheshire
400 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
401 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
402 declared in that file are ONLY appropriate to single-address-space embedded applications.
403 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
405 Revision 1.412 2004/09/17 00:46:33 cheshire
406 mDNS_TimeNow should take const mDNS parameter
408 Revision 1.411 2004/09/17 00:31:51 cheshire
409 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
411 Revision 1.410 2004/09/17 00:19:10 cheshire
412 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
414 Revision 1.409 2004/09/16 21:59:15 cheshire
415 For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
417 Revision 1.408 2004/09/16 21:36:36 cheshire
418 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
419 Changes to add necessary locking calls around unicast DNS operations
421 Revision 1.407 2004/09/16 02:29:39 cheshire
422 Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
423 uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
425 Revision 1.406 2004/09/16 01:58:14 cheshire
426 Fix compiler warnings
428 Revision 1.405 2004/09/16 00:24:48 cheshire
429 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
431 Revision 1.404 2004/09/15 21:44:11 cheshire
432 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
433 Show time value in log to help diagnose errors
435 Revision 1.403 2004/09/15 00:46:32 ksekar
436 Changed debugf to verbosedebugf in CheckCacheExpiration
438 Revision 1.402 2004/09/14 23:59:55 cheshire
439 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
441 Revision 1.401 2004/09/14 23:27:46 cheshire
444 Revision 1.400 2004/09/02 03:48:47 cheshire
445 <rdar://problem/3709039> Disable targeted unicast query support by default
446 1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
447 2. New field AllowRemoteQuery in AuthRecord structure
448 3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
449 4. mDNS.c only answers remote queries if AllowRemoteQuery is set
451 Revision 1.399 2004/09/02 01:39:40 cheshire
452 For better readability, follow consistent convention that QR bit comes first, followed by OP bits
454 Revision 1.398 2004/09/01 03:59:29 ksekar
455 <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
457 Revision 1.397 2004/08/25 22:04:25 rpantos
458 Fix the standard Windows compile error.
460 Revision 1.396 2004/08/25 00:37:27 ksekar
461 <rdar://problem/3774635>: Cleanup DynDNS hostname registration code
463 Revision 1.395 2004/08/18 17:21:18 ksekar
464 Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
466 Revision 1.394 2004/08/14 03:22:41 cheshire
467 <rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
468 Add GetUserSpecifiedDDNSName() routine
469 Convert ServiceRegDomain to domainname instead of C string
470 Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
472 Revision 1.393 2004/08/13 23:42:52 cheshire
473 Removed unused "zeroDomainNamePtr"
475 Revision 1.392 2004/08/13 23:37:02 cheshire
476 Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
477 "uDNS_info.UnicastHostname" for clarity
479 Revision 1.391 2004/08/13 23:25:00 cheshire
480 Now that we do both uDNS and mDNS, global replace "m->hostname" with
481 "m->MulticastHostname" for clarity
483 Revision 1.390 2004/08/11 02:17:01 cheshire
484 <rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record
486 Revision 1.389 2004/08/10 23:19:14 ksekar
487 <rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
488 Moved routines/constants to allow extern access for garbage collection daemon
490 Revision 1.388 2004/07/30 17:40:06 ksekar
491 <rdar://problem/3739115>: TXT Record updates not available for wide-area services
493 Revision 1.387 2004/07/26 22:49:30 ksekar
494 <rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
496 Revision 1.386 2004/07/13 21:24:24 rpantos
497 Fix for <rdar://problem/3701120>.
499 Revision 1.385 2004/06/18 19:09:59 cheshire
500 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
502 Revision 1.384 2004/06/15 04:31:23 cheshire
503 Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
505 Revision 1.383 2004/06/11 00:04:59 cheshire
506 <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
508 Revision 1.382 2004/06/08 04:59:40 cheshire
509 Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
511 Revision 1.381 2004/06/05 00:57:30 cheshire
512 Remove incorrect LogMsg()
514 Revision 1.380 2004/06/05 00:04:26 cheshire
515 <rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
517 Revision 1.379 2004/05/28 23:42:36 ksekar
518 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
520 Revision 1.378 2004/05/25 17:25:25 cheshire
521 Remove extraneous blank lines and white space
523 Revision 1.377 2004/05/18 23:51:25 cheshire
524 Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
526 Revision 1.376 2004/05/05 18:30:44 ksekar
527 Restored surpressed Cache Tail debug messages.
529 Revision 1.375 2004/04/26 21:36:25 cheshire
530 Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive
531 is indicated as being available on that interface
533 Revision 1.374 2004/04/21 02:53:26 cheshire
534 Typo in debugf statement
536 Revision 1.373 2004/04/21 02:49:11 cheshire
537 To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
539 Revision 1.372 2004/04/21 02:38:51 cheshire
542 Revision 1.371 2004/04/14 23:09:28 ksekar
543 Support for TSIG signed dynamic updates.
545 Revision 1.370 2004/04/09 17:40:26 cheshire
546 Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
548 Revision 1.369 2004/04/09 16:34:00 cheshire
549 Debugging code for later; currently unused
551 Revision 1.368 2004/04/02 19:19:48 cheshire
552 Add code to do optional logging of multi-packet KA list time intervals
554 Revision 1.367 2004/03/20 03:16:10 cheshire
555 Minor refinement to "Excessive update rate" message
557 Revision 1.366 2004/03/20 03:12:57 cheshire
558 <rdar://problem/3587619>: UpdateCredits not granted promptly enough
560 Revision 1.365 2004/03/19 23:51:22 cheshire
561 Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
563 Revision 1.364 2004/03/13 01:57:33 ksekar
564 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
566 Revision 1.363 2004/03/12 21:00:51 cheshire
567 Also show port numbers when logging "apparent spoof mDNS Response" messages
569 Revision 1.362 2004/03/12 08:58:18 cheshire
570 Guard against empty TXT records
572 Revision 1.361 2004/03/09 03:00:46 cheshire
573 <rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good.
575 Revision 1.360 2004/03/08 02:52:41 cheshire
576 Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages
578 Revision 1.359 2004/03/02 03:21:56 cheshire
579 <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
581 Revision 1.358 2004/02/20 08:18:34 cheshire
582 <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
584 Revision 1.357 2004/02/18 01:47:41 cheshire
585 <rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms
587 Revision 1.356 2004/02/06 23:04:19 ksekar
588 Basic Dynamic Update support via mDNS_Register (dissabled via
589 UNICAST_REGISTRATION #define)
591 Revision 1.355 2004/02/05 09:32:33 cheshire
592 Fix from Bob Bradley: When using the "%.*s" string form,
593 guard against truncating in the middle of a multi-byte UTF-8 character.
595 Revision 1.354 2004/02/05 09:30:22 cheshire
598 Revision 1.353 2004/01/28 03:41:00 cheshire
599 <rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
601 Revision 1.352 2004/01/28 02:30:07 ksekar
602 Added default Search Domains to unicast browsing, controlled via
603 Networking sharing prefs pane. Stopped sending unicast messages on
604 every interface. Fixed unicast resolving via mach-port API.
606 Revision 1.351 2004/01/27 20:15:22 cheshire
607 <rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
609 Revision 1.350 2004/01/24 23:38:16 cheshire
610 Use mDNSVal16() instead of shifting and ORing operations
612 Revision 1.349 2004/01/23 23:23:14 ksekar
613 Added TCP support for truncated unicast messages.
615 Revision 1.348 2004/01/22 03:54:11 cheshire
616 Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
617 which means "do this query via multicast, even if it's apparently a unicast domain"
619 Revision 1.347 2004/01/22 03:50:49 cheshire
620 If the client has specified an explicit InterfaceID, then do query by multicast, not unicast
622 Revision 1.346 2004/01/22 03:48:41 cheshire
623 Make sure uDNS client doesn't accidentally use query ID zero
625 Revision 1.345 2004/01/22 03:43:08 cheshire
626 Export constants like mDNSInterface_LocalOnly so that the client layers can use them
628 Revision 1.344 2004/01/21 21:53:18 cheshire
629 <rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
631 Revision 1.343 2003/12/23 00:07:47 cheshire
632 Make port number in debug message be five-character field, left justified
634 Revision 1.342 2003/12/20 01:34:28 cheshire
635 <rdar://problem/3515876>: Error putting additional records into packets
636 Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
637 after every record, not once at the end.
639 Revision 1.341 2003/12/18 22:56:12 cheshire
640 <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
642 Revision 1.340 2003/12/16 02:31:37 cheshire
643 Minor update to comments
645 Revision 1.339 2003/12/13 05:50:33 bradley
646 Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform
647 layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to
648 fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf.
650 Revision 1.338 2003/12/13 03:05:27 ksekar
651 <rdar://problem/3192548>: DynDNS: Unicast query of service records
653 Revision 1.337 2003/12/01 21:46:05 cheshire
654 mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
656 Revision 1.336 2003/12/01 21:26:19 cheshire
657 Guard against zero-length sbuffer in mDNS_vsnprintf()
659 Revision 1.335 2003/12/01 20:27:48 cheshire
660 Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors
662 Revision 1.334 2003/11/20 22:59:53 cheshire
663 Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
664 Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
666 Revision 1.333 2003/11/20 20:49:53 cheshire
667 Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
669 Revision 1.332 2003/11/20 05:47:37 cheshire
670 <rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query
671 Now that we only include answers in the known answer list if they are less than
672 halfway to expiry, the check to also see if we have another query scheduled
673 before the record expires is no longer necessary (and in fact, not correct).
675 Revision 1.331 2003/11/19 22:31:48 cheshire
676 When automatically adding A records to SRVs, add them as additionals, not answers
678 Revision 1.330 2003/11/19 22:28:50 cheshire
679 Increment/Decrement mDNS_reentrancy around calls to m->MainCallback()
680 to allow client to make mDNS calls (specifically the call to mDNS_GrowCache())
682 Revision 1.329 2003/11/19 22:19:24 cheshire
683 Show log message when ignoring packets with bad TTL.
684 This is to help diagnose problems on Linux versions that may not report the TTL reliably.
686 Revision 1.328 2003/11/19 22:06:38 cheshire
687 Show log messages when a service or hostname is renamed
689 Revision 1.327 2003/11/19 22:03:44 cheshire
690 Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
692 Revision 1.326 2003/11/17 22:27:02 cheshire
693 Another fix from ramaprasad.kr@hp.com: Improve reply delay computation
694 on platforms that have native clock rates below fifty ticks per second.
696 Revision 1.325 2003/11/17 20:41:44 cheshire
697 Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
699 Revision 1.324 2003/11/17 20:36:32 cheshire
700 Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and
701 DeadvertiseInterface() -- they're internal private routines, not API routines.
703 Revision 1.323 2003/11/14 20:59:08 cheshire
704 Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
705 Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
707 Revision 1.322 2003/11/14 19:47:52 cheshire
708 Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
710 Revision 1.321 2003/11/14 19:18:34 cheshire
711 Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
713 Revision 1.320 2003/11/13 06:45:04 cheshire
714 Fix compiler warning on certain compilers
716 Revision 1.319 2003/11/13 00:47:40 cheshire
717 <rdar://problem/3437556> We should delay AAAA record query if A record already in cache.
719 Revision 1.318 2003/11/13 00:33:26 cheshire
720 Change macro "RRIsAddressType" to "RRTypeIsAddressType"
722 Revision 1.317 2003/11/13 00:10:49 cheshire
723 <rdar://problem/3436412>: Verify that rr data is different before updating.
725 Revision 1.316 2003/11/08 23:37:54 cheshire
726 Give explicit zero initializers to blank static structure, required by certain compilers.
727 (Thanks to ramaprasad.kr@hp.com for reporting this.)
729 Revision 1.315 2003/11/07 03:32:56 cheshire
730 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
731 This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
732 purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
733 to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
734 can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
736 Revision 1.314 2003/11/07 03:19:49 cheshire
737 Minor variable renaming for clarity
739 Revision 1.313 2003/11/07 03:14:49 cheshire
740 Previous checkin proved to be overly simplistic; reversing
742 Revision 1.312 2003/11/03 23:45:15 cheshire
743 <rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
744 Build cache lists in FIFO order, not customary C LIFO order
745 (Append new elements to tail of cache list, instead of prepending at the head.)
747 Revision 1.311 2003/10/09 18:00:11 cheshire
748 Another compiler warning fix.
750 Revision 1.310 2003/10/07 20:27:05 cheshire
751 Patch from Bob Bradley, to fix warning and compile error on Windows
753 Revision 1.309 2003/09/26 01:06:36 cheshire
754 <rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too
755 Made new routine HaveSentEntireRRSet() to check if flag should be set
757 Revision 1.308 2003/09/23 01:05:01 cheshire
758 Minor changes to comments and debugf() message
760 Revision 1.307 2003/09/09 20:13:30 cheshire
761 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
762 Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
763 rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
765 Revision 1.306 2003/09/09 03:00:03 cheshire
766 <rdar://problem/3413099> Services take a long time to disappear when switching networks.
767 Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
769 Revision 1.305 2003/09/09 02:49:31 cheshire
770 <rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
772 Revision 1.304 2003/09/09 02:41:19 cheshire
773 <rdar://problem/3411105> Don't send a Goodbye record if we never announced it
775 Revision 1.303 2003/09/05 19:55:02 cheshire
776 <rdar://problem/3409533> Include address records when announcing SRV records
778 Revision 1.302 2003/09/05 00:01:36 cheshire
779 <rdar://problem/3407549> Don't accelerate queries that have large KA lists
781 Revision 1.301 2003/09/04 22:51:13 cheshire
782 <rdar://problem/3398213> Group probes and goodbyes better
784 Revision 1.300 2003/09/03 02:40:37 cheshire
785 <rdar://problem/3404842> mDNSResponder complains about '_'s
786 Underscores are not supposed to be legal in standard DNS names, but IANA appears
787 to have allowed them in previous service name registrations, so we should too.
789 Revision 1.299 2003/09/03 02:33:09 cheshire
790 <rdar://problem/3404795> CacheRecordRmv ERROR
791 Don't update m->NewQuestions until *after* CheckCacheExpiration();
793 Revision 1.298 2003/09/03 01:47:01 cheshire
794 <rdar://problem/3319418> Services always in a state of flux
795 Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds
797 Revision 1.297 2003/08/29 19:44:15 cheshire
798 <rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
799 1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries
800 that already have at least one unique answer in the cache
801 2. For these queries, go straight to QM, skipping QU
803 Revision 1.296 2003/08/29 19:08:21 cheshire
804 <rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
805 Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
807 Revision 1.295 2003/08/28 01:10:59 cheshire
808 <rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
810 Revision 1.294 2003/08/27 02:30:22 cheshire
811 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
812 One more change: "query->GotTXT" is now a straightforward bi-state boolean again
814 Revision 1.293 2003/08/27 02:25:31 cheshire
815 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
817 Revision 1.292 2003/08/21 19:27:36 cheshire
818 <rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
820 Revision 1.291 2003/08/21 18:57:44 cheshire
821 <rdar://problem/3387140> Synchronized queries on the network
823 Revision 1.290 2003/08/21 02:25:23 cheshire
824 Minor changes to comments and debugf() messages
826 Revision 1.289 2003/08/21 02:21:50 cheshire
827 <rdar://problem/3386473> Efficiency: Reduce repeated queries
829 Revision 1.288 2003/08/20 23:39:30 cheshire
830 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
832 Revision 1.287 2003/08/20 20:47:18 cheshire
835 Revision 1.286 2003/08/20 02:18:51 cheshire
836 <rdar://problem/3344098> Cleanup: Review syslog messages
838 Revision 1.285 2003/08/20 01:59:06 cheshire
839 <rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
840 Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
842 Revision 1.284 2003/08/19 22:20:00 cheshire
843 <rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
844 More minor refinements
846 Revision 1.283 2003/08/19 22:16:27 cheshire
847 Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
849 Revision 1.282 2003/08/19 06:48:25 cheshire
850 <rdar://problem/3376552> Guard against excessive record updates
851 Each record starts with 10 UpdateCredits.
852 Every update consumes one UpdateCredit.
853 UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
854 As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
855 When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
857 Revision 1.281 2003/08/19 04:49:28 cheshire
858 <rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right
859 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.
860 2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
861 3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
863 Revision 1.280 2003/08/19 02:33:36 cheshire
866 Revision 1.279 2003/08/19 02:31:11 cheshire
867 <rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
868 Final expiration queries now only mark the question for sending on the particular interface
869 pertaining to the record that's expiring.
871 Revision 1.278 2003/08/18 22:53:37 cheshire
872 <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
874 Revision 1.277 2003/08/18 19:05:44 cheshire
875 <rdar://problem/3382423> UpdateRecord not working right
876 Added "newrdlength" field to hold new length of updated rdata
878 Revision 1.276 2003/08/16 03:39:00 cheshire
879 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
881 Revision 1.275 2003/08/16 02:51:27 cheshire
882 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
883 Don't try to compute namehash etc, until *after* validating the name
885 Revision 1.274 2003/08/16 01:12:40 cheshire
886 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
887 Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
888 simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
889 and in the process of copying it, the C compiler may run off the end of the rdata object into
890 unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
891 call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
893 Revision 1.273 2003/08/15 20:16:02 cheshire
894 <rdar://problem/3366590> mDNSResponder takes too much RPRVT
895 We want to avoid touching the rdata pages, so we don't page them in.
896 1. RDLength was stored with the rdata, which meant touching the page just to find the length.
897 Moved this from the RData to the ResourceRecord object.
898 2. To avoid unnecessarily touching the rdata just to compare it,
899 compute a hash of the rdata and store the hash in the ResourceRecord object.
901 Revision 1.272 2003/08/14 19:29:04 cheshire
902 <rdar://problem/3378473> Include cache records in SIGINFO output
903 Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
905 Revision 1.271 2003/08/14 02:17:05 cheshire
906 <rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
908 Revision 1.270 2003/08/13 17:07:28 ksekar
909 <rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
910 Added check to result of mDNS_Register() before linking extra record into list.
912 Revision 1.269 2003/08/12 19:56:23 cheshire
915 Revision 1.268 2003/08/12 15:01:10 cheshire
918 Revision 1.267 2003/08/12 14:59:27 cheshire
919 <rdar://problem/3374490> Rate-limiting blocks some legitimate responses
920 When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
921 whether to suppress the response, also check LastMCInterface to see if it matches.
923 Revision 1.266 2003/08/12 12:47:16 cheshire
924 In mDNSCoreMachineSleep debugf message, display value of m->timenow
926 Revision 1.265 2003/08/11 20:04:28 cheshire
927 <rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
929 Revision 1.264 2003/08/09 00:55:02 cheshire
930 <rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
931 Don't scan the whole cache after every packet.
933 Revision 1.263 2003/08/09 00:35:29 cheshire
934 Moved AnswerNewQuestion() later in the file, in preparation for next checkin
936 Revision 1.262 2003/08/08 19:50:33 cheshire
937 <rdar://problem/3370332> Remove "Cache size now xxx" messages
939 Revision 1.261 2003/08/08 19:18:45 cheshire
940 <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
942 Revision 1.260 2003/08/08 18:55:48 cheshire
943 <rdar://problem/3370365> Guard against time going backwards
945 Revision 1.259 2003/08/08 18:36:04 cheshire
946 <rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
948 Revision 1.258 2003/08/08 16:22:05 cheshire
949 <rdar://problem/3335473> Need to check validity of TXT (and other) records
950 Remove unneeded LogMsg
952 Revision 1.257 2003/08/07 01:41:08 cheshire
953 <rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
955 Revision 1.256 2003/08/06 23:25:51 cheshire
956 <rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four
958 Revision 1.255 2003/08/06 23:22:50 cheshire
959 Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
961 Revision 1.254 2003/08/06 21:33:39 cheshire
962 Fix compiler warnings on PocketPC 2003 (Windows CE)
964 Revision 1.253 2003/08/06 20:43:57 cheshire
965 <rdar://problem/3335473> Need to check validity of TXT (and other) records
966 Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
968 Revision 1.252 2003/08/06 20:35:47 cheshire
969 Enhance debugging routine GetRRDisplayString() so it can also be used to display
970 other RDataBody objects, not just the one currently attached the given ResourceRecord
972 Revision 1.251 2003/08/06 19:07:34 cheshire
973 <rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
974 Was checking LastAPTime instead of LastMCTime
976 Revision 1.250 2003/08/06 19:01:55 cheshire
979 Revision 1.249 2003/08/06 00:13:28 cheshire
980 Tidy up debugf messages
982 Revision 1.248 2003/08/05 22:20:15 cheshire
983 <rdar://problem/3330324> Need to check IP TTL on responses
985 Revision 1.247 2003/08/05 00:56:39 cheshire
986 <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
988 Revision 1.246 2003/08/04 19:20:49 cheshire
989 Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
991 Revision 1.245 2003/08/02 01:56:29 cheshire
992 For debugging: log message if we ever get more than one question in a truncated packet
994 Revision 1.244 2003/08/01 23:55:32 cheshire
995 Fix for compiler warnings on Windows, submitted by Bob Bradley
997 Revision 1.243 2003/07/25 02:26:09 cheshire
998 Typo: FIxed missing semicolon
1000 Revision 1.242 2003/07/25 01:18:41 cheshire
1001 Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
1003 Revision 1.241 2003/07/23 21:03:42 cheshire
1004 Only show "Found record..." debugf message in verbose mode
1006 Revision 1.240 2003/07/23 21:01:11 cheshire
1007 <rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
1008 After sending a packet, suppress further sending for the next 100ms.
1010 Revision 1.239 2003/07/22 01:30:05 cheshire
1011 <rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once
1013 Revision 1.238 2003/07/22 00:10:20 cheshire
1014 <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
1016 Revision 1.237 2003/07/19 03:23:13 cheshire
1017 <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
1019 Revision 1.236 2003/07/19 03:04:55 cheshire
1020 Fix warnings; some debugf message improvements
1022 Revision 1.235 2003/07/19 00:03:32 cheshire
1023 <rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received
1024 ScheduleNextTask is quite an expensive operation.
1025 We don't need to do all that work after receiving a no-op packet that didn't change our state.
1027 Revision 1.234 2003/07/18 23:52:11 cheshire
1028 To improve consistency of field naming, global search-and-replace:
1029 NextProbeTime -> NextScheduledProbe
1030 NextResponseTime -> NextScheduledResponse
1032 Revision 1.233 2003/07/18 00:29:59 cheshire
1033 <rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
1035 Revision 1.232 2003/07/18 00:11:38 cheshire
1036 Add extra case to switch statements to handle HINFO data for Get, Put and Display
1037 (In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT)
1039 Revision 1.231 2003/07/18 00:06:37 cheshire
1040 To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->"
1042 Revision 1.230 2003/07/17 18:16:54 cheshire
1043 <rdar://problem/3319418> Services always in a state of flux
1044 In preparation for working on this, made some debugf messages a little more selective
1046 Revision 1.229 2003/07/17 17:35:04 cheshire
1047 <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
1049 Revision 1.228 2003/07/16 20:50:27 cheshire
1050 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1052 Revision 1.227 2003/07/16 05:01:36 cheshire
1053 Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
1054 <rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
1056 Revision 1.226 2003/07/16 04:51:44 cheshire
1057 Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
1059 Revision 1.225 2003/07/16 04:46:41 cheshire
1060 Minor wording cleanup: The correct DNS term is "response", not "reply"
1062 Revision 1.224 2003/07/16 04:39:02 cheshire
1063 Textual cleanup (no change to functionality):
1064 Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
1066 Revision 1.223 2003/07/16 00:09:22 cheshire
1067 Textual cleanup (no change to functionality):
1068 Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
1069 replace with macro "TicksTTL(rr)"
1070 Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
1071 replaced with macro "RRExpireTime(rr)"
1073 Revision 1.222 2003/07/15 23:40:46 cheshire
1074 Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
1076 Revision 1.221 2003/07/15 22:17:56 cheshire
1077 <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
1079 Revision 1.220 2003/07/15 02:12:51 cheshire
1080 Slight tidy-up of debugf messages and comments
1082 Revision 1.219 2003/07/15 01:55:12 cheshire
1083 <rdar://problem/3315777> Need to implement service registration with subtypes
1085 Revision 1.218 2003/07/14 16:26:06 cheshire
1086 <rdar://problem/3324795> Duplicate query suppression not working right
1087 Refinement: Don't record DS information for a question in the first quarter second
1088 right after we send it -- in the case where a question happens to be accelerated by
1089 the maximum allowed amount, we don't want it to then be suppressed because the previous
1090 time *we* sent that question falls (just) within the valid duplicate suppression window.
1092 Revision 1.217 2003/07/13 04:43:53 cheshire
1093 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1094 Minor refinement: No need to make address query broader than the original SRV query that provoked it
1096 Revision 1.216 2003/07/13 03:13:17 cheshire
1097 <rdar://problem/3325169> Services on multiple interfaces not always resolving
1098 If we get an identical SRV on a second interface, convert address queries to non-specific
1100 Revision 1.215 2003/07/13 02:28:00 cheshire
1101 <rdar://problem/3325166> SendResponses didn't all its responses
1102 Delete all references to RRInterfaceActive -- it's now superfluous
1104 Revision 1.214 2003/07/13 01:47:53 cheshire
1105 Fix one error and one warning in the Windows build
1107 Revision 1.213 2003/07/12 04:25:48 cheshire
1108 Fix minor signed/unsigned warnings
1110 Revision 1.212 2003/07/12 01:59:11 cheshire
1111 Minor changes to debugf messages
1113 Revision 1.211 2003/07/12 01:47:01 cheshire
1114 <rdar://problem/3324495> After name conflict, appended number should be higher than previous number
1116 Revision 1.210 2003/07/12 01:43:28 cheshire
1117 <rdar://problem/3324795> Duplicate query suppression not working right
1118 The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
1119 The code was incorrectly using the last query time plus one-half the query interval.
1120 This was only correct in the case where query acceleration was not in effect.
1122 Revision 1.209 2003/07/12 01:27:50 cheshire
1123 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1124 Fix missing "-1" in RemoveLabelSuffix()
1126 Revision 1.208 2003/07/11 01:32:38 cheshire
1127 Syntactic cleanup (no change to funcationality): Now that we only have one host name,
1128 rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
1130 Revision 1.207 2003/07/11 01:28:00 cheshire
1131 <rdar://problem/3161289> No more local.arpa
1133 Revision 1.206 2003/07/11 00:45:02 cheshire
1134 <rdar://problem/3321909> Client should get callback confirming successful host name registration
1136 Revision 1.205 2003/07/11 00:40:18 cheshire
1137 Tidy up debug message in HostNameCallback()
1139 Revision 1.204 2003/07/11 00:20:32 cheshire
1140 <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
1142 Revision 1.203 2003/07/10 23:53:41 cheshire
1143 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1145 Revision 1.202 2003/07/04 02:23:20 cheshire
1146 <rdar://problem/3311955> Responder too aggressive at flushing stale data
1147 Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
1149 Revision 1.201 2003/07/04 01:09:41 cheshire
1150 <rdar://problem/3315775> Need to implement subtype queries
1151 Modified ConstructServiceName() to allow three-part service types
1153 Revision 1.200 2003/07/03 23:55:26 cheshire
1154 Minor change to wording of syslog warning messages
1156 Revision 1.199 2003/07/03 23:51:13 cheshire
1157 <rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings
1158 Added more detailed debugging information
1160 Revision 1.198 2003/07/03 22:19:30 cheshire
1161 <rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
1162 Make exception to allow _tivo_servemedia._tcp.
1164 Revision 1.197 2003/07/02 22:33:05 cheshire
1165 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1167 When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
1168 Allow cache to grow to 512 records before considering it a potential denial-of-service attack
1170 Revision 1.196 2003/07/02 21:19:45 cheshire
1171 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
1173 Revision 1.195 2003/07/02 19:56:58 cheshire
1174 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1175 Minor refinement: m->rrcache_active was not being decremented when
1176 an active record was deleted because its TTL expired
1178 Revision 1.194 2003/07/02 18:47:40 cheshire
1179 Minor wording change to log messages
1181 Revision 1.193 2003/07/02 02:44:13 cheshire
1182 Fix warning in non-debug build
1184 Revision 1.192 2003/07/02 02:41:23 cheshire
1185 <rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
1187 Revision 1.191 2003/07/02 02:30:51 cheshire
1188 HashSlot() returns an array index. It can't be negative; hence it should not be signed.
1190 Revision 1.190 2003/06/27 00:03:05 vlubet
1191 <rdar://problem/3304625> Merge of build failure fix for gcc 3.3
1193 Revision 1.189 2003/06/11 19:24:03 cheshire
1194 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1195 Slight refinement to previous checkin
1197 Revision 1.188 2003/06/10 20:33:28 cheshire
1198 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1200 Revision 1.187 2003/06/10 04:30:44 cheshire
1201 <rdar://problem/3286234> Need to re-probe/re-announce on configuration change
1202 Only interface-specific records were re-probing and re-announcing, not non-specific records.
1204 Revision 1.186 2003/06/10 04:24:39 cheshire
1205 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1206 Some additional refinements:
1207 Don't try to do this for unicast-response queries
1208 better tracking of Qs and KAs in multi-packet KA lists
1210 Revision 1.185 2003/06/10 03:52:49 cheshire
1211 Update comments and debug messages
1213 Revision 1.184 2003/06/10 02:26:39 cheshire
1214 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1215 Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
1217 Revision 1.183 2003/06/09 18:53:13 cheshire
1218 Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
1220 Revision 1.182 2003/06/09 18:38:42 cheshire
1221 <rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network
1222 Only issue a correction if the TTL in the proxy packet is less than half the correct value.
1224 Revision 1.181 2003/06/07 06:45:05 cheshire
1225 <rdar://problem/3283666> No need for multiple machines to all be sending the same queries
1227 Revision 1.180 2003/06/07 06:31:07 cheshire
1228 Create little four-line helper function "FindIdenticalRecordInCache()"
1230 Revision 1.179 2003/06/07 06:28:13 cheshire
1231 For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
1233 Revision 1.178 2003/06/07 06:25:12 cheshire
1234 Update some comments
1236 Revision 1.177 2003/06/07 04:50:53 cheshire
1237 <rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
1239 Revision 1.176 2003/06/07 04:33:26 cheshire
1240 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1241 Minor change: Increment/decrement logic for q->CurrentAnswers should be in
1242 CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
1244 Revision 1.175 2003/06/07 04:11:52 cheshire
1245 Minor changes to comments and debug messages
1247 Revision 1.174 2003/06/07 01:46:38 cheshire
1248 <rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
1250 Revision 1.173 2003/06/07 01:22:13 cheshire
1251 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1253 Revision 1.172 2003/06/07 00:59:42 cheshire
1254 <rdar://problem/3283454> Need some randomness to spread queries on the network
1256 Revision 1.171 2003/06/06 21:41:10 cheshire
1257 For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
1259 Revision 1.170 2003/06/06 21:38:55 cheshire
1260 Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
1261 already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
1263 Revision 1.169 2003/06/06 21:35:55 cheshire
1264 Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
1265 (the target is a domain name, but not necessarily a host name)
1267 Revision 1.168 2003/06/06 21:33:31 cheshire
1268 Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
1270 Revision 1.167 2003/06/06 21:30:42 cheshire
1271 <rdar://problem/3282962> Don't delay queries for shared record types
1273 Revision 1.166 2003/06/06 17:20:14 cheshire
1274 For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
1275 (Global search-and-replace; no functional change to code execution.)
1277 Revision 1.165 2003/06/04 02:53:21 cheshire
1278 Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
1280 Revision 1.164 2003/06/04 01:25:33 cheshire
1281 <rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
1282 Display time interval between first and subsequent queries
1284 Revision 1.163 2003/06/03 19:58:14 cheshire
1285 <rdar://problem/3277665> mDNS_DeregisterService() fixes:
1286 When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
1287 Guard against a couple of possible mDNS_DeregisterService() race conditions.
1289 Revision 1.162 2003/06/03 19:30:39 cheshire
1290 Minor addition refinements for
1291 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1293 Revision 1.161 2003/06/03 18:29:03 cheshire
1294 Minor changes to comments and debugf() messages
1296 Revision 1.160 2003/06/03 05:02:16 cheshire
1297 <rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
1299 Revision 1.159 2003/06/03 03:31:57 cheshire
1300 <rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine
1302 Revision 1.158 2003/06/02 22:57:09 cheshire
1303 Minor clarifying changes to comments and log messages;
1304 IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
1306 Revision 1.157 2003/05/31 00:09:49 cheshire
1307 <rdar://problem/3274862> Add ability to discover what services are on a network
1309 Revision 1.156 2003/05/30 23:56:49 cheshire
1310 <rdar://problem/3274847> Crash after error in mDNS_RegisterService()
1311 Need to set "sr->Extras = mDNSNULL" before returning
1313 Revision 1.155 2003/05/30 23:48:00 cheshire
1314 <rdar://problem/3274832> Announcements not properly grouped
1315 Due to inconsistent setting of rr->LastAPTime at different places in the
1316 code, announcements were not properly grouped into a single packet.
1317 Fixed by creating a single routine called InitializeLastAPTime().
1319 Revision 1.154 2003/05/30 23:38:14 cheshire
1320 <rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
1321 Wrote buffer[32] where it should have said buffer[64]
1323 Revision 1.153 2003/05/30 19:10:56 cheshire
1324 <rdar://problem/3274153> ConstructServiceName needs to be more restrictive
1326 Revision 1.152 2003/05/29 22:39:16 cheshire
1327 <rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
1329 Revision 1.151 2003/05/29 06:35:42 cheshire
1330 <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
1332 Revision 1.150 2003/05/29 06:25:45 cheshire
1333 <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
1335 Revision 1.149 2003/05/29 06:18:39 cheshire
1336 <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
1338 Revision 1.148 2003/05/29 06:11:34 cheshire
1339 <rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
1341 Revision 1.147 2003/05/29 06:01:18 cheshire
1342 Change some debugf() calls to LogMsg() calls to help with debugging
1344 Revision 1.146 2003/05/28 21:00:44 cheshire
1345 Re-enable "immediate answer burst" debugf message
1347 Revision 1.145 2003/05/28 20:57:44 cheshire
1348 <rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
1349 known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
1350 version of mDNSResponder, so for now we should suppress this warning message.
1352 Revision 1.144 2003/05/28 18:05:12 cheshire
1353 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1354 Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
1356 Revision 1.143 2003/05/28 04:31:29 cheshire
1357 <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
1359 Revision 1.142 2003/05/28 03:13:07 cheshire
1360 <rdar://problem/3009899> mDNSResponder allows invalid service registrations
1361 Require that the transport protocol be _udp or _tcp
1363 Revision 1.141 2003/05/28 02:19:12 cheshire
1364 <rdar://problem/3270634> Misleading messages generated by iChat
1365 Better fix: Only generate the log message for queries where the TC bit is set.
1367 Revision 1.140 2003/05/28 01:55:24 cheshire
1368 Minor change to log messages
1370 Revision 1.139 2003/05/28 01:52:51 cheshire
1371 <rdar://problem/3270634> Misleading messages generated by iChat
1373 Revision 1.138 2003/05/27 22:35:00 cheshire
1374 <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
1376 Revision 1.137 2003/05/27 20:04:33 cheshire
1377 <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
1379 Revision 1.136 2003/05/27 18:50:07 cheshire
1380 <rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
1382 Revision 1.135 2003/05/26 04:57:28 cheshire
1383 <rdar://problem/3268953> Delay queries when there are already answers in the cache
1385 Revision 1.134 2003/05/26 04:54:54 cheshire
1386 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1387 Accidentally deleted '%' case from the switch statement
1389 Revision 1.133 2003/05/26 03:21:27 cheshire
1390 Tidy up address structure naming:
1391 mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
1392 mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
1393 mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
1395 Revision 1.132 2003/05/26 03:01:26 cheshire
1396 <rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
1398 Revision 1.131 2003/05/26 00:42:05 cheshire
1399 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
1401 Revision 1.130 2003/05/24 16:39:48 cheshire
1402 <rdar://problem/3268631> SendResponses also needs to handle multihoming better
1404 Revision 1.129 2003/05/23 02:15:37 cheshire
1405 Fixed misleading use of the term "duplicate suppression" where it should have
1406 said "known answer suppression". (Duplicate answer suppression is something
1407 different, and duplicate question suppression is yet another thing, so the use
1408 of the completely vague term "duplicate suppression" was particularly bad.)
1410 Revision 1.128 2003/05/23 01:55:13 cheshire
1411 <rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
1413 Revision 1.127 2003/05/23 01:02:15 ksekar
1414 <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
1416 Revision 1.126 2003/05/22 02:29:22 cheshire
1417 <rdar://problem/2984918> SendQueries needs to handle multihoming better
1418 Complete rewrite of SendQueries. Works much better now :-)
1420 Revision 1.125 2003/05/22 01:50:45 cheshire
1421 Fix warnings, and improve log messages
1423 Revision 1.124 2003/05/22 01:41:50 cheshire
1424 DiscardDeregistrations doesn't need InterfaceID parameter
1426 Revision 1.123 2003/05/22 01:38:55 cheshire
1427 Change bracketing of #pragma mark
1429 Revision 1.122 2003/05/21 19:59:04 cheshire
1430 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1431 Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
1433 Revision 1.121 2003/05/21 17:54:07 ksekar
1434 <rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
1435 New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
1437 Revision 1.120 2003/05/19 22:14:14 ksekar
1438 <rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type
1440 Revision 1.119 2003/05/16 01:34:10 cheshire
1443 Revision 1.118 2003/05/14 18:48:40 cheshire
1444 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1445 More minor refinements:
1446 mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
1447 mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
1449 Revision 1.117 2003/05/14 07:08:36 cheshire
1450 <rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
1451 Previously, when there was any network configuration change, mDNSResponder
1452 would tear down the entire list of active interfaces and start again.
1453 That was very disruptive, and caused the entire cache to be flushed,
1454 and caused lots of extra network traffic. Now it only removes interfaces
1455 that have really gone, and only adds new ones that weren't there before.
1457 Revision 1.116 2003/05/14 06:51:56 cheshire
1458 <rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
1460 Revision 1.115 2003/05/14 06:44:31 cheshire
1461 Improve debugging message
1463 Revision 1.114 2003/05/07 01:47:03 cheshire
1464 <rdar://problem/3250330> Also protect against NULL domainlabels
1466 Revision 1.113 2003/05/07 00:28:18 cheshire
1467 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
1469 Revision 1.112 2003/05/06 00:00:46 cheshire
1470 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
1472 Revision 1.111 2003/05/05 23:42:08 cheshire
1473 <rdar://problem/3245631> Resolves never succeed
1474 Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
1475 instead of "rr->LastAPTime = timenow - rr->ThisAPInterval"
1477 Revision 1.110 2003/04/30 21:09:59 cheshire
1478 <rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
1480 Revision 1.109 2003/04/26 02:41:56 cheshire
1481 <rdar://problem/3241281> Change timenow from a local variable to a structure member
1483 Revision 1.108 2003/04/25 01:45:56 cheshire
1484 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
1486 Revision 1.107 2003/04/25 00:41:31 cheshire
1487 <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
1489 Revision 1.106 2003/04/22 03:14:45 cheshire
1490 <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
1492 Revision 1.105 2003/04/22 01:07:43 cheshire
1493 <rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
1494 If TTL parameter is zero, leave record TTL unchanged
1496 Revision 1.104 2003/04/21 19:15:52 cheshire
1497 Fix some compiler warnings
1499 Revision 1.103 2003/04/19 02:26:35 cheshire
1500 <rdar://problem/3233804> Incorrect goodbye packet after conflict
1502 Revision 1.102 2003/04/17 03:06:28 cheshire
1503 <rdar://problem/3231321> No need to query again when a service goes away
1504 Set UnansweredQueries to 2 when receiving a "goodbye" packet
1506 Revision 1.101 2003/04/15 20:58:31 jgraessl
1507 <rdar://problem/3229014> Added a hash to lookup records in the cache.
1509 Revision 1.100 2003/04/15 18:53:14 cheshire
1510 <rdar://problem/3229064> Bug in ScheduleNextTask
1511 mDNS.c 1.94 incorrectly combined two "if" statements into one.
1513 Revision 1.99 2003/04/15 18:09:13 jgraessl
1514 <rdar://problem/3228892>
1515 Reviewed by: Stuart Cheshire
1516 Added code to keep track of when the next cache item will expire so we can
1517 call TidyRRCache only when necessary.
1519 Revision 1.98 2003/04/03 03:43:55 cheshire
1520 <rdar://problem/3216837> Off-by-one error in probe rate limiting
1522 Revision 1.97 2003/04/02 01:48:17 cheshire
1523 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1524 Additional fix pointed out by Josh:
1525 Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
1527 Revision 1.96 2003/04/01 23:58:55 cheshire
1528 Minor comment changes
1530 Revision 1.95 2003/04/01 23:46:05 cheshire
1531 <rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
1532 mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
1533 to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
1534 window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
1535 FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
1537 Revision 1.94 2003/03/29 01:55:19 cheshire
1538 <rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
1539 Solution: Major cleanup of packet timing and conflict handling rules
1541 Revision 1.93 2003/03/28 01:54:36 cheshire
1542 Minor tidyup of IPv6 (AAAA) code
1544 Revision 1.92 2003/03/27 03:30:55 cheshire
1545 <rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
1546 Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
1548 1. Make mDNS_DeregisterInterface() safe to call from a callback
1549 2. Make HostNameCallback() use DeadvertiseInterface() instead
1550 (it never really needed to deregister the interface at all)
1552 Revision 1.91 2003/03/15 04:40:36 cheshire
1553 Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
1555 Revision 1.90 2003/03/14 20:26:37 cheshire
1556 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1558 Revision 1.89 2003/03/12 19:57:50 cheshire
1559 Fixed typo in debug message
1561 Revision 1.88 2003/03/12 00:17:44 cheshire
1562 <rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records
1564 Revision 1.87 2003/03/11 01:27:20 cheshire
1565 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1567 Revision 1.86 2003/03/06 20:44:33 cheshire
1570 Revision 1.85 2003/03/05 03:38:35 cheshire
1571 <rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
1572 Fixed by leaving client in list after conflict, until client explicitly deallocates
1574 Revision 1.84 2003/03/05 01:27:30 cheshire
1575 <rdar://problem/3185482> Different TTL for multicast versus unicast responses
1576 When building unicast responses, record TTLs are capped to 10 seconds
1578 Revision 1.83 2003/03/04 23:48:52 cheshire
1579 <rdar://problem/3188865> Double probes after wake from sleep
1580 Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
1582 Revision 1.82 2003/03/04 23:38:29 cheshire
1583 <rdar://problem/3099194> mDNSResponder needs performance improvements
1584 Only set rr->CRActiveQuestion to point to the
1585 currently active representative of a question set
1587 Revision 1.81 2003/02/21 03:35:34 cheshire
1588 <rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
1590 Revision 1.80 2003/02/21 02:47:53 cheshire
1591 <rdar://problem/3099194> mDNSResponder needs performance improvements
1592 Several places in the code were calling CacheRRActive(), which searched the entire
1593 question list every time, to see if this cache resource record answers any question.
1594 Instead, we now have a field "CRActiveQuestion" in the resource record structure
1596 Revision 1.79 2003/02/21 01:54:07 cheshire
1597 <rdar://problem/3099194> mDNSResponder needs performance improvements
1598 Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
1600 Revision 1.78 2003/02/20 06:48:32 cheshire
1601 <rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
1602 Reviewed by: Josh Graessley, Bob Bradley
1604 Revision 1.77 2003/01/31 03:35:59 cheshire
1605 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1606 When there were *two* active questions in the list, they were incorrectly
1607 finding *each other* and *both* being marked as duplicates of another question
1609 Revision 1.76 2003/01/29 02:46:37 cheshire
1611 A physical interface is identified solely by its InterfaceID (not by IP and type).
1612 On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
1613 In cases where the requested outbound protocol (v4 or v6) is not supported on
1614 that InterfaceID, the platform support layer should simply discard that packet.
1616 Revision 1.75 2003/01/29 01:47:40 cheshire
1617 Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
1619 Revision 1.74 2003/01/28 05:26:25 cheshire
1620 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1621 Add 'Active' flag for interfaces
1623 Revision 1.73 2003/01/28 03:45:12 cheshire
1624 Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
1626 Revision 1.72 2003/01/28 01:49:48 cheshire
1627 <rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
1628 FindDuplicateQuestion() was incorrectly finding the question itself in the list,
1629 and incorrectly marking it as a duplicate (of itself), so that it became inactive.
1631 Revision 1.71 2003/01/28 01:41:44 cheshire
1632 <rdar://problem/3153091> Race condition when network change causes bad stuff
1633 When an interface goes away, interface-specific questions on that interface become orphaned.
1634 Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
1635 Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
1637 Revision 1.70 2003/01/23 19:00:20 cheshire
1638 Protect against infinite loops in mDNS_Execute
1640 Revision 1.69 2003/01/21 22:56:32 jgraessl
1641 <rdar://problem/3124348> service name changes are not properly handled
1642 Submitted by: Stuart Cheshire
1643 Reviewed by: Joshua Graessley
1644 Applying changes for 3124348 to main branch. 3124348 changes went in to a
1647 Revision 1.68 2003/01/17 04:09:27 cheshire
1648 <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
1650 Revision 1.67 2003/01/17 03:56:45 cheshire
1651 Default 24-hour TTL is far too long. Changing to two hours.
1653 Revision 1.66 2003/01/13 23:49:41 jgraessl
1654 Merged changes for the following fixes in to top of tree:
1655 <rdar://problem/3086540> computer name changes not handled properly
1656 <rdar://problem/3124348> service name changes are not properly handled
1657 <rdar://problem/3124352> announcements sent in pairs, failing chattiness test
1659 Revision 1.65 2002/12/23 22:13:28 jgraessl
1660 Reviewed by: Stuart Cheshire
1661 Initial IPv6 support for mDNSResponder.
1663 Revision 1.64 2002/11/26 20:49:06 cheshire
1664 <rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
1666 Revision 1.63 2002/09/21 20:44:49 zarzycki
1669 Revision 1.62 2002/09/20 03:25:37 cheshire
1670 Fix some compiler warnings
1672 Revision 1.61 2002/09/20 01:05:24 cheshire
1673 Don't kill the Extras list in mDNS_DeregisterService()
1675 Revision 1.60 2002/09/19 23:47:35 cheshire
1676 Added mDNS_RegisterNoSuchService() function for assertion of non-existence
1677 of a particular named service
1679 Revision 1.59 2002/09/19 21:25:34 cheshire
1680 mDNS_snprintf() doesn't need to be in a separate file
1682 Revision 1.58 2002/09/19 04:20:43 cheshire
1683 Remove high-ascii characters that confuse some systems
1685 Revision 1.57 2002/09/17 01:07:08 cheshire
1686 Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
1688 Revision 1.56 2002/09/16 19:44:17 cheshire
1689 Merge in license terms from Quinn's copy, in preparation for Darwin release
1692 #include "DNSCommon.h" // Defines general DNS untility routines
1693 #include "uDNS.h" // Defines entry points into unicast-specific routines
1694 // Disable certain benign warnings with Microsoft compilers
1695 #if(defined(_MSC_VER))
1696 // Disable "conditional expression is constant" warning for debug macros.
1697 // Otherwise, this generates warnings for the perfectly natural construct "while(1)"
1698 // If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
1699 #pragma warning(disable:4127)
1701 // Disable "assignment within conditional expression".
1702 // Other compilers understand the convention that if you place the assignment expression within an extra pair
1703 // of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
1704 // The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
1705 // to the compiler that the assignment is intentional, we have to just turn this warning off completely.
1706 #pragma warning(disable:4706)
1709 // ***************************************************************************
1710 #if COMPILER_LIKES_PRAGMA_MARK
1712 #pragma mark - Program Constants
1715 mDNSexport
const mDNSIPPort zeroIPPort
= { { 0 } };
1716 mDNSexport
const mDNSv4Addr zerov4Addr
= { { 0 } };
1717 mDNSexport
const mDNSv6Addr zerov6Addr
= { { 0 } };
1718 mDNSexport
const mDNSEthAddr zeroEthAddr
= { { 0 } };
1719 mDNSexport
const mDNSv4Addr onesIPv4Addr
= { { 255, 255, 255, 255 } };
1720 mDNSexport
const mDNSv6Addr onesIPv6Addr
= { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
1721 mDNSexport
const mDNSAddr zeroAddr
= { mDNSAddrType_None
, {{{ 0 }}} };
1723 mDNSexport
const mDNSInterfaceID mDNSInterface_Any
= 0;
1724 mDNSexport
const mDNSInterfaceID mDNSInterface_LocalOnly
= (mDNSInterfaceID
)1;
1726 mDNSlocal
const mDNSInterfaceID mDNSInterfaceMark
= (mDNSInterfaceID
)~0;
1728 #define UnicastDNSPortAsNumber 53
1729 #define MulticastDNSPortAsNumber 5353
1730 mDNSexport
const mDNSIPPort UnicastDNSPort
= { { UnicastDNSPortAsNumber
>> 8, UnicastDNSPortAsNumber
& 0xFF } };
1731 mDNSexport
const mDNSIPPort MulticastDNSPort
= { { MulticastDNSPortAsNumber
>> 8, MulticastDNSPortAsNumber
& 0xFF } };
1732 mDNSexport
const mDNSv4Addr AllDNSAdminGroup
= { { 239, 255, 255, 251 } };
1733 mDNSexport
const mDNSv4Addr AllDNSLinkGroupv4
= { { 224, 0, 0, 251 } };
1734 mDNSexport
const mDNSv6Addr AllDNSLinkGroupv6
= { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } };
1735 mDNSexport
const mDNSAddr AllDNSLinkGroup_v4
= { mDNSAddrType_IPv4
, { { { 224, 0, 0, 251 } } } };
1736 mDNSexport
const mDNSAddr AllDNSLinkGroup_v6
= { mDNSAddrType_IPv6
, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
1738 mDNSexport
const mDNSOpaque16 zeroID
= { { 0, 0 } };
1739 mDNSexport
const mDNSOpaque16 QueryFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
, 0 } };
1740 mDNSexport
const mDNSOpaque16 ResponseFlags
= { { kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
| kDNSFlag0_AA
, 0 } };
1741 mDNSexport
const mDNSOpaque16 UpdateReqFlags
= { { kDNSFlag0_QR_Query
| kDNSFlag0_OP_Update
, 0 } };
1742 mDNSexport
const mDNSOpaque16 UpdateRespFlags
={ { kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
, 0 } };
1744 // Any records bigger than this are considered 'large' records
1745 #define SmallRecordLimit 1024
1747 #define kMaxUpdateCredits 10
1748 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1750 mDNSexport
const char *const mDNS_DomainTypeNames
[] =
1752 "b._dns-sd._udp.", // Browse
1753 "db._dns-sd._udp.", // Default Browse
1754 "lb._dns-sd._udp.", // Legacy Browse
1755 "r._dns-sd._udp.", // Registration
1756 "dr._dns-sd._udp." // Default Registration
1759 #ifdef UNICAST_DISABLED
1760 #define uDNS_IsActiveQuery(q, u) mDNSfalse
1763 // ***************************************************************************
1764 #if COMPILER_LIKES_PRAGMA_MARK
1766 #pragma mark - Specialized mDNS version of vsnprintf
1769 static const struct mDNSprintf_format
1771 unsigned leftJustify
: 1;
1772 unsigned forceSign
: 1;
1773 unsigned zeroPad
: 1;
1774 unsigned havePrecision
: 1;
1778 char sign
; // +, - or space
1779 unsigned int fieldWidth
;
1780 unsigned int precision
;
1781 } mDNSprintf_format_default
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1783 mDNSexport mDNSu32
mDNS_vsnprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, va_list arg
)
1785 mDNSu32 nwritten
= 0;
1787 if (buflen
== 0) return(0);
1788 buflen
--; // Pre-reserve one space in the buffer for the terminating nul
1789 if (buflen
== 0) goto exit
;
1791 for (c
= *fmt
; c
!= 0; c
= *++fmt
)
1795 *sbuffer
++ = (char)c
;
1796 if (++nwritten
>= buflen
) goto exit
;
1800 unsigned int i
=0, j
;
1801 // The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
1802 // generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
1803 // The size needs to be enough for a 256-byte domain name plus some error text.
1804 #define mDNS_VACB_Size 300
1805 char mDNS_VACB
[mDNS_VACB_Size
];
1806 #define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
1807 #define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
1808 char *s
= mDNS_VACB_Lim
, *digits
;
1809 struct mDNSprintf_format F
= mDNSprintf_format_default
;
1811 while (1) // decode flags
1814 if (c
== '-') F
.leftJustify
= 1;
1815 else if (c
== '+') F
.forceSign
= 1;
1816 else if (c
== ' ') F
.sign
= ' ';
1817 else if (c
== '#') F
.altForm
++;
1818 else if (c
== '0') F
.zeroPad
= 1;
1822 if (c
== '*') // decode field width
1824 int f
= va_arg(arg
, int);
1825 if (f
< 0) { f
= -f
; F
.leftJustify
= 1; }
1826 F
.fieldWidth
= (unsigned int)f
;
1831 for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
1832 F
.fieldWidth
= (10 * F
.fieldWidth
) + (c
- '0');
1835 if (c
== '.') // decode precision
1837 if ((c
= *++fmt
) == '*')
1838 { F
.precision
= va_arg(arg
, unsigned int); c
= *++fmt
; }
1839 else for (; c
>= '0' && c
<= '9'; c
= *++fmt
)
1840 F
.precision
= (10 * F
.precision
) + (c
- '0');
1841 F
.havePrecision
= 1;
1844 if (F
.leftJustify
) F
.zeroPad
= 0;
1847 switch (c
) // perform appropriate conversion
1850 case 'h' : F
.hSize
= 1; c
= *++fmt
; goto conv
;
1851 case 'l' : // fall through
1852 case 'L' : F
.lSize
= 1; c
= *++fmt
; goto conv
;
1854 case 'i' : if (F
.lSize
) n
= (unsigned long)va_arg(arg
, long);
1855 else n
= (unsigned long)va_arg(arg
, int);
1856 if (F
.hSize
) n
= (short) n
;
1857 if ((long) n
< 0) { n
= (unsigned long)-(long)n
; F
.sign
= '-'; }
1858 else if (F
.forceSign
) F
.sign
= '+';
1860 case 'u' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1861 else n
= va_arg(arg
, unsigned int);
1862 if (F
.hSize
) n
= (unsigned short) n
;
1865 decimal
: if (!F
.havePrecision
)
1869 F
.precision
= F
.fieldWidth
;
1870 if (F
.sign
) --F
.precision
;
1872 if (F
.precision
< 1) F
.precision
= 1;
1874 if (F
.precision
> mDNS_VACB_Size
- 1)
1875 F
.precision
= mDNS_VACB_Size
- 1;
1876 for (i
= 0; n
; n
/= 10, i
++) *--s
= (char)(n
% 10 + '0');
1877 for (; i
< F
.precision
; i
++) *--s
= '0';
1878 if (F
.sign
) { *--s
= F
.sign
; i
++; }
1881 case 'o' : if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1882 else n
= va_arg(arg
, unsigned int);
1883 if (F
.hSize
) n
= (unsigned short) n
;
1884 if (!F
.havePrecision
)
1886 if (F
.zeroPad
) F
.precision
= F
.fieldWidth
;
1887 if (F
.precision
< 1) F
.precision
= 1;
1889 if (F
.precision
> mDNS_VACB_Size
- 1)
1890 F
.precision
= mDNS_VACB_Size
- 1;
1891 for (i
= 0; n
; n
/= 8, i
++) *--s
= (char)(n
% 8 + '0');
1892 if (F
.altForm
&& i
&& *s
!= '0') { *--s
= '0'; i
++; }
1893 for (; i
< F
.precision
; i
++) *--s
= '0';
1897 unsigned char *a
= va_arg(arg
, unsigned char *);
1898 if (!a
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
1901 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
1904 mDNSAddr
*ip
= (mDNSAddr
*)a
;
1907 case mDNSAddrType_IPv4
: F
.precision
= 4; a
= (unsigned char *)&ip
->ip
.v4
; break;
1908 case mDNSAddrType_IPv6
: F
.precision
= 16; a
= (unsigned char *)&ip
->ip
.v6
; break;
1909 default: F
.precision
= 0; break;
1912 switch (F
.precision
)
1914 case 4: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%d.%d.%d.%d",
1915 a
[0], a
[1], a
[2], a
[3]); break;
1916 case 6: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%02X:%02X:%02X:%02X:%02X:%02X",
1917 a
[0], a
[1], a
[2], a
[3], a
[4], a
[5]); break;
1918 case 16: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
),
1919 "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
1920 a
[0x0], a
[0x1], a
[0x2], a
[0x3], a
[0x4], a
[0x5], a
[0x6], a
[0x7],
1921 a
[0x8], a
[0x9], a
[0xA], a
[0xB], a
[0xC], a
[0xD], a
[0xE], a
[0xF]); break;
1922 default: i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "%s", "<< ERROR: Must specify address size "
1923 "(i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
1929 case 'p' : F
.havePrecision
= F
.lSize
= 1;
1931 case 'X' : digits
= "0123456789ABCDEF";
1933 case 'x' : digits
= "0123456789abcdef";
1934 hexadecimal
:if (F
.lSize
) n
= va_arg(arg
, unsigned long);
1935 else n
= va_arg(arg
, unsigned int);
1936 if (F
.hSize
) n
= (unsigned short) n
;
1937 if (!F
.havePrecision
)
1941 F
.precision
= F
.fieldWidth
;
1942 if (F
.altForm
) F
.precision
-= 2;
1944 if (F
.precision
< 1) F
.precision
= 1;
1946 if (F
.precision
> mDNS_VACB_Size
- 1)
1947 F
.precision
= mDNS_VACB_Size
- 1;
1948 for (i
= 0; n
; n
/= 16, i
++) *--s
= digits
[n
% 16];
1949 for (; i
< F
.precision
; i
++) *--s
= '0';
1950 if (F
.altForm
) { *--s
= (char)c
; *--s
= '0'; i
+= 2; }
1953 case 'c' : *--s
= (char)va_arg(arg
, int); i
= 1; break;
1955 case 's' : s
= va_arg(arg
, char *);
1956 if (!s
) { static char emsg
[] = "<<NULL>>"; s
= emsg
; i
= sizeof(emsg
)-1; }
1957 else switch (F
.altForm
)
1960 if (!F
.havePrecision
) // C string
1964 while ((i
< F
.precision
) && s
[i
]) i
++;
1965 // Make sure we don't truncate in the middle of a UTF-8 character
1966 // If last character we got was any kind of UTF-8 multi-byte character,
1967 // then see if we have to back up.
1968 // This is not as easy as the similar checks below, because
1969 // here we can't assume it's safe to examine the *next* byte, so we
1970 // have to confine ourselves to working only backwards in the string.
1971 j
= i
; // Record where we got to
1972 // Now, back up until we find first non-continuation-char
1973 while (i
>0 && (s
[i
-1] & 0xC0) == 0x80) i
--;
1974 // Now s[i-1] is the first non-continuation-char
1975 // and (j-i) is the number of continuation-chars we found
1976 if (i
>0 && (s
[i
-1] & 0xC0) == 0xC0) // If we found a start-char
1978 i
--; // Tentatively eliminate this start-char as well
1979 // Now (j-i) is the number of characters we're considering eliminating.
1980 // To be legal UTF-8, the start-char must contain (j-i) one-bits,
1981 // followed by a zero bit. If we shift it right by (7-(j-i)) bits
1982 // (with sign extension) then the result has to be 0xFE.
1983 // If this is right, then we reinstate the tentatively eliminated bytes.
1984 if (((j
-i
) < 7) && (((s
[i
] >> (7-(j
-i
))) & 0xFF) == 0xFE)) i
= j
;
1988 case 1: i
= (unsigned char) *s
++; break; // Pascal string
1989 case 2: { // DNS label-sequence name
1990 unsigned char *a
= (unsigned char *)s
;
1991 s
= mDNS_VACB
; // Adjust s to point to the start of the buffer, not the end
1992 if (*a
== 0) *s
++ = '.'; // Special case for root DNS name
1995 if (*a
> 63) { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<INVALID LABEL LENGTH %u>>", *a
); break; }
1996 if (s
+ *a
>= &mDNS_VACB
[254]) { s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "<<NAME TOO LONG>>"); break; }
1997 s
+= mDNS_snprintf(s
, mDNS_VACB_Remain(s
), "%#s.", a
);
2000 i
= (mDNSu32
)(s
- mDNS_VACB
);
2001 s
= mDNS_VACB
; // Reset s back to the start of the buffer
2005 // Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
2006 if (F
.havePrecision
&& i
> F
.precision
)
2007 { i
= F
.precision
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2010 case 'n' : s
= va_arg(arg
, char *);
2011 if (F
.hSize
) * (short *) s
= (short)nwritten
;
2012 else if (F
.lSize
) * (long *) s
= (long)nwritten
;
2013 else * (int *) s
= (int)nwritten
;
2016 default: s
= mDNS_VACB
;
2017 i
= mDNS_snprintf(mDNS_VACB
, sizeof(mDNS_VACB
), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c
);
2019 case '%' : *sbuffer
++ = (char)c
;
2020 if (++nwritten
>= buflen
) goto exit
;
2024 if (i
< F
.fieldWidth
&& !F
.leftJustify
) // Pad on the left
2027 if (++nwritten
>= buflen
) goto exit
;
2028 } while (i
< --F
.fieldWidth
);
2030 // Make sure we don't truncate in the middle of a UTF-8 character.
2031 // 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
2032 // 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
2033 // 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).
2034 if (i
> buflen
- nwritten
)
2035 { i
= buflen
- nwritten
; while (i
>0 && (s
[i
] & 0xC0) == 0x80) i
--; }
2036 for (j
=0; j
<i
; j
++) *sbuffer
++ = *s
++; // Write the converted result
2038 if (nwritten
>= buflen
) goto exit
;
2040 for (; i
< F
.fieldWidth
; i
++) // Pad on the right
2043 if (++nwritten
>= buflen
) goto exit
;
2052 mDNSexport mDNSu32
mDNS_snprintf(char *sbuffer
, mDNSu32 buflen
, const char *fmt
, ...)
2058 length
= mDNS_vsnprintf(sbuffer
, buflen
, fmt
, ptr
);
2064 // ***************************************************************************
2065 #if COMPILER_LIKES_PRAGMA_MARK
2067 #pragma mark - General Utility Functions
2070 #define InitialQuestionInterval (mDNSPlatformOneSecond/2)
2071 #define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
2072 #define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
2074 mDNSlocal
void SetNextQueryTime(mDNS
*const m
, const DNSQuestion
*const q
)
2076 if (ActiveQuestion(q
))
2077 if (m
->NextScheduledQuery
- (q
->LastQTime
+ q
->ThisQInterval
) > 0)
2078 m
->NextScheduledQuery
= (q
->LastQTime
+ q
->ThisQInterval
);
2081 mDNSlocal CacheGroup
*CacheGroupForName(const mDNS
*const m
, const mDNSu32 slot
, const mDNSu32 namehash
, const domainname
*const name
)
2084 for (cg
= m
->rrcache_hash
[slot
]; cg
; cg
=cg
->next
)
2085 if (cg
->namehash
== namehash
&& SameDomainName(cg
->name
, name
))
2090 mDNSlocal CacheGroup
*CacheGroupForRecord(const mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
2092 return(CacheGroupForName(m
, slot
, rr
->namehash
, rr
->name
));
2095 mDNSlocal mDNSBool
AddressIsLocalSubnet(mDNS
*const m
, const mDNSInterfaceID InterfaceID
, const mDNSAddr
*addr
)
2097 NetworkInterfaceInfo
*intf
;
2099 if (addr
->type
== mDNSAddrType_IPv4
)
2101 if (addr
->ip
.v4
.b
[0] == 169 && addr
->ip
.v4
.b
[1] == 254) return(mDNStrue
);
2102 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2103 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
2104 if (((intf
->ip
.ip
.v4
.NotAnInteger
^ addr
->ip
.v4
.NotAnInteger
) & intf
->mask
.ip
.v4
.NotAnInteger
) == 0)
2108 if (addr
->type
== mDNSAddrType_IPv6
)
2110 if (addr
->ip
.v6
.b
[0] == 0xFE && addr
->ip
.v6
.b
[1] == 0x80) return(mDNStrue
);
2111 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2112 if (intf
->ip
.type
== addr
->type
&& intf
->InterfaceID
== InterfaceID
&& intf
->McastTxRx
)
2113 if ((((intf
->ip
.ip
.v6
.l
[0] ^ addr
->ip
.v6
.l
[0]) & intf
->mask
.ip
.v6
.l
[0]) == 0) &&
2114 (((intf
->ip
.ip
.v6
.l
[1] ^ addr
->ip
.v6
.l
[1]) & intf
->mask
.ip
.v6
.l
[1]) == 0) &&
2115 (((intf
->ip
.ip
.v6
.l
[2] ^ addr
->ip
.v6
.l
[2]) & intf
->mask
.ip
.v6
.l
[2]) == 0) &&
2116 (((intf
->ip
.ip
.v6
.l
[3] ^ addr
->ip
.v6
.l
[3]) & intf
->mask
.ip
.v6
.l
[3]) == 0))
2123 // Set up a AuthRecord with sensible default values.
2124 // These defaults may be overwritten with new values before mDNS_Register is called
2125 mDNSexport
void mDNS_SetupResourceRecord(AuthRecord
*rr
, RData
*RDataStorage
, mDNSInterfaceID InterfaceID
,
2126 mDNSu16 rrtype
, mDNSu32 ttl
, mDNSu8 RecordType
, mDNSRecordCallback Callback
, void *Context
)
2128 mDNSPlatformMemZero(&rr
->uDNS_info
, sizeof(uDNS_RegInfo
));
2129 // Don't try to store a TTL bigger than we can represent in platform time units
2130 if (ttl
> 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
)
2131 ttl
= 0x7FFFFFFFUL
/ mDNSPlatformOneSecond
;
2132 else if (ttl
== 0) // And Zero TTL is illegal
2133 ttl
= DefaultTTLforRRType(rrtype
);
2135 // Field Group 1: The actual information pertaining to this resource record
2136 rr
->resrec
.RecordType
= RecordType
;
2137 rr
->resrec
.InterfaceID
= InterfaceID
;
2138 rr
->resrec
.name
= &rr
->namestorage
;
2139 rr
->resrec
.rrtype
= rrtype
;
2140 rr
->resrec
.rrclass
= kDNSClass_IN
;
2141 rr
->resrec
.rroriginalttl
= ttl
;
2142 // rr->resrec.rdlength = MUST set by client and/or in mDNS_Register_internal
2143 // rr->resrec.rdestimate = set in mDNS_Register_internal
2144 // rr->resrec.rdata = MUST be set by client
2147 rr
->resrec
.rdata
= RDataStorage
;
2150 rr
->resrec
.rdata
= &rr
->rdatastorage
;
2151 rr
->resrec
.rdata
->MaxRDLength
= sizeof(RDataBody
);
2154 // Field Group 2: Persistent metadata for Authoritative Records
2155 rr
->Additional1
= mDNSNULL
;
2156 rr
->Additional2
= mDNSNULL
;
2157 rr
->DependentOn
= mDNSNULL
;
2158 rr
->RRSet
= mDNSNULL
;
2159 rr
->RecordCallback
= Callback
;
2160 rr
->RecordContext
= Context
;
2162 rr
->HostTarget
= mDNSfalse
;
2163 rr
->AllowRemoteQuery
= mDNSfalse
;
2164 rr
->ForceMCast
= mDNSfalse
;
2166 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
2168 rr
->namestorage
.c
[0] = 0; // MUST be set by client before calling mDNS_Register()
2171 // For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
2172 // Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
2173 mDNSlocal
void AnswerLocalOnlyQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, AuthRecord
*rr
, mDNSBool AddRecord
)
2175 // Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
2176 if (AddRecord
) rr
->LocalAnswer
= mDNStrue
;
2177 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2178 if (q
->QuestionCallback
)
2179 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
2180 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2183 // When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers to each,
2184 // stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
2185 // If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
2186 // Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
2187 mDNSlocal
void AnswerLocalQuestions(mDNS
*const m
, AuthRecord
*rr
, mDNSBool AddRecord
)
2189 if (m
->CurrentQuestion
) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2191 m
->CurrentQuestion
= m
->LocalOnlyQuestions
;
2192 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewLocalOnlyQuestions
)
2194 DNSQuestion
*q
= m
->CurrentQuestion
;
2195 m
->CurrentQuestion
= q
->next
;
2196 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2197 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
2200 // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
2201 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
2203 m
->CurrentQuestion
= m
->Questions
;
2204 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
2206 DNSQuestion
*q
= m
->CurrentQuestion
;
2207 m
->CurrentQuestion
= q
->next
;
2208 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
2209 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, AddRecord
); // MUST NOT dereference q again
2213 m
->CurrentQuestion
= mDNSNULL
;
2216 // ***************************************************************************
2217 #if COMPILER_LIKES_PRAGMA_MARK
2219 #pragma mark - Resource Record Utility Functions
2222 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
2224 #define ResourceRecordIsValidAnswer(RR) ( ((RR)-> resrec.RecordType & kDNSRecordTypeActiveMask) && \
2225 ((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2226 ((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
2227 ((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask)) )
2229 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2230 (ResourceRecordIsValidAnswer(RR) && \
2231 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
2233 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2234 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
2236 #define InitialAnnounceCount ((mDNSu8)10)
2238 // Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
2239 // This means that because the announce interval is doubled after sending the first packet, the first
2240 // observed on-the-wire inter-packet interval between announcements is actually one second.
2241 // The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
2242 #define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
2243 #define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
2244 #define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
2246 #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
2247 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
2248 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
2250 #define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
2251 #define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
2252 #define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
2253 #define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
2255 #define MaxUnansweredQueries 4
2257 // SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
2258 // (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
2259 // TTL and rdata may differ.
2260 // This is used for cache flush management:
2261 // When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
2262 // When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
2263 mDNSlocal mDNSBool
SameResourceRecordSignature(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
2265 if (!r1
) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse
); }
2266 if (!r2
) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse
); }
2267 if (r1
->InterfaceID
&&
2269 r1
->InterfaceID
!= r2
->InterfaceID
) return(mDNSfalse
);
2270 return(mDNSBool
)(r1
->rrtype
== r2
->rrtype
&& r1
->rrclass
== r2
->rrclass
&& r1
->namehash
== r2
->namehash
&& SameDomainName(r1
->name
, r2
->name
));
2273 // PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
2274 // authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
2275 // complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
2276 // In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
2277 // so a response of any type should match, even if it is not actually the type the client plans to use.
2278 mDNSlocal mDNSBool
PacketRRMatchesSignature(const CacheRecord
*const pktrr
, const AuthRecord
*const authrr
)
2280 if (!pktrr
) { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse
); }
2281 if (!authrr
) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse
); }
2282 if (pktrr
->resrec
.InterfaceID
&&
2283 authrr
->resrec
.InterfaceID
&&
2284 pktrr
->resrec
.InterfaceID
!= authrr
->resrec
.InterfaceID
) return(mDNSfalse
);
2285 if (!(authrr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) && pktrr
->resrec
.rrtype
!= authrr
->resrec
.rrtype
) return(mDNSfalse
);
2286 return(mDNSBool
)(pktrr
->resrec
.rrclass
== authrr
->resrec
.rrclass
&& pktrr
->resrec
.namehash
== authrr
->resrec
.namehash
&& SameDomainName(pktrr
->resrec
.name
, authrr
->resrec
.name
));
2289 // IdenticalResourceRecord returns true if two resources records have
2290 // the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
2291 mDNSlocal mDNSBool
IdenticalResourceRecord(const ResourceRecord
*const r1
, const ResourceRecord
*const r2
)
2293 if (!r1
) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse
); }
2294 if (!r2
) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse
); }
2295 if (r1
->rrtype
!= r2
->rrtype
|| r1
->rrclass
!= r2
->rrclass
|| r1
->namehash
!= r2
->namehash
|| !SameDomainName(r1
->name
, r2
->name
)) return(mDNSfalse
);
2296 return(SameRData(r1
, r2
));
2299 // CacheRecord *ks is the CacheRecord from the known answer list in the query.
2300 // This is the information that the requester believes to be correct.
2301 // AuthRecord *rr is the answer we are proposing to give, if not suppressed.
2302 // This is the information that we believe to be correct.
2303 // We've already determined that we plan to give this answer on this interface
2304 // (either the record is non-specific, or it is specific to this interface)
2305 // so now we just need to check the name, type, class, rdata and TTL.
2306 mDNSlocal mDNSBool
ShouldSuppressKnownAnswer(const CacheRecord
*const ka
, const AuthRecord
*const rr
)
2308 // If RR signature is different, or data is different, then don't suppress our answer
2309 if (!IdenticalResourceRecord(&ka
->resrec
, &rr
->resrec
)) return(mDNSfalse
);
2311 // If the requester's indicated TTL is less than half the real TTL,
2312 // we need to give our answer before the requester's copy expires.
2313 // If the requester's indicated TTL is at least half the real TTL,
2314 // then we can suppress our answer this time.
2315 // If the requester's indicated TTL is greater than the TTL we believe,
2316 // then that's okay, and we don't need to do anything about it.
2317 // (If two responders on the network are offering the same information,
2318 // that's okay, and if they are offering the information with different TTLs,
2319 // the one offering the lower TTL should defer to the one offering the higher TTL.)
2320 return(mDNSBool
)(ka
->resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/ 2);
2323 mDNSlocal
void SetNextAnnounceProbeTime(mDNS
*const m
, const AuthRecord
*const rr
)
2325 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
2327 //LogMsg("ProbeCount %d Next %ld %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
2328 if (m
->NextScheduledProbe
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
2329 m
->NextScheduledProbe
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
2331 else if (rr
->AnnounceCount
&& ResourceRecordIsValidAnswer(rr
))
2333 if (m
->NextScheduledResponse
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) >= 0)
2334 m
->NextScheduledResponse
= (rr
->LastAPTime
+ rr
->ThisAPInterval
);
2338 mDNSlocal
void InitializeLastAPTime(mDNS
*const m
, AuthRecord
*const rr
)
2340 // To allow us to aggregate probes when a group of services are registered together,
2341 // the first probe is delayed 1/4 second. This means the common-case behaviour is:
2342 // 1/4 second wait; probe
2343 // 1/4 second wait; probe
2344 // 1/4 second wait; probe
2345 // 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
2347 // If we have no probe suppression time set, or it is in the past, set it now
2348 if (m
->SuppressProbes
== 0 || m
->SuppressProbes
- m
->timenow
< 0)
2350 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ DefaultProbeIntervalForTypeUnique
);
2351 // If we already have a probe scheduled to go out sooner, then use that time to get better aggregation
2352 if (m
->SuppressProbes
- m
->NextScheduledProbe
>= 0)
2353 m
->SuppressProbes
= m
->NextScheduledProbe
;
2354 // If we already have a query scheduled to go out sooner, then use that time to get better aggregation
2355 if (m
->SuppressProbes
- m
->NextScheduledQuery
>= 0)
2356 m
->SuppressProbes
= m
->NextScheduledQuery
;
2359 // We announce to flush stale data from other caches. It is a reasonable assumption that any
2360 // old stale copies will probably have the same TTL we're using, so announcing longer than
2361 // this serves no purpose -- any stale copies of that record will have expired by then anyway.
2362 rr
->AnnounceUntil
= m
->timenow
+ TicksTTL(rr
);
2363 rr
->LastAPTime
= m
->SuppressProbes
- rr
->ThisAPInterval
;
2364 // Set LastMCTime to now, to inhibit multicast responses
2365 // (no need to send additional multicast responses when we're announcing anyway)
2366 rr
->LastMCTime
= m
->timenow
;
2367 rr
->LastMCInterface
= mDNSInterfaceMark
;
2369 // If this is a record type that's not going to probe, then delay its first announcement so that
2370 // it will go out synchronized with the first announcement for the other records that *are* probing.
2371 // This is a minor performance tweak that helps keep groups of related records synchronized together.
2372 // The addition of "rr->ThisAPInterval / 2" is to make sure that, in the event that any of the probes are
2373 // delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
2374 // When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
2375 // because they will meet the criterion of being at least half-way to their scheduled announcement time.
2376 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
)
2377 rr
->LastAPTime
+= DefaultProbeIntervalForTypeUnique
* DefaultProbeCountForTypeUnique
+ rr
->ThisAPInterval
/ 2;
2379 SetNextAnnounceProbeTime(m
, rr
);
2382 #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
2384 mDNSlocal
void SetTargetToHostName(mDNS
*const m
, AuthRecord
*const rr
)
2386 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
2388 if (!target
) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr
->resrec
.rrtype
);
2390 if (target
&& SameDomainName(target
, &m
->MulticastHostname
))
2391 debugf("SetTargetToHostName: Target of %##s is already %##s", rr
->resrec
.name
->c
, target
->c
);
2393 if (target
&& !SameDomainName(target
, &m
->MulticastHostname
))
2395 AssignDomainName(target
, &m
->MulticastHostname
);
2396 SetNewRData(&rr
->resrec
, mDNSNULL
, 0);
2398 // If we're in the middle of probing this record, we need to start again,
2399 // because changing its rdata may change the outcome of the tie-breaker.
2400 // (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
2401 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
2403 // If we've announced this record, we really should send a goodbye packet for the old rdata before
2404 // changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
2405 // so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
2406 if (rr
->RequireGoodbye
&& rr
->resrec
.RecordType
== kDNSRecordTypeShared
)
2407 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
));
2409 rr
->AnnounceCount
= InitialAnnounceCount
;
2410 rr
->RequireGoodbye
= mDNSfalse
;
2411 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
2412 InitializeLastAPTime(m
,rr
);
2416 mDNSlocal
void AcknowledgeRecord(mDNS
*const m
, AuthRecord
*const rr
)
2418 if (!rr
->Acknowledged
&& rr
->RecordCallback
)
2420 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2421 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2422 rr
->Acknowledged
= mDNStrue
;
2423 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2424 rr
->RecordCallback(m
, rr
, mStatus_NoError
);
2425 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2429 // Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
2430 #define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
2431 #define RecordIsLocalDuplicate(A,B) ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
2433 mDNSlocal mStatus
mDNS_Register_internal(mDNS
*const m
, AuthRecord
*const rr
)
2435 domainname
*target
= GetRRDomainNameTarget(&rr
->resrec
);
2437 AuthRecord
**p
= &m
->ResourceRecords
;
2438 AuthRecord
**d
= &m
->DuplicateRecords
;
2440 mDNSPlatformMemZero(&rr
->uDNS_info
, sizeof(uDNS_RegInfo
));
2442 if ((mDNSs32
)rr
->resrec
.rroriginalttl
<= 0)
2443 { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m
, rr
)); return(mStatus_BadParamErr
); }
2445 #ifndef UNICAST_DISABLED
2446 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| rr
->ForceMCast
|| IsLocalDomain(rr
->resrec
.name
))
2447 rr
->uDNS_info
.id
= zeroID
;
2448 else return uDNS_RegisterRecord(m
, rr
);
2451 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2452 while (*d
&& *d
!= rr
) d
=&(*d
)->next
;
2455 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
));
2456 return(mStatus_AlreadyRegistered
);
2459 if (rr
->DependentOn
)
2461 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
2462 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
2465 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
2466 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2467 return(mStatus_Invalid
);
2469 if (!(rr
->DependentOn
->resrec
.RecordType
& (kDNSRecordTypeUnique
| kDNSRecordTypeVerified
)))
2471 LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
2472 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->DependentOn
->resrec
.RecordType
);
2473 return(mStatus_Invalid
);
2477 // If this resource record is referencing a specific interface, make sure it exists
2478 if (rr
->resrec
.InterfaceID
&& rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
2480 NetworkInterfaceInfo
*intf
;
2481 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
2482 if (intf
->InterfaceID
== rr
->resrec
.InterfaceID
) break;
2485 debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr
->resrec
.InterfaceID
);
2486 return(mStatus_BadReferenceErr
);
2490 rr
->next
= mDNSNULL
;
2492 // Field Group 1: Persistent metadata for Authoritative Records
2493 // rr->Additional1 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2494 // rr->Additional2 = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2495 // rr->DependentOn = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2496 // rr->RRSet = set to mDNSNULL in mDNS_SetupResourceRecord; may be overridden by client
2497 // rr->Callback = already set in mDNS_SetupResourceRecord
2498 // rr->Context = already set in mDNS_SetupResourceRecord
2499 // rr->RecordType = already set in mDNS_SetupResourceRecord
2500 // rr->HostTarget = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2501 // rr->AllowRemoteQuery = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
2502 // Make sure target is not uninitialized data, or we may crash writing debugging log messages
2503 if (rr
->HostTarget
&& target
) target
->c
[0] = 0;
2505 // Field Group 2: Transient state for Authoritative Records
2506 rr
->Acknowledged
= mDNSfalse
;
2507 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
2508 rr
->AnnounceCount
= InitialAnnounceCount
;
2509 rr
->RequireGoodbye
= mDNSfalse
;
2510 rr
->LocalAnswer
= mDNSfalse
;
2511 rr
->IncludeInProbe
= mDNSfalse
;
2512 rr
->ImmedAnswer
= mDNSNULL
;
2513 rr
->ImmedUnicast
= mDNSfalse
;
2514 rr
->ImmedAdditional
= mDNSNULL
;
2515 rr
->SendRNow
= mDNSNULL
;
2516 rr
->v4Requester
= zerov4Addr
;
2517 rr
->v6Requester
= zerov6Addr
;
2518 rr
->NextResponse
= mDNSNULL
;
2519 rr
->NR_AnswerTo
= mDNSNULL
;
2520 rr
->NR_AdditionalTo
= mDNSNULL
;
2521 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
2522 if (!rr
->HostTarget
) InitializeLastAPTime(m
, rr
);
2523 // rr->AnnounceUntil = Set for us in InitializeLastAPTime()
2524 // rr->LastAPTime = Set for us in InitializeLastAPTime()
2525 // rr->LastMCTime = Set for us in InitializeLastAPTime()
2526 // rr->LastMCInterface = Set for us in InitializeLastAPTime()
2527 rr
->NewRData
= mDNSNULL
;
2528 rr
->newrdlength
= 0;
2529 rr
->UpdateCallback
= mDNSNULL
;
2530 rr
->UpdateCredits
= kMaxUpdateCredits
;
2531 rr
->NextUpdateCredit
= 0;
2532 rr
->UpdateBlocked
= 0;
2534 // rr->resrec.interface = already set in mDNS_SetupResourceRecord
2535 // rr->resrec.name->c = MUST be set by client
2536 // rr->resrec.rrtype = already set in mDNS_SetupResourceRecord
2537 // rr->resrec.rrclass = already set in mDNS_SetupResourceRecord
2538 // rr->resrec.rroriginalttl = already set in mDNS_SetupResourceRecord
2539 // rr->resrec.rdata = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
2542 SetTargetToHostName(m
, rr
); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
2545 rr
->resrec
.rdlength
= GetRDLength(&rr
->resrec
, mDNSfalse
);
2546 rr
->resrec
.rdestimate
= GetRDLength(&rr
->resrec
, mDNStrue
);
2549 if (!ValidateDomainName(rr
->resrec
.name
))
2550 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
2552 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
2553 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
2554 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
2555 if (rr
->resrec
.rrtype
== kDNSType_TXT
&& rr
->resrec
.rdlength
== 0) { rr
->resrec
.rdlength
= 1; rr
->resrec
.rdata
->u
.txt
.c
[0] = 0; }
2557 // Don't do this until *after* we've set rr->resrec.rdlength
2558 if (!ValidateRData(rr
->resrec
.rrtype
, rr
->resrec
.rdlength
, rr
->resrec
.rdata
))
2559 { LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m
, rr
)); return(mStatus_Invalid
); }
2561 rr
->resrec
.namehash
= DomainNameHashValue(rr
->resrec
.name
);
2562 rr
->resrec
.rdatahash
= target
? DomainNameHashValue(target
) : RDataHashValue(rr
->resrec
.rdlength
, &rr
->resrec
.rdata
->u
);
2564 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
2566 // If this is supposed to be unique, make sure we don't have any name conflicts
2567 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2569 const AuthRecord
*s1
= rr
->RRSet
? rr
->RRSet
: rr
;
2570 for (r
= m
->ResourceRecords
; r
; r
=r
->next
)
2572 const AuthRecord
*s2
= r
->RRSet
? r
->RRSet
: r
;
2573 if (s1
!= s2
&& SameResourceRecordSignature(&r
->resrec
, &rr
->resrec
) && !SameRData(&r
->resrec
, &rr
->resrec
))
2576 if (r
) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
2578 debugf("Name conflict %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2579 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
2580 rr
->resrec
.rroriginalttl
= 0;
2581 rr
->ImmedAnswer
= mDNSInterfaceMark
;
2582 m
->NextScheduledResponse
= m
->timenow
;
2587 // Now that we've finished building our new record, make sure it's not identical to one we already have
2588 for (r
= m
->ResourceRecords
; r
; r
=r
->next
) if (RecordIsLocalDuplicate(r
, rr
)) break;
2592 debugf("Adding to duplicate list %p %s", rr
, ARDisplayString(m
,rr
));
2594 // If the previous copy of this record is already verified unique,
2595 // then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
2596 // Setting ProbeCount to zero will cause SendQueries() to advance this record to
2597 // kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
2598 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& r
->resrec
.RecordType
== kDNSRecordTypeVerified
)
2603 debugf("Adding to active record list %p %s", rr
, ARDisplayString(m
,rr
));
2604 if (!m
->NewLocalRecords
) m
->NewLocalRecords
= rr
;
2608 // For records that are not going to probe, acknowledge them right away
2609 if (rr
->resrec
.RecordType
!= kDNSRecordTypeUnique
&& rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
2610 AcknowledgeRecord(m
, rr
);
2612 return(mStatus_NoError
);
2615 mDNSlocal
void RecordProbeFailure(mDNS
*const m
, const AuthRecord
*const rr
)
2617 m
->ProbeFailTime
= m
->timenow
;
2618 m
->NumFailedProbes
++;
2619 // If we've had fifteen or more probe failures, rate-limit to one every five seconds.
2620 // If a bunch of hosts have all been configured with the same name, then they'll all
2621 // conflict and run through the same series of names: name-2, name-3, name-4, etc.,
2622 // up to name-10. After that they'll start adding random increments in the range 1-100,
2623 // so they're more likely to branch out in the available namespace and settle on a set of
2624 // unique names quickly. If after five more tries the host is still conflicting, then we
2625 // may have a serious problem, so we start rate-limiting so we don't melt down the network.
2626 if (m
->NumFailedProbes
>= 15)
2628 m
->SuppressProbes
= NonZeroTime(m
->timenow
+ mDNSPlatformOneSecond
* 5);
2629 LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
2630 m
->NumFailedProbes
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2634 mDNSlocal
void CompleteRDataUpdate(mDNS
*const m
, AuthRecord
*const rr
)
2636 RData
*OldRData
= rr
->resrec
.rdata
;
2637 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
); // Update our rdata
2638 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
2639 if (rr
->UpdateCallback
)
2640 rr
->UpdateCallback(m
, rr
, OldRData
); // ... and let the client know
2643 // mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
2644 // mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
2645 // mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
2646 typedef enum { mDNS_Dereg_normal
, mDNS_Dereg_conflict
, mDNS_Dereg_repeat
} mDNS_Dereg_type
;
2648 // NOTE: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
2649 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2650 mDNSlocal mStatus
mDNS_Deregister_internal(mDNS
*const m
, AuthRecord
*const rr
, mDNS_Dereg_type drt
)
2653 mDNSu8 RecordType
= rr
->resrec
.RecordType
;
2654 AuthRecord
**p
= &m
->ResourceRecords
; // Find this record in our list of active records
2656 #ifndef UNICAST_DISABLED
2657 if (!(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| rr
->ForceMCast
|| IsLocalDomain(rr
->resrec
.name
)))
2658 return uDNS_DeregisterRecord(m
, rr
);
2661 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2665 // We found our record on the main list. See if there are any duplicates that need special handling.
2666 if (drt
== mDNS_Dereg_conflict
) // If this was a conflict, see that all duplicates get the same treatment
2668 // Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
2669 // deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
2670 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
) if (RecordIsLocalDuplicate(r2
, rr
)) r2
->ProbeCount
= 0xFF;
2674 // Before we delete the record (and potentially send a goodbye packet)
2675 // first see if we have a record on the duplicate list ready to take over from it.
2676 AuthRecord
**d
= &m
->DuplicateRecords
;
2677 while (*d
&& !RecordIsLocalDuplicate(*d
, rr
)) d
=&(*d
)->next
;
2680 AuthRecord
*dup
= *d
;
2681 debugf("Duplicate record %p taking over from %p %##s (%s)", dup
, rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2682 *d
= dup
->next
; // Cut replacement record from DuplicateRecords list
2683 dup
->next
= rr
->next
; // And then...
2684 rr
->next
= dup
; // ... splice it in right after the record we're about to delete
2685 dup
->resrec
.RecordType
= rr
->resrec
.RecordType
;
2686 dup
->ProbeCount
= rr
->ProbeCount
;
2687 dup
->AnnounceCount
= rr
->AnnounceCount
;
2688 dup
->RequireGoodbye
= rr
->RequireGoodbye
;
2689 dup
->ImmedAnswer
= rr
->ImmedAnswer
;
2690 dup
->ImmedUnicast
= rr
->ImmedUnicast
;
2691 dup
->ImmedAdditional
= rr
->ImmedAdditional
;
2692 dup
->v4Requester
= rr
->v4Requester
;
2693 dup
->v6Requester
= rr
->v6Requester
;
2694 dup
->ThisAPInterval
= rr
->ThisAPInterval
;
2695 dup
->AnnounceUntil
= rr
->AnnounceUntil
;
2696 dup
->LastAPTime
= rr
->LastAPTime
;
2697 dup
->LastMCTime
= rr
->LastMCTime
;
2698 dup
->LastMCInterface
= rr
->LastMCInterface
;
2699 rr
->RequireGoodbye
= mDNSfalse
;
2705 // We didn't find our record on the main list; try the DuplicateRecords list instead.
2706 p
= &m
->DuplicateRecords
;
2707 while (*p
&& *p
!= rr
) p
=&(*p
)->next
;
2708 // If we found our record on the duplicate list, then make sure we don't send a goodbye for it
2709 if (*p
) rr
->RequireGoodbye
= mDNSfalse
;
2710 if (*p
) debugf("DNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2715 // No need to log an error message if we already know this is a potentially repeated deregistration
2716 if (drt
!= mDNS_Dereg_repeat
)
2717 LogMsg("mDNS_Deregister_internal: Record %p %##s (%s) not found in list", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2718 return(mStatus_BadReferenceErr
);
2721 // If this is a shared record and we've announced it at least once,
2722 // we need to retract that announcement before we delete the record
2723 if (RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
2725 verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2726 rr
->resrec
.RecordType
= kDNSRecordTypeDeregistering
;
2727 rr
->resrec
.rroriginalttl
= 0;
2728 rr
->ImmedAnswer
= mDNSInterfaceMark
;
2729 if (m
->NextScheduledResponse
- (m
->timenow
+ mDNSPlatformOneSecond
/10) >= 0)
2730 m
->NextScheduledResponse
= (m
->timenow
+ mDNSPlatformOneSecond
/10);
2734 *p
= rr
->next
; // Cut this record from the list
2735 // If someone is about to look at this, bump the pointer forward
2736 if (m
->CurrentRecord
== rr
) m
->CurrentRecord
= rr
->next
;
2737 if (m
->NewLocalRecords
== rr
) m
->NewLocalRecords
= rr
->next
;
2738 rr
->next
= mDNSNULL
;
2740 if (RecordType
== kDNSRecordTypeUnregistered
)
2741 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
2742 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2743 else if (RecordType
== kDNSRecordTypeDeregistering
)
2744 debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
2745 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2748 verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2749 rr
->resrec
.RecordType
= kDNSRecordTypeUnregistered
;
2752 if ((drt
== mDNS_Dereg_conflict
|| drt
== mDNS_Dereg_repeat
) && RecordType
== kDNSRecordTypeShared
)
2753 debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
2754 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2756 // If we have an update queued up which never executed, give the client a chance to free that memory
2757 if (rr
->NewRData
) CompleteRDataUpdate(m
, rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
2759 if (rr
->LocalAnswer
) AnswerLocalQuestions(m
, rr
, mDNSfalse
);
2761 // CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
2762 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
2763 // In this case the likely client action to the mStatus_MemFree message is to free the memory,
2764 // so any attempt to touch rr after this is likely to lead to a crash.
2765 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
2766 if (drt
!= mDNS_Dereg_conflict
)
2768 if (rr
->RecordCallback
) rr
->RecordCallback(m
, rr
, mStatus_MemFree
); // MUST NOT touch rr after this
2772 RecordProbeFailure(m
, rr
);
2773 if (rr
->RecordCallback
) rr
->RecordCallback(m
, rr
, mStatus_NameConflict
); // MUST NOT touch rr after this
2774 // Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
2775 // Note that with all the client callbacks going on, by the time we get here all the
2776 // records we marked may have been explicitly deregistered by the client anyway.
2777 r2
= m
->DuplicateRecords
;
2780 if (r2
->ProbeCount
!= 0xFF) r2
= r2
->next
;
2781 else { mDNS_Deregister_internal(m
, r2
, mDNS_Dereg_conflict
); r2
= m
->DuplicateRecords
; }
2784 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
2786 return(mStatus_NoError
);
2789 // ***************************************************************************
2790 #if COMPILER_LIKES_PRAGMA_MARK
2793 #pragma mark - Packet Sending Functions
2796 mDNSlocal
void AddRecordToResponseList(AuthRecord
***nrpp
, AuthRecord
*rr
, AuthRecord
*add
)
2798 if (rr
->NextResponse
== mDNSNULL
&& *nrpp
!= &rr
->NextResponse
)
2801 // NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
2802 // If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
2803 // The referenced record will definitely be acceptable (by recursive application of this rule)
2804 if (add
&& add
->NR_AdditionalTo
) add
= add
->NR_AdditionalTo
;
2805 rr
->NR_AdditionalTo
= add
;
2806 *nrpp
= &rr
->NextResponse
;
2808 debugf("AddRecordToResponseList: %##s (%s) already in list", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
2811 mDNSlocal
void AddAdditionalsToResponseList(mDNS
*const m
, AuthRecord
*ResponseRecords
, AuthRecord
***nrpp
, const mDNSInterfaceID InterfaceID
)
2813 AuthRecord
*rr
, *rr2
;
2814 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // For each record we plan to put
2816 // (Note: This is an "if", not a "while". If we add a record, we'll find it again
2817 // later in the "for" loop, and we will follow further "additional" links then.)
2818 if (rr
->Additional1
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional1
, InterfaceID
))
2819 AddRecordToResponseList(nrpp
, rr
->Additional1
, rr
);
2821 if (rr
->Additional2
&& ResourceRecordIsValidInterfaceAnswer(rr
->Additional2
, InterfaceID
))
2822 AddRecordToResponseList(nrpp
, rr
->Additional2
, rr
);
2824 // For SRV records, automatically add the Address record(s) for the target host
2825 if (rr
->resrec
.rrtype
== kDNSType_SRV
)
2826 for (rr2
=m
->ResourceRecords
; rr2
; rr2
=rr2
->next
) // Scan list of resource records
2827 if (RRTypeIsAddressType(rr2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
2828 ResourceRecordIsValidInterfaceAnswer(rr2
, InterfaceID
) && // ... which are valid for answer ...
2829 rr
->resrec
.rdatahash
== rr2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
2830 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, rr2
->resrec
.name
))
2831 AddRecordToResponseList(nrpp
, rr2
, rr
);
2835 mDNSlocal
void SendDelayedUnicastResponse(mDNS
*const m
, const mDNSAddr
*const dest
, const mDNSInterfaceID InterfaceID
)
2838 AuthRecord
*ResponseRecords
= mDNSNULL
;
2839 AuthRecord
**nrp
= &ResponseRecords
;
2841 // Make a list of all our records that need to be unicast to this destination
2842 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2844 // If we find we can no longer unicast this answer, clear ImmedUnicast
2845 if (rr
->ImmedAnswer
== mDNSInterfaceMark
||
2846 mDNSSameIPv4Address(rr
->v4Requester
, onesIPv4Addr
) ||
2847 mDNSSameIPv6Address(rr
->v6Requester
, onesIPv6Addr
) )
2848 rr
->ImmedUnicast
= mDNSfalse
;
2850 if (rr
->ImmedUnicast
&& rr
->ImmedAnswer
== InterfaceID
)
2851 if ((dest
->type
== mDNSAddrType_IPv4
&& mDNSSameIPv4Address(rr
->v4Requester
, dest
->ip
.v4
)) ||
2852 (dest
->type
== mDNSAddrType_IPv6
&& mDNSSameIPv6Address(rr
->v6Requester
, dest
->ip
.v6
)))
2854 rr
->ImmedAnswer
= mDNSNULL
; // Clear the state fields
2855 rr
->ImmedUnicast
= mDNSfalse
;
2856 rr
->v4Requester
= zerov4Addr
;
2857 rr
->v6Requester
= zerov6Addr
;
2858 if (rr
->NextResponse
== mDNSNULL
&& nrp
!= &rr
->NextResponse
) // rr->NR_AnswerTo
2859 { rr
->NR_AnswerTo
= (mDNSu8
*)~0; *nrp
= rr
; nrp
= &rr
->NextResponse
; }
2863 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
2865 while (ResponseRecords
)
2867 mDNSu8
*responseptr
= m
->omsg
.data
;
2869 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
2871 // Put answers in the packet
2872 while (ResponseRecords
&& ResponseRecords
->NR_AnswerTo
)
2874 rr
= ResponseRecords
;
2875 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2876 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
2877 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
2878 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
2879 if (!newptr
&& m
->omsg
.h
.numAnswers
) break; // If packet full, send it now
2880 if (newptr
) responseptr
= newptr
;
2881 ResponseRecords
= rr
->NextResponse
;
2882 rr
->NextResponse
= mDNSNULL
;
2883 rr
->NR_AnswerTo
= mDNSNULL
;
2884 rr
->NR_AdditionalTo
= mDNSNULL
;
2885 rr
->RequireGoodbye
= mDNStrue
;
2888 // Add additionals, if there's space
2889 while (ResponseRecords
&& !ResponseRecords
->NR_AnswerTo
)
2891 rr
= ResponseRecords
;
2892 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
2893 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
2894 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
2895 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
2897 if (newptr
) responseptr
= newptr
;
2898 if (newptr
&& m
->omsg
.h
.numAnswers
) rr
->RequireGoodbye
= mDNStrue
;
2899 else if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
) rr
->ImmedAnswer
= mDNSInterfaceMark
;
2900 ResponseRecords
= rr
->NextResponse
;
2901 rr
->NextResponse
= mDNSNULL
;
2902 rr
->NR_AnswerTo
= mDNSNULL
;
2903 rr
->NR_AdditionalTo
= mDNSNULL
;
2906 if (m
->omsg
.h
.numAnswers
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, mDNSInterface_Any
, dest
, MulticastDNSPort
, -1, mDNSNULL
);
2910 mDNSlocal
void CompleteDeregistration(mDNS
*const m
, AuthRecord
*rr
)
2912 // Clearing rr->RequireGoodbye signals mDNS_Deregister_internal()
2913 // that it should go ahead and immediately dispose of this registration
2914 rr
->resrec
.RecordType
= kDNSRecordTypeShared
;
2915 rr
->RequireGoodbye
= mDNSfalse
;
2916 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
); // Don't touch rr after this
2919 // NOTE: DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback, which may change
2920 // the record list and/or question list.
2921 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2922 mDNSlocal
void DiscardDeregistrations(mDNS
*const m
)
2924 if (m
->CurrentRecord
) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
2925 m
->CurrentRecord
= m
->ResourceRecords
;
2927 while (m
->CurrentRecord
)
2929 AuthRecord
*rr
= m
->CurrentRecord
;
2930 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
2931 CompleteDeregistration(m
, rr
); // Don't touch rr after this
2933 m
->CurrentRecord
= rr
->next
;
2937 mDNSlocal
void GrantUpdateCredit(AuthRecord
*rr
)
2939 if (++rr
->UpdateCredits
>= kMaxUpdateCredits
) rr
->NextUpdateCredit
= 0;
2940 else rr
->NextUpdateCredit
= NonZeroTime(rr
->NextUpdateCredit
+ kUpdateCreditRefreshInterval
);
2943 // Note about acceleration of announcements to facilitate automatic coalescing of
2944 // multiple independent threads of announcements into a single synchronized thread:
2945 // The announcements in the packet may be at different stages of maturity;
2946 // One-second interval, two-second interval, four-second interval, and so on.
2947 // After we've put in all the announcements that are due, we then consider
2948 // whether there are other nearly-due announcements that are worth accelerating.
2949 // To be eligible for acceleration, a record MUST NOT be older (further along
2950 // its timeline) than the most mature record we've already put in the packet.
2951 // In other words, younger records can have their timelines accelerated to catch up
2952 // with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
2953 // Older records cannot have their timelines accelerated; this would just widen
2954 // the gap between them and their younger bretheren and get them even more out of sync.
2956 // NOTE: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
2957 // the record list and/or question list.
2958 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
2959 mDNSlocal
void SendResponses(mDNS
*const m
)
2962 AuthRecord
*rr
, *r2
;
2963 mDNSs32 maxExistingAnnounceInterval
= 0;
2964 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
2966 m
->NextScheduledResponse
= m
->timenow
+ 0x78000000;
2968 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2969 if (rr
->ImmedUnicast
)
2971 mDNSAddr v4
= { mDNSAddrType_IPv4
, {{{0}}} };
2972 mDNSAddr v6
= { mDNSAddrType_IPv6
, {{{0}}} };
2973 v4
.ip
.v4
= rr
->v4Requester
;
2974 v6
.ip
.v6
= rr
->v6Requester
;
2975 if (!mDNSIPv4AddressIsZero(rr
->v4Requester
)) SendDelayedUnicastResponse(m
, &v4
, rr
->ImmedAnswer
);
2976 if (!mDNSIPv6AddressIsZero(rr
->v6Requester
)) SendDelayedUnicastResponse(m
, &v6
, rr
->ImmedAnswer
);
2977 if (rr
->ImmedUnicast
)
2979 LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m
, rr
));
2980 rr
->ImmedUnicast
= mDNSfalse
;
2985 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
2988 // Run through our list of records, and decide which ones we're going to announce on all interfaces
2989 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
2991 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
2992 if (TimeToAnnounceThisRecord(rr
, m
->timenow
) && ResourceRecordIsValidAnswer(rr
))
2994 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
2995 if (maxExistingAnnounceInterval
< rr
->ThisAPInterval
)
2996 maxExistingAnnounceInterval
= rr
->ThisAPInterval
;
2997 if (rr
->UpdateBlocked
) rr
->UpdateBlocked
= 0;
3001 // Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
3002 // Eligible records that are more than half-way to their announcement time are accelerated
3003 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3004 if ((rr
->resrec
.InterfaceID
&& rr
->ImmedAnswer
) ||
3005 (rr
->ThisAPInterval
<= maxExistingAnnounceInterval
&&
3006 TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2) &&
3007 ResourceRecordIsValidAnswer(rr
)))
3008 rr
->ImmedAnswer
= mDNSInterfaceMark
; // Send on all interfaces
3010 // When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
3011 // NOTE: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
3012 // which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
3013 // then all that means is that it won't get sent -- which would not be the end of the world.
3014 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3015 if (rr
->ImmedAnswer
&& rr
->resrec
.rrtype
== kDNSType_SRV
)
3016 for (r2
=m
->ResourceRecords
; r2
; r2
=r2
->next
) // Scan list of resource records
3017 if (RRTypeIsAddressType(r2
->resrec
.rrtype
) && // For all address records (A/AAAA) ...
3018 ResourceRecordIsValidAnswer(r2
) && // ... which are valid for answer ...
3019 rr
->LastMCTime
- r2
->LastMCTime
>= 0 && // ... which we have not sent recently ...
3020 rr
->resrec
.rdatahash
== r2
->resrec
.namehash
&& // ... whose name is the name of the SRV target
3021 SameDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, r2
->resrec
.name
) &&
3022 (rr
->ImmedAnswer
== mDNSInterfaceMark
|| rr
->ImmedAnswer
== r2
->resrec
.InterfaceID
))
3023 r2
->ImmedAdditional
= r2
->resrec
.InterfaceID
; // ... then mark this address record for sending too
3025 // If there's a record which is supposed to be unique that we're going to send, then make sure that we give
3026 // the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
3027 // then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
3028 // record, then other RRSet members that have not been sent recently will get flushed out of client caches.
3029 // -- 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
3030 // -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
3031 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3032 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3034 if (rr
->ImmedAnswer
) // If we're sending this as answer, see that its whole RRSet is similarly marked
3036 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
3037 if (ResourceRecordIsValidAnswer(r2
))
3038 if (r2
->ImmedAnswer
!= mDNSInterfaceMark
&& r2
->ImmedAnswer
!= rr
->ImmedAnswer
&& SameResourceRecordSignature(&r2
->resrec
, &rr
->resrec
))
3039 r2
->ImmedAnswer
= rr
->ImmedAnswer
;
3041 else if (rr
->ImmedAdditional
) // If we're sending this as additional, see that its whole RRSet is similarly marked
3043 for (r2
= m
->ResourceRecords
; r2
; r2
=r2
->next
)
3044 if (ResourceRecordIsValidAnswer(r2
))
3045 if (r2
->ImmedAdditional
!= rr
->ImmedAdditional
&& SameResourceRecordSignature(&r2
->resrec
, &rr
->resrec
))
3046 r2
->ImmedAdditional
= rr
->ImmedAdditional
;
3050 // Now set SendRNow state appropriately
3051 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3053 if (rr
->ImmedAnswer
== mDNSInterfaceMark
) // Sending this record on all appropriate interfaces
3055 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
3056 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional if sending as answer
3057 rr
->LastMCTime
= m
->timenow
;
3058 rr
->LastMCInterface
= rr
->ImmedAnswer
;
3059 // If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
3060 if (TimeToAnnounceThisRecord(rr
, m
->timenow
+ rr
->ThisAPInterval
/2))
3062 rr
->AnnounceCount
--;
3063 rr
->ThisAPInterval
*= 2;
3064 rr
->LastAPTime
= m
->timenow
;
3065 if (rr
->LastAPTime
+ rr
->ThisAPInterval
- rr
->AnnounceUntil
>= 0) rr
->AnnounceCount
= 0;
3066 debugf("Announcing %##s (%s) %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->AnnounceCount
);
3069 else if (rr
->ImmedAnswer
) // Else, just respond to a single query on single interface:
3071 rr
->SendRNow
= rr
->ImmedAnswer
; // Just respond on that interface
3072 rr
->ImmedAdditional
= mDNSNULL
; // No need to send as additional too
3073 rr
->LastMCTime
= m
->timenow
;
3074 rr
->LastMCInterface
= rr
->ImmedAnswer
;
3076 SetNextAnnounceProbeTime(m
, rr
);
3077 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
3081 // *** 2. Loop through interface list, sending records as appropriate
3087 int numAnnounce
= 0;
3089 mDNSu8
*responseptr
= m
->omsg
.data
;
3091 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, ResponseFlags
);
3093 // First Pass. Look for:
3094 // 1. Deregistering records that need to send their goodbye packet
3095 // 2. Updated records that need to retract their old data
3096 // 3. Answers and announcements we need to send
3097 // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
3098 // send this packet and then try again.
3099 // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
3100 // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
3101 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3102 if (rr
->SendRNow
== intf
->InterfaceID
)
3104 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
3106 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
3107 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3109 responseptr
= newptr
;
3111 else if (rr
->NewRData
) // If we have new data for this record
3113 RData
*OldRData
= rr
->resrec
.rdata
;
3114 mDNSu16 oldrdlength
= rr
->resrec
.rdlength
;
3115 // See if we should send a courtesy "goodbye" for the old data before we replace it.
3116 if (ResourceRecordIsValidAnswer(rr
) && rr
->RequireGoodbye
)
3118 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, 0);
3119 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3121 responseptr
= newptr
;
3123 // Now try to see if we can fit the update in the same packet (not fatal if we can't)
3124 SetNewRData(&rr
->resrec
, rr
->NewRData
, rr
->newrdlength
);
3125 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3126 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3127 newptr
= PutResourceRecord(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
);
3128 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3129 if (newptr
) responseptr
= newptr
;
3130 SetNewRData(&rr
->resrec
, OldRData
, oldrdlength
);
3134 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3135 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3136 newptr
= PutResourceRecordTTL(&m
->omsg
, responseptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, m
->SleepState
? 0 : rr
->resrec
.rroriginalttl
);
3137 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3138 if (!newptr
&& m
->omsg
.h
.numAnswers
) break;
3139 rr
->RequireGoodbye
= !m
->SleepState
;
3140 if (rr
->LastAPTime
== m
->timenow
) numAnnounce
++; else numAnswer
++;
3141 responseptr
= newptr
;
3143 // If sending on all interfaces, go to next interface; else we're finished now
3144 if (rr
->ImmedAnswer
== mDNSInterfaceMark
&& rr
->resrec
.InterfaceID
== mDNSInterface_Any
)
3145 rr
->SendRNow
= GetNextActiveInterfaceID(intf
);
3147 rr
->SendRNow
= mDNSNULL
;
3150 // Second Pass. Add additional records, if there's space.
3151 newptr
= responseptr
;
3152 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3153 if (rr
->ImmedAdditional
== intf
->InterfaceID
)
3154 if (ResourceRecordIsValidAnswer(rr
))
3156 // If we have at least one answer already in the packet, then plan to add additionals too
3157 mDNSBool SendAdditional
= (m
->omsg
.h
.numAnswers
> 0);
3159 // If we're not planning to send any additionals, but this record is a unique one, then
3160 // make sure we haven't already sent any other members of its RRSet -- if we have, then they
3161 // will have had the cache flush bit set, so now we need to finish the job and send the rest.
3162 if (!SendAdditional
&& (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
))
3164 const AuthRecord
*a
;
3165 for (a
= m
->ResourceRecords
; a
; a
=a
->next
)
3166 if (a
->LastMCTime
== m
->timenow
&&
3167 a
->LastMCInterface
== intf
->InterfaceID
&&
3168 SameResourceRecordSignature(&a
->resrec
, &rr
->resrec
)) { SendAdditional
= mDNStrue
; break; }
3170 if (!SendAdditional
) // If we don't want to send this after all,
3171 rr
->ImmedAdditional
= mDNSNULL
; // then cancel its ImmedAdditional field
3172 else if (newptr
) // Else, try to add it if we can
3174 if (rr
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)
3175 rr
->resrec
.rrclass
|= kDNSClass_UniqueRRSet
; // Temporarily set the cache flush bit so PutResourceRecord will set it
3176 newptr
= PutResourceRecord(&m
->omsg
, newptr
, &m
->omsg
.h
.numAdditionals
, &rr
->resrec
);
3177 rr
->resrec
.rrclass
&= ~kDNSClass_UniqueRRSet
; // Make sure to clear cache flush bit back to normal state
3180 responseptr
= newptr
;
3181 rr
->ImmedAdditional
= mDNSNULL
;
3182 rr
->RequireGoodbye
= mDNStrue
;
3183 // If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
3184 // This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
3185 // when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
3186 // all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
3187 rr
->LastMCTime
= m
->timenow
;
3188 rr
->LastMCInterface
= intf
->InterfaceID
;
3193 if (m
->omsg
.h
.numAnswers
> 0 || m
->omsg
.h
.numAdditionals
)
3195 debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
3196 numDereg
, numDereg
== 1 ? "" : "s",
3197 numAnnounce
, numAnnounce
== 1 ? "" : "s",
3198 numAnswer
, numAnswer
== 1 ? "" : "s",
3199 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s", intf
->InterfaceID
);
3200 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, -1, mDNSNULL
);
3201 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, responseptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, -1, mDNSNULL
);
3202 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
3203 if (++pktcount
>= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount
); break; }
3204 // There might be more things to send on this interface, so go around one more time and try again.
3206 else // Nothing more to send on this interface; go to next
3208 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
3209 #if MDNS_DEBUGMSGS && 0
3210 const char *const msg
= next
? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
3211 debugf(msg
, intf
, next
);
3218 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3221 if (m
->CurrentRecord
) LogMsg("SendResponses: ERROR m->CurrentRecord already set");
3222 m
->CurrentRecord
= m
->ResourceRecords
;
3223 while (m
->CurrentRecord
)
3225 rr
= m
->CurrentRecord
;
3226 m
->CurrentRecord
= rr
->next
;
3230 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
3231 LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m
, rr
));
3232 rr
->SendRNow
= mDNSNULL
;
3235 if (rr
->ImmedAnswer
)
3237 if (rr
->NewRData
) CompleteRDataUpdate(m
,rr
); // Update our rdata, clear the NewRData pointer, and return memory to the client
3239 if (rr
->resrec
.RecordType
== kDNSRecordTypeDeregistering
)
3240 CompleteDeregistration(m
, rr
); // Don't touch rr after this
3243 rr
->ImmedAnswer
= mDNSNULL
;
3244 rr
->ImmedUnicast
= mDNSfalse
;
3245 rr
->v4Requester
= zerov4Addr
;
3246 rr
->v6Requester
= zerov6Addr
;
3250 verbosedebugf("SendResponses: Next in %ld ticks", m
->NextScheduledResponse
- m
->timenow
);
3253 // Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
3254 // so we want to be lazy about how frequently we do it.
3255 // 1. If a cache record is currently referenced by *no* active questions,
3256 // then we don't mind expiring it up to a minute late (who will know?)
3257 // 2. Else, if a cache record is due for some of its final expiration queries,
3258 // we'll allow them to be late by up to 2% of the TTL
3259 // 3. Else, if a cache record has completed all its final expiration queries without success,
3260 // and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
3261 // 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
3262 // so allow at most 1/10 second lateness
3263 #define CacheCheckGracePeriod(RR) ( \
3264 ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
3265 ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \
3266 ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \
3267 ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10))
3269 // Note: MUST call SetNextCacheCheckTime any time we change:
3271 // rr->DelayDelivery
3272 // rr->resrec.rroriginalttl
3273 // rr->UnansweredQueries
3274 // rr->CRActiveQuestion
3275 mDNSlocal
void SetNextCacheCheckTime(mDNS
*const m
, CacheRecord
*const rr
)
3277 rr
->NextRequiredQuery
= RRExpireTime(rr
);
3279 // If we have an active question, then see if we want to schedule a refresher query for this record.
3280 // Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
3281 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
3283 rr
->NextRequiredQuery
-= TicksTTL(rr
)/20 * (MaxUnansweredQueries
- rr
->UnansweredQueries
);
3284 rr
->NextRequiredQuery
+= mDNSRandom((mDNSu32
)TicksTTL(rr
)/50);
3285 verbosedebugf("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec",
3286 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), (rr
->NextRequiredQuery
- m
->timenow
) / mDNSPlatformOneSecond
);
3289 if (m
->NextCacheCheck
- (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
)) > 0)
3290 m
->NextCacheCheck
= (rr
->NextRequiredQuery
+ CacheCheckGracePeriod(rr
));
3292 if (rr
->DelayDelivery
)
3293 if (m
->NextCacheCheck
- rr
->DelayDelivery
> 0)
3294 m
->NextCacheCheck
= rr
->DelayDelivery
;
3297 #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
3298 #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
3299 #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
3301 mDNSlocal mStatus
mDNS_Reconfirm_internal(mDNS
*const m
, CacheRecord
*const rr
, mDNSu32 interval
)
3303 if (interval
< kMinimumReconfirmTime
)
3304 interval
= kMinimumReconfirmTime
;
3305 if (interval
> 0x10000000) // Make sure interval doesn't overflow when we multiply by four below
3306 interval
= 0x10000000;
3308 // If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
3309 if (RRExpireTime(rr
) - m
->timenow
> (mDNSs32
)((interval
* 4) / 3))
3311 // Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
3312 interval
+= mDNSRandom(interval
/3);
3313 rr
->TimeRcvd
= m
->timenow
- (mDNSs32
)interval
* 3;
3314 rr
->resrec
.rroriginalttl
= interval
* 4 / mDNSPlatformOneSecond
;
3315 SetNextCacheCheckTime(m
, rr
);
3317 debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr
) - m
->timenow
, CRDisplayString(m
, rr
));
3318 return(mStatus_NoError
);
3321 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
3323 // BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
3324 // It also appends to the list of known answer records that need to be included,
3325 // and updates the forcast for the size of the known answer section.
3326 mDNSlocal mDNSBool
BuildQuestion(mDNS
*const m
, DNSMessage
*query
, mDNSu8
**queryptr
, DNSQuestion
*q
,
3327 CacheRecord
***kalistptrptr
, mDNSu32
*answerforecast
)
3329 mDNSBool ucast
= (q
->LargeAnswers
|| q
->RequestUnicast
) && m
->CanReceiveUnicastOn5353
;
3330 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
3331 const mDNSu8
*const limit
= query
->data
+ NormalMaxDNSMessageData
;
3332 mDNSu8
*newptr
= putQuestion(query
, *queryptr
, limit
, &q
->qname
, q
->qtype
, (mDNSu16
)(q
->qclass
| ucbit
));
3335 debugf("BuildQuestion: No more space in this packet for question %##s", q
->qname
.c
);
3338 else if (newptr
+ *answerforecast
>= limit
)
3340 verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q
->qname
.c
, newptr
+ *answerforecast
- query
->data
);
3341 query
->h
.numQuestions
--;
3346 mDNSu32 forecast
= *answerforecast
;
3347 const mDNSu32 slot
= HashSlot(&q
->qname
);
3348 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
3350 CacheRecord
**ka
= *kalistptrptr
; // Make a working copy of the pointer we're going to update
3352 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
3353 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
3354 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not already in the known answer list
3355 rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
3356 ResourceRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
3357 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
>= 0) // and it is less than half-way to expiry
3359 *ka
= rr
; // Link this record into our known answer chain
3360 ka
= &rr
->NextInKAList
;
3361 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3362 forecast
+= 12 + rr
->resrec
.rdestimate
;
3363 // If we're trying to put more than one question in this packet, and it doesn't fit
3364 // then undo that last question and try again next time
3365 if (query
->h
.numQuestions
> 1 && newptr
+ forecast
>= limit
)
3367 debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
3368 q
->qname
.c
, DNSTypeName(q
->qtype
), newptr
+ forecast
- query
->data
);
3369 query
->h
.numQuestions
--;
3370 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
3371 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
3372 return(mDNSfalse
); // Return false, so we'll try again in the next packet
3376 // Traffic reduction:
3377 // If we already have at least one unique answer in the cache,
3378 // OR we have so many shared answers that the KA list is too big to fit in one packet
3379 // The we suppress queries number 3 and 5:
3380 // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies)
3381 // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally)
3382 // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress)
3383 // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally)
3384 // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress)
3385 // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally)
3386 if (q
->UniqueAnswers
|| newptr
+ forecast
>= limit
)
3387 if (q
->ThisQInterval
== InitialQuestionInterval
* 8 || q
->ThisQInterval
== InitialQuestionInterval
* 32)
3389 query
->h
.numQuestions
--;
3390 ka
= *kalistptrptr
; // Go back to where we started and retract these answer records
3391 while (*ka
) { CacheRecord
*rr
= *ka
; *ka
= mDNSNULL
; ka
= &rr
->NextInKAList
; }
3392 return(mDNStrue
); // Return true: pretend we succeeded, even though we actually suppressed this question
3395 // Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
3396 *queryptr
= newptr
; // Update the packet pointer
3397 *answerforecast
= forecast
; // Update the forecast
3398 *kalistptrptr
= ka
; // Update the known answer list pointer
3399 if (ucast
) m
->ExpectUnicastResponse
= m
->timenow
;
3401 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // For every resource record in our cache,
3402 if (rr
->resrec
.InterfaceID
== q
->SendQNow
&& // received on this interface
3403 rr
->NextInKAList
== mDNSNULL
&& ka
!= &rr
->NextInKAList
&& // which is not in the known answer list
3404 ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) // which answers our question
3406 rr
->UnansweredQueries
++; // indicate that we're expecting a response
3407 rr
->LastUnansweredTime
= m
->timenow
;
3408 SetNextCacheCheckTime(m
, rr
);
3415 mDNSlocal
void ReconfirmAntecedents(mDNS
*const m
, DNSQuestion
*q
)
3421 FORALL_CACHERECORDS(slot
, cg
, rr
)
3422 if ((target
= GetRRDomainNameTarget(&rr
->resrec
)) && rr
->resrec
.rdatahash
== q
->qnamehash
&& SameDomainName(target
, &q
->qname
))
3423 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
3426 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
3427 mDNSlocal
void ExpireDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
)
3430 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
3433 mDNSlocal
void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 time
, mDNSInterfaceID InterfaceID
)
3436 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Time
- time
< 0) ds
[i
].InterfaceID
= mDNSNULL
;
3439 mDNSlocal mDNSBool
SuppressOnThisInterface(const DupSuppressInfo ds
[DupSuppressInfoSize
], const NetworkInterfaceInfo
* const intf
)
3442 mDNSBool v4
= !intf
->IPv4Available
; // If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
3443 mDNSBool v6
= !intf
->IPv6Available
; // If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
3444 for (i
=0; i
<DupSuppressInfoSize
; i
++)
3445 if (ds
[i
].InterfaceID
== intf
->InterfaceID
)
3447 if (ds
[i
].Type
== mDNSAddrType_IPv4
) v4
= mDNStrue
;
3448 else if (ds
[i
].Type
== mDNSAddrType_IPv6
) v6
= mDNStrue
;
3449 if (v4
&& v6
) return(mDNStrue
);
3454 mDNSlocal
int RecordDupSuppressInfo(DupSuppressInfo ds
[DupSuppressInfoSize
], mDNSs32 Time
, mDNSInterfaceID InterfaceID
, mDNSs32 Type
)
3458 // See if we have this one in our list somewhere already
3459 for (i
=0; i
<DupSuppressInfoSize
; i
++) if (ds
[i
].InterfaceID
== InterfaceID
&& ds
[i
].Type
== Type
) break;
3461 // If not, find a slot we can re-use
3462 if (i
>= DupSuppressInfoSize
)
3465 for (j
=1; j
<DupSuppressInfoSize
&& ds
[i
].InterfaceID
; j
++)
3466 if (!ds
[j
].InterfaceID
|| ds
[j
].Time
- ds
[i
].Time
< 0)
3470 // Record the info about this query we saw
3472 ds
[i
].InterfaceID
= InterfaceID
;
3478 mDNSlocal mDNSBool
AccelerateThisQuery(mDNS
*const m
, DNSQuestion
*q
)
3480 // If more than 90% of the way to the query time, we should unconditionally accelerate it
3481 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/10))
3484 // If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
3485 if (TimeToSendThisQuestion(q
, m
->timenow
+ q
->ThisQInterval
/2))
3487 // We forecast: qname (n) type (2) class (2)
3488 mDNSu32 forecast
= (mDNSu32
)DomainNameLength(&q
->qname
) + 4;
3489 const mDNSu32 slot
= HashSlot(&q
->qname
);
3490 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
3492 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
) // If we have a resource record in our cache,
3493 if (rr
->resrec
.rdlength
<= SmallRecordLimit
&& // which is small enough to sensibly fit in the packet
3494 ResourceRecordAnswersQuestion(&rr
->resrec
, q
) && // which answers our question
3495 rr
->TimeRcvd
+ TicksTTL(rr
)/2 - m
->timenow
>= 0 && // and it is less than half-way to expiry
3496 rr
->NextRequiredQuery
- (m
->timenow
+ q
->ThisQInterval
) > 0)// and we'll ask at least once again before NextRequiredQuery
3498 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3499 forecast
+= 12 + rr
->resrec
.rdestimate
;
3500 if (forecast
>= 512) return(mDNSfalse
); // If this would add 512 bytes or more to the packet, don't accelerate
3508 // How Standard Queries are generated:
3509 // 1. The Question Section contains the question
3510 // 2. The Additional Section contains answers we already know, to suppress duplicate responses
3512 // How Probe Queries are generated:
3513 // 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
3514 // if some other host is already using *any* records with this name, we want to know about it.
3515 // 2. The Authority Section contains the proposed values we intend to use for one or more
3516 // of our records with that name (analogous to the Update section of DNS Update packets)
3517 // because if some other host is probing at the same time, we each want to know what the other is
3518 // planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
3520 mDNSlocal
void SendQueries(mDNS
*const m
)
3524 // For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
3525 mDNSs32 maxExistingQuestionInterval
= 0;
3526 const NetworkInterfaceInfo
*intf
= GetFirstActiveInterface(m
->HostInterfaces
);
3527 CacheRecord
*KnownAnswerList
= mDNSNULL
;
3529 // 1. If time for a query, work out what we need to do
3530 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
3535 m
->NextScheduledQuery
= m
->timenow
+ 0x78000000;
3537 // We're expecting to send a query anyway, so see if any expiring cache records are close enough
3538 // to their NextRequiredQuery to be worth batching them together with this one
3539 FORALL_CACHERECORDS(slot
, cg
, rr
)
3540 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
3541 if (m
->timenow
+ TicksTTL(rr
)/50 - rr
->NextRequiredQuery
>= 0)
3543 q
= rr
->CRActiveQuestion
;
3544 ExpireDupSuppressInfoOnInterface(q
->DupSuppress
, m
->timenow
- TicksTTL(rr
)/20, rr
->resrec
.InterfaceID
);
3545 if (q
->Target
.type
) q
->SendQNow
= mDNSInterfaceMark
; // If unicast query, mark it
3546 else if (q
->SendQNow
== mDNSNULL
) q
->SendQNow
= rr
->resrec
.InterfaceID
;
3547 else if (q
->SendQNow
!= rr
->resrec
.InterfaceID
) q
->SendQNow
= mDNSInterfaceMark
;
3550 // Scan our list of questions to see which *unicast* queries need to be sent
3551 for (q
= m
->Questions
; q
; q
=q
->next
)
3552 if (q
->Target
.type
&& (q
->SendQNow
|| TimeToSendThisQuestion(q
, m
->timenow
)))
3554 mDNSu8
*qptr
= m
->omsg
.data
;
3555 const mDNSu8
*const limit
= m
->omsg
.data
+ sizeof(m
->omsg
.data
);
3556 InitializeDNSMessage(&m
->omsg
.h
, q
->TargetQID
, QueryFlags
);
3557 qptr
= putQuestion(&m
->omsg
, qptr
, limit
, &q
->qname
, q
->qtype
, q
->qclass
);
3558 mDNSSendDNSMessage(m
, &m
->omsg
, qptr
, mDNSInterface_Any
, &q
->Target
, q
->TargetPort
, -1, mDNSNULL
);
3559 q
->ThisQInterval
*= 2;
3560 if (q
->ThisQInterval
> MaxQuestionInterval
)
3561 q
->ThisQInterval
= MaxQuestionInterval
;
3562 q
->LastQTime
= m
->timenow
;
3563 q
->LastQTxTime
= m
->timenow
;
3564 q
->RecentAnswerPkts
= 0;
3565 q
->SendQNow
= mDNSNULL
;
3566 m
->ExpectUnicastResponse
= m
->timenow
;
3569 // Scan our list of questions to see which *multicast* queries we're definitely going to send
3570 for (q
= m
->Questions
; q
; q
=q
->next
)
3571 if (!q
->Target
.type
&& TimeToSendThisQuestion(q
, m
->timenow
))
3573 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
3574 if (maxExistingQuestionInterval
< q
->ThisQInterval
)
3575 maxExistingQuestionInterval
= q
->ThisQInterval
;
3578 // Scan our list of questions
3579 // (a) to see if there are any more that are worth accelerating, and
3580 // (b) to update the state variables for *all* the questions we're going to send
3581 for (q
= m
->Questions
; q
; q
=q
->next
)
3584 (!q
->Target
.type
&& ActiveQuestion(q
) && q
->ThisQInterval
<= maxExistingQuestionInterval
&& AccelerateThisQuery(m
,q
)))
3586 // If at least halfway to next query time, advance to next interval
3587 // If less than halfway to next query time, treat this as logically a repeat of the last transmission, without advancing the interval
3588 if (m
->timenow
- (q
->LastQTime
+ q
->ThisQInterval
/2) >= 0)
3590 q
->SendQNow
= mDNSInterfaceMark
; // Mark this question for sending on all interfaces
3591 q
->ThisQInterval
*= 2;
3592 if (q
->ThisQInterval
> MaxQuestionInterval
)
3593 q
->ThisQInterval
= MaxQuestionInterval
;
3594 else if (q
->CurrentAnswers
== 0 && q
->ThisQInterval
== InitialQuestionInterval
* 8)
3596 debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents", q
->qname
.c
, DNSTypeName(q
->qtype
));
3597 ReconfirmAntecedents(m
, q
); // If sending third query, and no answers yet, time to begin doubting the source
3601 // Mark for sending. (If no active interfaces, then don't even try.)
3602 q
->SendOnAll
= (q
->SendQNow
== mDNSInterfaceMark
);
3605 q
->SendQNow
= !intf
? mDNSNULL
: (q
->InterfaceID
) ? q
->InterfaceID
: intf
->InterfaceID
;
3606 q
->LastQTime
= m
->timenow
;
3609 // If we recorded a duplicate suppression for this question less than half an interval ago,
3610 // then we consider it recent enough that we don't need to do an identical query ourselves.
3611 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
- q
->ThisQInterval
/2);
3613 q
->LastQTxTime
= m
->timenow
;
3614 q
->RecentAnswerPkts
= 0;
3615 if (q
->RequestUnicast
) q
->RequestUnicast
--;
3617 // For all questions (not just the ones we're sending) check what the next scheduled event will be
3618 SetNextQueryTime(m
,q
);
3622 // 2. Scan our authoritative RR list to see what probes we might need to send
3623 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
3625 m
->NextScheduledProbe
= m
->timenow
+ 0x78000000;
3627 if (m
->CurrentRecord
) LogMsg("SendQueries: ERROR m->CurrentRecord already set");
3628 m
->CurrentRecord
= m
->ResourceRecords
;
3629 while (m
->CurrentRecord
)
3631 AuthRecord
*rr
= m
->CurrentRecord
;
3632 m
->CurrentRecord
= rr
->next
;
3633 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
) // For all records that are still probing...
3635 // 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
3636 if (m
->timenow
- (rr
->LastAPTime
+ rr
->ThisAPInterval
) < 0)
3638 SetNextAnnounceProbeTime(m
, rr
);
3640 // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
3641 else if (rr
->ProbeCount
)
3643 // Mark for sending. (If no active interfaces, then don't even try.)
3644 rr
->SendRNow
= !intf
? mDNSNULL
: (rr
->resrec
.InterfaceID
) ? rr
->resrec
.InterfaceID
: intf
->InterfaceID
;
3645 rr
->LastAPTime
= m
->timenow
;
3647 SetNextAnnounceProbeTime(m
, rr
);
3649 // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
3653 rr
->resrec
.RecordType
= kDNSRecordTypeVerified
;
3654 rr
->ThisAPInterval
= DefaultAnnounceIntervalForTypeUnique
;
3655 rr
->LastAPTime
= m
->timenow
- DefaultAnnounceIntervalForTypeUnique
;
3656 SetNextAnnounceProbeTime(m
, rr
);
3657 // If we have any records on our duplicate list that match this one, they have now also completed probing
3658 for (r2
= m
->DuplicateRecords
; r2
; r2
=r2
->next
)
3659 if (r2
->resrec
.RecordType
== kDNSRecordTypeUnique
&& RecordIsLocalDuplicate(r2
, rr
))
3661 AcknowledgeRecord(m
, rr
);
3665 m
->CurrentRecord
= m
->DuplicateRecords
;
3666 while (m
->CurrentRecord
)
3668 AuthRecord
*rr
= m
->CurrentRecord
;
3669 m
->CurrentRecord
= rr
->next
;
3670 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
&& rr
->ProbeCount
== 0)
3671 AcknowledgeRecord(m
, rr
);
3675 // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
3679 mDNSu8
*queryptr
= m
->omsg
.data
;
3680 InitializeDNSMessage(&m
->omsg
.h
, zeroID
, QueryFlags
);
3681 if (KnownAnswerList
) verbosedebugf("SendQueries: KnownAnswerList set... Will continue from previous packet");
3682 if (!KnownAnswerList
)
3684 // Start a new known-answer list
3685 CacheRecord
**kalistptr
= &KnownAnswerList
;
3686 mDNSu32 answerforecast
= 0;
3688 // Put query questions in this packet
3689 for (q
= m
->Questions
; q
; q
=q
->next
)
3690 if (q
->SendQNow
== intf
->InterfaceID
)
3692 debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
3693 SuppressOnThisInterface(q
->DupSuppress
, intf
) ? "Suppressing" : "Putting ",
3694 q
->qname
.c
, DNSTypeName(q
->qtype
), queryptr
- m
->omsg
.data
, queryptr
+ answerforecast
- m
->omsg
.data
);
3695 // If we're suppressing this question, or we successfully put it, update its SendQNow state
3696 if (SuppressOnThisInterface(q
->DupSuppress
, intf
) ||
3697 BuildQuestion(m
, &m
->omsg
, &queryptr
, q
, &kalistptr
, &answerforecast
))
3698 q
->SendQNow
= (q
->InterfaceID
|| !q
->SendOnAll
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
3701 // Put probe questions in this packet
3702 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3703 if (rr
->SendRNow
== intf
->InterfaceID
)
3705 mDNSBool ucast
= (rr
->ProbeCount
>= DefaultProbeCountForTypeUnique
-1) && m
->CanReceiveUnicastOn5353
;
3706 mDNSu16 ucbit
= (mDNSu16
)(ucast
? kDNSQClass_UnicastResponse
: 0);
3707 const mDNSu8
*const limit
= m
->omsg
.data
+ ((m
->omsg
.h
.numQuestions
) ? NormalMaxDNSMessageData
: AbsoluteMaxDNSMessageData
);
3708 mDNSu8
*newptr
= putQuestion(&m
->omsg
, queryptr
, limit
, rr
->resrec
.name
, kDNSQType_ANY
, (mDNSu16
)(rr
->resrec
.rrclass
| ucbit
));
3709 // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
3710 mDNSu32 forecast
= answerforecast
+ 12 + rr
->resrec
.rdestimate
;
3711 if (newptr
&& newptr
+ forecast
< limit
)
3714 answerforecast
= forecast
;
3715 rr
->SendRNow
= (rr
->resrec
.InterfaceID
) ? mDNSNULL
: GetNextActiveInterfaceID(intf
);
3716 rr
->IncludeInProbe
= mDNStrue
;
3717 verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->ProbeCount
);
3721 verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3722 m
->omsg
.h
.numQuestions
--;
3727 // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
3728 while (KnownAnswerList
)
3730 CacheRecord
*rr
= KnownAnswerList
;
3731 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
3732 mDNSu8
*newptr
= PutResourceRecordTTL(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAnswers
, &rr
->resrec
, rr
->resrec
.rroriginalttl
- SecsSinceRcvd
);
3735 verbosedebugf("SendQueries: Put %##s (%s) at %d - %d", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), queryptr
- m
->omsg
.data
, newptr
- m
->omsg
.data
);
3737 KnownAnswerList
= rr
->NextInKAList
;
3738 rr
->NextInKAList
= mDNSNULL
;
3742 // If we ran out of space and we have more than one question in the packet, that's an error --
3743 // we shouldn't have put more than one question if there was a risk of us running out of space.
3744 if (m
->omsg
.h
.numQuestions
> 1)
3745 LogMsg("SendQueries: Put %d answers; No more space for known answers", m
->omsg
.h
.numAnswers
);
3746 m
->omsg
.h
.flags
.b
[0] |= kDNSFlag0_TC
;
3751 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3752 if (rr
->IncludeInProbe
)
3754 mDNSu8
*newptr
= PutResourceRecord(&m
->omsg
, queryptr
, &m
->omsg
.h
.numAuthorities
, &rr
->resrec
);
3755 rr
->IncludeInProbe
= mDNSfalse
;
3756 if (newptr
) queryptr
= newptr
;
3757 else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?",
3758 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3761 if (queryptr
> m
->omsg
.data
)
3763 if ((m
->omsg
.h
.flags
.b
[0] & kDNSFlag0_TC
) && m
->omsg
.h
.numQuestions
> 1)
3764 LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m
->omsg
.h
.numQuestions
);
3765 debugf("SendQueries: Sending %d Question%s %d Answer%s %d Update%s on %p",
3766 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
3767 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
3768 m
->omsg
.h
.numAuthorities
, m
->omsg
.h
.numAuthorities
== 1 ? "" : "s", intf
->InterfaceID
);
3769 if (intf
->IPv4Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v4
, MulticastDNSPort
, -1, mDNSNULL
);
3770 if (intf
->IPv6Available
) mDNSSendDNSMessage(m
, &m
->omsg
, queryptr
, intf
->InterfaceID
, &AllDNSLinkGroup_v6
, MulticastDNSPort
, -1, mDNSNULL
);
3771 if (!m
->SuppressSending
) m
->SuppressSending
= NonZeroTime(m
->timenow
+ (mDNSPlatformOneSecond
+9)/10);
3772 if (++pktcount
>= 1000)
3773 { LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount
); break; }
3774 // There might be more records left in the known answer list, or more questions to send
3775 // on this interface, so go around one more time and try again.
3777 else // Nothing more to send on this interface; go to next
3779 const NetworkInterfaceInfo
*next
= GetFirstActiveInterface(intf
->next
);
3780 #if MDNS_DEBUGMSGS && 0
3781 const char *const msg
= next
? "SendQueries: Nothing more on %p; moving to %p" : "SendQueries: Nothing more on %p";
3782 debugf(msg
, intf
, next
);
3788 // Final sanity check for debugging purposes
3791 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
3794 if (rr
->resrec
.InterfaceID
!= mDNSInterface_LocalOnly
)
3795 LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m
, rr
));
3796 rr
->SendRNow
= mDNSNULL
;
3801 // ***************************************************************************
3802 #if COMPILER_LIKES_PRAGMA_MARK
3804 #pragma mark - RR List Management & Task Management
3807 // NOTE: AnswerQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
3808 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3809 mDNSlocal
void AnswerQuestionWithResourceRecord(mDNS
*const m
, DNSQuestion
*q
, CacheRecord
*rr
, mDNSBool AddRecord
)
3811 verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
3812 q
->CurrentAnswers
, AddRecord
? "Add" : "Rmv", rr
->resrec
.rroriginalttl
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3814 // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... mDNStrue)
3815 // may be called twice, once when the record is received, and again when it's time to notify local clients.
3816 // If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
3818 rr
->LastUsed
= m
->timenow
;
3819 if (ActiveQuestion(q
) && rr
->CRActiveQuestion
!= q
)
3821 if (!rr
->CRActiveQuestion
) m
->rrcache_active
++; // If not previously active, increment rrcache_active count
3822 rr
->CRActiveQuestion
= q
; // We know q is non-null
3823 SetNextCacheCheckTime(m
, rr
);
3827 // (a) a no-cache add, where we've already done at least one 'QM' query, or
3828 // (b) a normal add, where we have at least one unique-type answer,
3829 // then there's no need to keep polling the network.
3830 // (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
3831 if ((AddRecord
== 2 && !q
->RequestUnicast
) ||
3832 (AddRecord
== 1 && (q
->ExpectUnique
|| (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))))
3833 if (ActiveQuestion(q
))
3835 q
->LastQTime
= m
->timenow
;
3836 q
->LastQTxTime
= m
->timenow
;
3837 q
->RecentAnswerPkts
= 0;
3838 q
->ThisQInterval
= MaxQuestionInterval
;
3839 q
->RequestUnicast
= mDNSfalse
;
3842 if (rr
->DelayDelivery
) return; // We'll come back later when CacheRecordDeferredAdd() calls us
3844 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
3845 if (q
->QuestionCallback
)
3846 q
->QuestionCallback(m
, q
, &rr
->resrec
, AddRecord
);
3847 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
3848 // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function
3849 // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
3850 // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv()
3851 // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions
3852 // being deleted out from under them.
3855 mDNSlocal
void CacheRecordDeferredAdd(mDNS
*const m
, CacheRecord
*rr
)
3857 rr
->DelayDelivery
= 0;
3858 if (m
->CurrentQuestion
) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set");
3859 m
->CurrentQuestion
= m
->Questions
;
3860 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3862 DNSQuestion
*q
= m
->CurrentQuestion
;
3863 m
->CurrentQuestion
= q
->next
;
3864 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3865 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3867 m
->CurrentQuestion
= mDNSNULL
;
3870 mDNSlocal mDNSs32
CheckForSoonToExpireRecords(mDNS
*const m
, const domainname
*const name
, const mDNSu32 namehash
, const mDNSu32 slot
)
3872 const mDNSs32 threshhold
= m
->timenow
+ mDNSPlatformOneSecond
; // See if there are any records expiring within one second
3873 const mDNSs32 start
= m
->timenow
- 0x10000000;
3874 mDNSs32 delay
= start
;
3875 CacheGroup
*cg
= CacheGroupForName(m
, slot
, namehash
, name
);
3877 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
3878 if (rr
->resrec
.namehash
== namehash
&& SameDomainName(rr
->resrec
.name
, name
))
3879 if (threshhold
- RRExpireTime(rr
) >= 0) // If we have records about to expire within a second
3880 if (delay
- RRExpireTime(rr
) < 0) // then delay until after they've been deleted
3881 delay
= RRExpireTime(rr
);
3882 if (delay
- start
> 0) return(delay
? delay
: 1); // Make sure we return non-zero if we want to delay
3886 // CacheRecordAdd is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
3887 // If new questions are created as a result of invoking client callbacks, they will be added to
3888 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3889 // rr is a new CacheRecord just received into our cache
3890 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
3891 // NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord which can call a user callback,
3892 // which may change the record list and/or question list.
3893 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3894 mDNSlocal
void CacheRecordAdd(mDNS
*const m
, CacheRecord
*rr
)
3896 if (m
->CurrentQuestion
) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3897 m
->CurrentQuestion
= m
->Questions
;
3898 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3900 DNSQuestion
*q
= m
->CurrentQuestion
;
3901 m
->CurrentQuestion
= q
->next
;
3902 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3904 // If this question is one that's actively sending queries, and it's received ten answers within one second of sending the last
3905 // query packet, then that indicates some radical network topology change, so reset its exponential backoff back to the start.
3906 // We must be at least at the eight-second interval to do this. If we're at the four-second interval, or less,
3907 // there's not much benefit accelerating because we will anyway send another query within a few seconds.
3908 // The first reset query is sent out randomized over the next four seconds to reduce possible synchronization between machines.
3909 if (q
->LastAnswerPktNum
!= m
->PktNum
)
3911 q
->LastAnswerPktNum
= m
->PktNum
;
3912 if (ActiveQuestion(q
) && ++q
->RecentAnswerPkts
>= 10 &&
3913 q
->ThisQInterval
> InitialQuestionInterval
*32 && m
->timenow
- q
->LastQTxTime
< mDNSPlatformOneSecond
)
3915 LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
3916 q
->qname
.c
, DNSTypeName(q
->qtype
));
3917 q
->LastQTime
= m
->timenow
- InitialQuestionInterval
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*4);
3918 q
->ThisQInterval
= InitialQuestionInterval
;
3919 SetNextQueryTime(m
,q
);
3922 verbosedebugf("CacheRecordAdd %p %##s (%s) %lu", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), rr
->resrec
.rroriginalttl
);
3923 q
->CurrentAnswers
++;
3924 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
3925 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
3926 if (q
->CurrentAnswers
> 4000)
3928 static int msgcount
= 0;
3929 if (msgcount
++ < 10)
3930 LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
3931 q
->qname
.c
, DNSTypeName(q
->qtype
), q
->CurrentAnswers
);
3932 rr
->resrec
.rroriginalttl
= 1;
3933 rr
->UnansweredQueries
= MaxUnansweredQueries
;
3935 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
3936 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3939 m
->CurrentQuestion
= mDNSNULL
;
3942 // NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
3943 // If new questions are created as a result of invoking client callbacks, they will be added to
3944 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3945 // rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
3946 // but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
3947 // way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
3948 // so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
3949 // NOTE: NoCacheAnswer calls AnswerQuestionWithResourceRecord which can call a user callback,
3950 // which may change the record list and/or question list.
3951 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3952 mDNSlocal
void NoCacheAnswer(mDNS
*const m
, CacheRecord
*rr
)
3954 LogMsg("No cache space: Delivering non-cached result for %##s", m
->rec
.r
.resrec
.name
->c
);
3955 if (m
->CurrentQuestion
) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3956 m
->CurrentQuestion
= m
->Questions
;
3957 while (m
->CurrentQuestion
)
3959 DNSQuestion
*q
= m
->CurrentQuestion
;
3960 m
->CurrentQuestion
= q
->next
;
3961 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3962 AnswerQuestionWithResourceRecord(m
, q
, rr
, 2); // Value '2' indicates "don't expect 'remove' events for this"
3963 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3965 m
->CurrentQuestion
= mDNSNULL
;
3968 // CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
3969 // If new questions are created as a result of invoking client callbacks, they will be added to
3970 // the end of the question list, and m->NewQuestions will be set to indicate the first new question.
3971 // rr is an existing cache CacheRecord that just expired and is being deleted
3972 // (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
3973 // NOTE: CacheRecordRmv calls AnswerQuestionWithResourceRecord which can call a user callback,
3974 // which may change the record list and/or question list.
3975 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
3976 mDNSlocal
void CacheRecordRmv(mDNS
*const m
, CacheRecord
*rr
)
3978 if (m
->CurrentQuestion
) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set");
3979 m
->CurrentQuestion
= m
->Questions
;
3980 while (m
->CurrentQuestion
&& m
->CurrentQuestion
!= m
->NewQuestions
)
3982 DNSQuestion
*q
= m
->CurrentQuestion
;
3983 m
->CurrentQuestion
= q
->next
;
3984 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
3986 verbosedebugf("CacheRecordRmv %p %##s (%s)", rr
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
3987 if (q
->CurrentAnswers
== 0)
3988 LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?", q
, q
->qname
.c
, DNSTypeName(q
->qtype
));
3991 q
->CurrentAnswers
--;
3992 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
--;
3993 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
--;
3995 if (q
->CurrentAnswers
== 0)
3997 debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q
->qname
.c
, DNSTypeName(q
->qtype
));
3998 ReconfirmAntecedents(m
, q
);
4000 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNSfalse
);
4001 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4004 m
->CurrentQuestion
= mDNSNULL
;
4007 mDNSlocal
void ReleaseCacheEntity(mDNS
*const m
, CacheEntity
*e
)
4009 #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
4011 for (i
=0; i
<sizeof(*e
); i
++) ((char*)e
)[i
] = 0xFF;
4013 e
->next
= m
->rrcache_free
;
4014 m
->rrcache_free
= e
;
4015 m
->rrcache_totalused
--;
4018 mDNSlocal
void ReleaseCacheGroup(mDNS
*const m
, CacheGroup
**cp
)
4020 CacheEntity
*e
= (CacheEntity
*)(*cp
);
4021 //LogMsg("ReleaseCacheGroup: Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
4022 if ((*cp
)->rrcache_tail
!= &(*cp
)->members
) LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
4023 //if ((*cp)->name != (domainname*)((*cp)->namestorage)) LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
4024 if ((*cp
)->name
!= (domainname
*)((*cp
)->namestorage
)) mDNSPlatformMemFree((*cp
)->name
);
4025 (*cp
)->name
= mDNSNULL
;
4026 *cp
= (*cp
)->next
; // Cut record from list
4027 ReleaseCacheEntity(m
, e
);
4030 mDNSlocal
void ReleaseCacheRecord(mDNS
*const m
, CacheRecord
*r
)
4032 if (r
->resrec
.rdata
&& r
->resrec
.rdata
!= (RData
*)&r
->rdatastorage
) mDNSPlatformMemFree(r
->resrec
.rdata
);
4033 r
->resrec
.rdata
= mDNSNULL
;
4034 ReleaseCacheEntity(m
, (CacheEntity
*)r
);
4037 // Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering CacheRecordDeferredAdd calls
4038 // The in-order nature of the cache lists ensures that all callbacks for old records are delivered before callbacks for newer records
4039 mDNSlocal
void CheckCacheExpiration(mDNS
*const m
, CacheGroup
*cg
)
4041 CacheRecord
**rp
= &cg
->members
;
4043 if (m
->lock_rrcache
) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
4044 m
->lock_rrcache
= 1;
4048 CacheRecord
*const rr
= *rp
;
4049 mDNSs32 event
= RRExpireTime(rr
);
4050 if (m
->timenow
- event
>= 0) // If expired, delete it
4052 *rp
= rr
->next
; // Cut it from the list
4053 verbosedebugf("CheckCacheExpiration: Deleting %s", CRDisplayString(m
, rr
));
4054 if (rr
->CRActiveQuestion
) // If this record has one or more active questions, tell them it's going away
4056 CacheRecordRmv(m
, rr
);
4057 m
->rrcache_active
--;
4059 ReleaseCacheRecord(m
, rr
);
4061 else // else, not expired; see if we need to query
4063 if (rr
->DelayDelivery
&& rr
->DelayDelivery
- m
->timenow
> 0)
4064 event
= rr
->DelayDelivery
;
4067 if (rr
->DelayDelivery
) CacheRecordDeferredAdd(m
, rr
);
4068 if (rr
->CRActiveQuestion
&& rr
->UnansweredQueries
< MaxUnansweredQueries
)
4070 if (m
->timenow
- rr
->NextRequiredQuery
< 0) // If not yet time for next query
4071 event
= rr
->NextRequiredQuery
; // then just record when we want the next query
4072 else // else trigger our question to go out now
4074 // Set NextScheduledQuery to timenow so that SendQueries() will run.
4075 // SendQueries() will see that we have records close to expiration, and send FEQs for them.
4076 m
->NextScheduledQuery
= m
->timenow
;
4077 // After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTime(),
4078 // which will correctly update m->NextCacheCheck for us
4079 event
= m
->timenow
+ 0x3FFFFFFF;
4083 if (m
->NextCacheCheck
- (event
+ CacheCheckGracePeriod(rr
)) > 0)
4084 m
->NextCacheCheck
= (event
+ CacheCheckGracePeriod(rr
));
4088 if (cg
->rrcache_tail
!= rp
) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg
->rrcache_tail
, rp
);
4089 cg
->rrcache_tail
= rp
;
4090 m
->lock_rrcache
= 0;
4093 mDNSlocal
void AnswerNewQuestion(mDNS
*const m
)
4095 mDNSBool ShouldQueryImmediately
= mDNStrue
;
4097 DNSQuestion
*q
= m
->NewQuestions
; // Grab the question we're going to answer
4098 const mDNSu32 slot
= HashSlot(&q
->qname
);
4099 CacheGroup
*cg
= CacheGroupForName(m
, slot
, q
->qnamehash
, &q
->qname
);
4101 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4103 if (cg
) CheckCacheExpiration(m
, cg
);
4104 m
->NewQuestions
= q
->next
; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
4106 if (m
->lock_rrcache
) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
4107 // This should be safe, because calling the client's question callback may cause the
4108 // question list to be modified, but should not ever cause the rrcache list to be modified.
4109 // If the client's question callback deletes the question, then m->CurrentQuestion will
4110 // be advanced, and we'll exit out of the loop
4111 m
->lock_rrcache
= 1;
4112 if (m
->CurrentQuestion
) LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set");
4113 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
4115 if (q
->InterfaceID
== mDNSInterface_Any
) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
4117 if (m
->CurrentRecord
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4118 m
->CurrentRecord
= m
->ResourceRecords
;
4119 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
4121 AuthRecord
*rr
= m
->CurrentRecord
;
4122 m
->CurrentRecord
= rr
->next
;
4123 if (rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
)
4124 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4126 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4127 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4128 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4131 m
->CurrentRecord
= mDNSNULL
;
4134 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4135 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4137 // SecsSinceRcvd is whole number of elapsed seconds, rounded down
4138 mDNSu32 SecsSinceRcvd
= ((mDNSu32
)(m
->timenow
- rr
->TimeRcvd
)) / mDNSPlatformOneSecond
;
4139 if (rr
->resrec
.rroriginalttl
<= SecsSinceRcvd
)
4141 LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %##s (%s)",
4142 rr
->resrec
.rroriginalttl
, SecsSinceRcvd
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
4143 continue; // Go to next one in loop
4146 // If this record set is marked unique, then that means we can reasonably assume we have the whole set
4147 // -- we don't need to rush out on the network and query immediately to see if there are more answers out there
4148 if ((rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) || (q
->ExpectUnique
))
4149 ShouldQueryImmediately
= mDNSfalse
;
4150 q
->CurrentAnswers
++;
4151 if (rr
->resrec
.rdlength
> SmallRecordLimit
) q
->LargeAnswers
++;
4152 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) q
->UniqueAnswers
++;
4153 AnswerQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4154 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4155 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4157 else if (RRTypeIsAddressType(rr
->resrec
.rrtype
) && RRTypeIsAddressType(q
->qtype
))
4158 if (rr
->resrec
.namehash
== q
->qnamehash
&& SameDomainName(rr
->resrec
.name
, &q
->qname
))
4159 ShouldQueryImmediately
= mDNSfalse
;
4161 if (ShouldQueryImmediately
&& m
->CurrentQuestion
== q
)
4163 q
->ThisQInterval
= InitialQuestionInterval
;
4164 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
4165 m
->NextScheduledQuery
= m
->timenow
;
4167 m
->CurrentQuestion
= mDNSNULL
;
4168 m
->lock_rrcache
= 0;
4171 // When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any appropriate answers,
4172 // stopping if it reaches a NewLocalRecord -- these will be handled by AnswerLocalQuestions
4173 mDNSlocal
void AnswerNewLocalOnlyQuestion(mDNS
*const m
)
4175 DNSQuestion
*q
= m
->NewLocalOnlyQuestions
; // Grab the question we're going to answer
4176 m
->NewLocalOnlyQuestions
= q
->next
; // Advance NewQuestions to the next (if any)
4178 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q
->qname
.c
, DNSTypeName(q
->qtype
));
4180 if (m
->CurrentQuestion
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set");
4181 m
->CurrentQuestion
= q
; // Indicate which question we're answering, so we'll know if it gets deleted
4183 if (m
->CurrentRecord
) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4184 m
->CurrentRecord
= m
->ResourceRecords
;
4185 while (m
->CurrentRecord
&& m
->CurrentRecord
!= m
->NewLocalRecords
)
4187 AuthRecord
*rr
= m
->CurrentRecord
;
4188 m
->CurrentRecord
= rr
->next
;
4189 if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
4191 AnswerLocalOnlyQuestionWithResourceRecord(m
, q
, rr
, mDNStrue
);
4192 // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
4193 if (m
->CurrentQuestion
!= q
) break; // If callback deleted q, then we're finished here
4197 m
->CurrentQuestion
= mDNSNULL
;
4198 m
->CurrentRecord
= mDNSNULL
;
4201 mDNSlocal CacheEntity
*GetCacheEntity(mDNS
*const m
, const CacheGroup
*const PreserveCG
)
4203 CacheEntity
*e
= mDNSNULL
;
4205 if (m
->lock_rrcache
) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL
); }
4206 m
->lock_rrcache
= 1;
4208 // If we have no free records, ask the client layer to give us some more memory
4209 if (!m
->rrcache_free
&& m
->MainCallback
)
4211 if (m
->rrcache_totalused
!= m
->rrcache_size
)
4212 LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
4213 m
->rrcache_totalused
, m
->rrcache_size
);
4215 // We don't want to be vulnerable to a malicious attacker flooding us with an infinite
4216 // number of bogus records so that we keep growing our cache until the machine runs out of memory.
4217 // To guard against this, if we're actively using less than 1/32 of our cache, then we
4218 // purge all the unused records and recycle them, instead of allocating more memory.
4219 if (m
->rrcache_size
>= 512 && m
->rrcache_size
/ 32 > m
->rrcache_active
)
4220 debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
4221 m
->rrcache_size
, m
->rrcache_active
);
4224 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
4225 m
->MainCallback(m
, mStatus_GrowCache
);
4226 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
4230 // If we still have no free records, recycle all the records we can.
4231 // Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
4232 if (!m
->rrcache_free
)
4235 mDNSu32 oldtotalused
= m
->rrcache_totalused
;
4238 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
4240 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
4243 CacheRecord
**rp
= &(*cp
)->members
;
4246 // Records that answer still-active questions are not candidates for recycling
4247 // Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
4248 if ((*rp
)->CRActiveQuestion
|| (*rp
)->NextInCFList
)
4252 CacheRecord
*rr
= *rp
;
4253 *rp
= (*rp
)->next
; // Cut record from list
4254 ReleaseCacheRecord(m
, rr
);
4257 if ((*cp
)->rrcache_tail
!= rp
) verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot
, (*cp
)->rrcache_tail
, rp
);
4258 (*cp
)->rrcache_tail
= rp
;
4259 if ((*cp
)->members
|| (*cp
)==PreserveCG
) cp
=&(*cp
)->next
;
4260 else ReleaseCacheGroup(m
, cp
);
4264 debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused
, m
->rrcache_totalused
);
4268 if (m
->rrcache_free
) // If there are records in the free list, take one
4270 e
= m
->rrcache_free
;
4271 m
->rrcache_free
= e
->next
;
4272 if (++m
->rrcache_totalused
>= m
->rrcache_report
)
4274 debugf("RR Cache now using %ld objects", m
->rrcache_totalused
);
4275 if (m
->rrcache_report
< 100) m
->rrcache_report
+= 10;
4276 else m
->rrcache_report
+= 100;
4278 mDNSPlatformMemZero(e
, sizeof(*e
));
4281 m
->lock_rrcache
= 0;
4286 mDNSlocal CacheRecord
*GetCacheRecord(mDNS
*const m
, CacheGroup
*cg
, mDNSu16 RDLength
)
4288 CacheRecord
*r
= (CacheRecord
*)GetCacheEntity(m
, cg
);
4291 r
->resrec
.rdata
= (RData
*)&r
->rdatastorage
; // By default, assume we're usually going to be using local storage
4292 if (RDLength
> InlineCacheRDSize
) // If RDLength is too big, allocate extra storage
4294 r
->resrec
.rdata
= (RData
*)mDNSPlatformMemAllocate(sizeofRDataHeader
+ RDLength
);
4295 if (r
->resrec
.rdata
) r
->resrec
.rdata
->MaxRDLength
= r
->resrec
.rdlength
= RDLength
;
4296 else { ReleaseCacheEntity(m
, (CacheEntity
*)r
); r
= mDNSNULL
; }
4302 mDNSlocal CacheGroup
*GetCacheGroup(mDNS
*const m
, const mDNSu32 slot
, const ResourceRecord
*const rr
)
4304 mDNSu16 namelen
= DomainNameLength(rr
->name
);
4305 CacheGroup
*cg
= (CacheGroup
*)GetCacheEntity(m
, mDNSNULL
);
4306 if (!cg
) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr
->name
->c
); return(mDNSNULL
); }
4307 cg
->next
= m
->rrcache_hash
[slot
];
4308 cg
->namehash
= rr
->namehash
;
4309 cg
->members
= mDNSNULL
;
4310 cg
->rrcache_tail
= &cg
->members
;
4311 cg
->name
= (domainname
*)cg
->namestorage
;
4312 //LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s", (namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
4313 if (namelen
> InlineCacheGroupNameSize
) cg
->name
= mDNSPlatformMemAllocate(namelen
);
4316 LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr
->name
->c
);
4317 ReleaseCacheEntity(m
, (CacheEntity
*)cg
);
4320 AssignDomainName(cg
->name
, rr
->name
);
4322 if (CacheGroupForRecord(m
, slot
, rr
)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr
->name
->c
);
4323 m
->rrcache_hash
[slot
] = cg
;
4324 if (CacheGroupForRecord(m
, slot
, rr
) != cg
) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr
->name
->c
);
4329 mDNSlocal
void PurgeCacheResourceRecord(mDNS
*const m
, CacheRecord
*rr
)
4331 // Make sure we mark this record as thoroughly expired -- we don't ever want to give
4332 // a positive answer using an expired record (e.g. from an interface that has gone away).
4333 // We don't want to clear CRActiveQuestion here, because that would leave the record subject to
4334 // summary deletion without giving the proper callback to any questions that are monitoring it.
4335 // By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
4336 rr
->TimeRcvd
= m
->timenow
- mDNSPlatformOneSecond
* 60;
4337 rr
->UnansweredQueries
= MaxUnansweredQueries
;
4338 rr
->resrec
.rroriginalttl
= 0;
4339 SetNextCacheCheckTime(m
, rr
);
4342 mDNSexport mDNSs32
mDNS_TimeNow(const mDNS
*const m
)
4345 mDNSPlatformLock(m
);
4348 LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
4349 if (!m
->timenow
) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m
->mDNS_busy
);
4352 if (m
->timenow
) time
= m
->timenow
;
4353 else time
= mDNSPlatformRawTime() + m
->timenow_adjust
;
4354 mDNSPlatformUnlock(m
);
4358 mDNSexport mDNSs32
mDNS_Execute(mDNS
*const m
)
4360 mDNS_Lock(m
); // Must grab lock before trying to read m->timenow
4362 if (m
->timenow
- m
->NextScheduledEvent
>= 0)
4366 verbosedebugf("mDNS_Execute");
4367 if (m
->CurrentQuestion
) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
4369 // 1. If we're past the probe suppression time, we can clear it
4370 if (m
->SuppressProbes
&& m
->timenow
- m
->SuppressProbes
>= 0) m
->SuppressProbes
= 0;
4372 // 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
4373 if (m
->NumFailedProbes
&& m
->timenow
- m
->ProbeFailTime
>= mDNSPlatformOneSecond
* 10) m
->NumFailedProbes
= 0;
4375 // 3. Purge our cache of stale old records
4376 if (m
->rrcache_size
&& m
->timenow
- m
->NextCacheCheck
>= 0)
4379 m
->NextCacheCheck
= m
->timenow
+ 0x3FFFFFFF;
4380 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
4382 CacheGroup
**cp
= &m
->rrcache_hash
[slot
];
4385 CheckCacheExpiration(m
, *cp
);
4386 if ((*cp
)->members
) cp
=&(*cp
)->next
;
4387 else ReleaseCacheGroup(m
, cp
);
4392 // 4. See if we can answer any of our new local questions from the cache
4393 for (i
=0; m
->NewQuestions
&& i
<1000; i
++)
4395 if (m
->NewQuestions
->DelayAnswering
&& m
->timenow
- m
->NewQuestions
->DelayAnswering
< 0) break;
4396 AnswerNewQuestion(m
);
4398 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
4400 for (i
=0; m
->NewLocalOnlyQuestions
&& i
<1000; i
++) AnswerNewLocalOnlyQuestion(m
);
4401 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4403 for (i
=0; i
<1000 && m
->NewLocalRecords
&& LocalRecordReady(m
->NewLocalRecords
); i
++)
4405 AuthRecord
*rr
= m
->NewLocalRecords
;
4406 m
->NewLocalRecords
= m
->NewLocalRecords
->next
;
4407 AnswerLocalQuestions(m
, rr
, mDNStrue
);
4409 if (i
>= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
4411 // 5. See what packets we need to send
4412 if (m
->mDNSPlatformStatus
!= mStatus_NoError
|| m
->SleepState
) DiscardDeregistrations(m
);
4413 else if (m
->SuppressSending
== 0 || m
->timenow
- m
->SuppressSending
>= 0)
4415 // If the platform code is ready, and we're not suppressing packet generation right now
4416 // then send our responses, probes, and questions.
4417 // We check the cache first, because there might be records close to expiring that trigger questions to refresh them
4418 // We send queries next, because there might be final-stage probes that complete their probing here, causing
4419 // them to advance to announcing state, and we want those to be included in any announcements we send out.
4420 // Finally, we send responses, including the previously mentioned records that just completed probing
4421 m
->SuppressSending
= 0;
4423 // 6. Send Query packets. This may cause some probing records to advance to announcing state
4424 if (m
->timenow
- m
->NextScheduledQuery
>= 0 || m
->timenow
- m
->NextScheduledProbe
>= 0) SendQueries(m
);
4425 if (m
->timenow
- m
->NextScheduledQuery
>= 0)
4427 LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
4428 m
->NextScheduledQuery
= m
->timenow
+ mDNSPlatformOneSecond
;
4430 if (m
->timenow
- m
->NextScheduledProbe
>= 0)
4432 LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
4433 m
->NextScheduledProbe
= m
->timenow
+ mDNSPlatformOneSecond
;
4436 // 7. Send Response packets, including probing records just advanced to announcing state
4437 if (m
->timenow
- m
->NextScheduledResponse
>= 0) SendResponses(m
);
4438 if (m
->timenow
- m
->NextScheduledResponse
>= 0)
4440 LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
4441 m
->NextScheduledResponse
= m
->timenow
+ mDNSPlatformOneSecond
;
4445 m
->RandomQueryDelay
= 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
4448 // Note about multi-threaded systems:
4449 // On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
4450 // performing mDNS API operations that change our next scheduled event time.
4452 // On multi-threaded systems (like the current Windows implementation) that have a single main thread
4453 // calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
4454 // of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
4455 // signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
4456 // more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
4457 // in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
4458 // does, the state of the signal will be noticed, causing the blocking primitive to return immediately
4459 // without blocking. This avoids the race condition between the signal from the other thread arriving
4460 // just *before* or just *after* the main thread enters the blocking primitive.
4462 // On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
4463 // with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
4464 // set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
4465 // callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
4466 // by the time it gets to the timer callback function).
4468 #ifndef UNICAST_DISABLED
4471 mDNS_Unlock(m
); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4472 return(m
->NextScheduledEvent
);
4475 // Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
4476 // Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
4477 // Normally, the platform support layer below mDNSCore should call this, not the client layer above.
4478 // Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
4479 // mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
4480 // found itself in a new network environment. For example, if the Ethernet hardware indicates that the
4481 // cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
4482 // to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
4483 // While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
4484 // traffic, so it should only be called when there is legitimate reason to believe the machine
4485 // may have become attached to a new network.
4486 mDNSexport
void mDNSCoreMachineSleep(mDNS
*const m
, mDNSBool sleepstate
)
4492 m
->SleepState
= sleepstate
;
4493 LogOperation("%s at %ld", sleepstate
? "Sleeping" : "Waking", m
->timenow
);
4497 #ifndef UNICAST_DISABLED
4500 // Mark all the records we need to deregister and send them
4501 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4502 if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
&& rr
->RequireGoodbye
)
4503 rr
->ImmedAnswer
= mDNSInterfaceMark
;
4513 #ifndef UNICAST_DISABLED
4516 // 1. Retrigger all our questions
4517 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
4518 if (ActiveQuestion(q
))
4520 q
->ThisQInterval
= InitialQuestionInterval
; // MUST be > zero for an active question
4521 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
4522 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
;
4523 q
->RecentAnswerPkts
= 0;
4524 ExpireDupSuppressInfo(q
->DupSuppress
, m
->timenow
);
4525 m
->NextScheduledQuery
= m
->timenow
;
4528 // 2. Re-validate our cache records
4529 m
->NextCacheCheck
= m
->timenow
;
4530 FORALL_CACHERECORDS(slot
, cg
, cr
)
4531 mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForCableDisconnect
);
4533 // 3. Retrigger probing and announcing for all our authoritative records
4534 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4536 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
4537 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
4538 rr
->AnnounceCount
= InitialAnnounceCount
;
4539 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
4540 InitializeLastAPTime(m
, rr
);
4547 // ***************************************************************************
4548 #if COMPILER_LIKES_PRAGMA_MARK
4550 #pragma mark - Packet Reception Functions
4553 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4555 mDNSlocal mDNSu8
*GenerateUnicastResponse(const DNSMessage
*const query
, const mDNSu8
*const end
,
4556 const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, DNSMessage
*const response
, AuthRecord
*ResponseRecords
)
4558 mDNSu8
*responseptr
= response
->data
;
4559 const mDNSu8
*const limit
= response
->data
+ sizeof(response
->data
);
4560 const mDNSu8
*ptr
= query
->data
;
4562 mDNSu32 maxttl
= 0x70000000;
4565 // Initialize the response fields so we can answer the questions
4566 InitializeDNSMessage(&response
->h
, query
->h
.id
, ResponseFlags
);
4569 // *** 1. Write out the list of questions we are actually going to answer with this packet
4574 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
4577 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &q
); // get the question...
4578 if (!ptr
) return(mDNSNULL
);
4580 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
) // and search our list of proposed answers
4582 if (rr
->NR_AnswerTo
== ptr
) // If we're going to generate a record answering this question
4583 { // then put the question in the question section
4584 responseptr
= putQuestion(response
, responseptr
, limit
, &q
.qname
, q
.qtype
, q
.qclass
);
4585 if (!responseptr
) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL
); }
4586 break; // break out of the ResponseRecords loop, and go on to the next question
4591 if (response
->h
.numQuestions
== 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL
); }
4595 // *** 2. Write Answers
4597 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4598 if (rr
->NR_AnswerTo
)
4600 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAnswers
, &rr
->resrec
, maxttl
);
4601 if (p
) responseptr
= p
;
4602 else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response
->h
.flags
.b
[0] |= kDNSFlag0_TC
; }
4606 // *** 3. Write Additionals
4608 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4609 if (rr
->NR_AdditionalTo
&& !rr
->NR_AnswerTo
)
4611 mDNSu8
*p
= PutResourceRecordCappedTTL(response
, responseptr
, &response
->h
.numAdditionals
, &rr
->resrec
, maxttl
);
4612 if (p
) responseptr
= p
;
4613 else debugf("GenerateUnicastResponse: No more space for additionals");
4616 return(responseptr
);
4619 // AuthRecord *our is our Resource Record
4620 // CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
4621 // Returns 0 if there is no conflict
4622 // Returns +1 if there was a conflict and we won
4623 // Returns -1 if there was a conflict and we lost and have to rename
4624 mDNSlocal
int CompareRData(AuthRecord
*our
, CacheRecord
*pkt
)
4626 mDNSu8 ourdata
[256], *ourptr
= ourdata
, *ourend
;
4627 mDNSu8 pktdata
[256], *pktptr
= pktdata
, *pktend
;
4628 if (!our
) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
4629 if (!pkt
) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
4631 ourend
= putRData(mDNSNULL
, ourdata
, ourdata
+ sizeof(ourdata
), &our
->resrec
);
4632 pktend
= putRData(mDNSNULL
, pktdata
, pktdata
+ sizeof(pktdata
), &pkt
->resrec
);
4633 while (ourptr
< ourend
&& pktptr
< pktend
&& *ourptr
== *pktptr
) { ourptr
++; pktptr
++; }
4634 if (ourptr
>= ourend
&& pktptr
>= pktend
) return(0); // If data identical, not a conflict
4636 if (ourptr
>= ourend
) return(-1); // Our data ran out first; We lost
4637 if (pktptr
>= pktend
) return(+1); // Packet data ran out first; We won
4638 if (*pktptr
> *ourptr
) return(-1); // Our data is numerically lower; We lost
4639 if (*pktptr
< *ourptr
) return(+1); // Packet data is numerically lower; We won
4641 debugf("CompareRData: How did we get here?");
4645 // See if we have an authoritative record that's identical to this packet record,
4646 // whose canonical DependentOn record is the specified master record.
4647 // The DependentOn pointer is typically used for the TXT record of service registrations
4648 // It indicates that there is no inherent conflict detection for the TXT record
4649 // -- it depends on the SRV record to resolve name conflicts
4650 // If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
4651 // pointer chain (if any) to make sure we reach the canonical DependentOn record
4652 // If the record has no DependentOn, then just return that record's pointer
4653 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4654 mDNSlocal mDNSBool
MatchDependentOn(const mDNS
*const m
, const CacheRecord
*const pktrr
, const AuthRecord
*const master
)
4656 const AuthRecord
*r1
;
4657 for (r1
= m
->ResourceRecords
; r1
; r1
=r1
->next
)
4659 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
4661 const AuthRecord
*r2
= r1
;
4662 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
4663 if (r2
== master
) return(mDNStrue
);
4666 for (r1
= m
->DuplicateRecords
; r1
; r1
=r1
->next
)
4668 if (IdenticalResourceRecord(&r1
->resrec
, &pktrr
->resrec
))
4670 const AuthRecord
*r2
= r1
;
4671 while (r2
->DependentOn
) r2
= r2
->DependentOn
;
4672 if (r2
== master
) return(mDNStrue
);
4678 // Find the canonical RRSet pointer for this RR received in a packet.
4679 // If we find any identical AuthRecord in our authoritative list, then follow its RRSet
4680 // pointers (if any) to make sure we return the canonical member of this name/type/class
4681 // Returns NULL if we don't have any local RRs that are identical to the one from the packet
4682 mDNSlocal
const AuthRecord
*FindRRSet(const mDNS
*const m
, const CacheRecord
*const pktrr
)
4684 const AuthRecord
*rr
;
4685 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
4687 if (IdenticalResourceRecord(&rr
->resrec
, &pktrr
->resrec
))
4689 while (rr
->RRSet
&& rr
!= rr
->RRSet
) rr
= rr
->RRSet
;
4696 // PacketRRConflict is called when we've received an RR (pktrr) which has the same name
4697 // as one of our records (our) but different rdata.
4698 // 1. If our record is not a type that's supposed to be unique, we don't care.
4699 // 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
4700 // 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
4701 // points to our record, ignore this conflict (e.g. the packet record matches one of our
4702 // TXT records, and that record is marked as dependent on 'our', its SRV record).
4703 // 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
4704 // are members of the same RRSet, then this is not a conflict.
4705 mDNSlocal mDNSBool
PacketRRConflict(const mDNS
*const m
, const AuthRecord
*const our
, const CacheRecord
*const pktrr
)
4707 const AuthRecord
*ourset
= our
->RRSet
? our
->RRSet
: our
;
4709 // If not supposed to be unique, not a conflict
4710 if (!(our
->resrec
.RecordType
& kDNSRecordTypeUniqueMask
)) return(mDNSfalse
);
4712 // If a dependent record, not a conflict
4713 if (our
->DependentOn
|| MatchDependentOn(m
, pktrr
, our
)) return(mDNSfalse
);
4715 // If the pktrr matches a member of ourset, not a conflict
4716 if (FindRRSet(m
, pktrr
) == ourset
) return(mDNSfalse
);
4718 // Okay, this is a conflict
4722 // NOTE: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
4723 // the record list and/or question list.
4724 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
4725 mDNSlocal
void ResolveSimultaneousProbe(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
4726 DNSQuestion
*q
, AuthRecord
*our
)
4729 const mDNSu8
*ptr
= LocateAuthorities(query
, end
);
4730 mDNSBool FoundUpdate
= mDNSfalse
;
4732 for (i
= 0; i
< query
->h
.numAuthorities
; i
++)
4734 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, q
->InterfaceID
, kDNSRecordTypePacketAuth
, &m
->rec
);
4736 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
4738 FoundUpdate
= mDNStrue
;
4739 if (PacketRRConflict(m
, our
, &m
->rec
.r
))
4741 int result
= (int)our
->resrec
.rrclass
- (int)m
->rec
.r
.resrec
.rrclass
;
4742 if (!result
) result
= (int)our
->resrec
.rrtype
- (int)m
->rec
.r
.resrec
.rrtype
;
4743 if (!result
) result
= CompareRData(our
, &m
->rec
.r
);
4746 case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4749 case -1: debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4750 mDNS_Deregister_internal(m
, our
, mDNS_Dereg_conflict
);
4755 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4758 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our
->resrec
.name
->c
, DNSTypeName(our
->resrec
.rrtype
));
4760 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4763 mDNSlocal CacheRecord
*FindIdenticalRecordInCache(const mDNS
*const m
, ResourceRecord
*pktrr
)
4765 mDNSu32 slot
= HashSlot(pktrr
->name
);
4766 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, pktrr
);
4768 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4769 if (pktrr
->InterfaceID
== rr
->resrec
.InterfaceID
&& IdenticalResourceRecord(pktrr
, &rr
->resrec
)) break;
4773 // ProcessQuery examines a received query to see if we have any answers to give
4774 mDNSlocal mDNSu8
*ProcessQuery(mDNS
*const m
, const DNSMessage
*const query
, const mDNSu8
*const end
,
4775 const mDNSAddr
*srcaddr
, const mDNSInterfaceID InterfaceID
, mDNSBool LegacyQuery
, mDNSBool QueryWasMulticast
, mDNSBool QueryWasLocalUnicast
,
4776 DNSMessage
*const response
)
4778 mDNSBool FromLocalSubnet
= AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
4779 AuthRecord
*ResponseRecords
= mDNSNULL
;
4780 AuthRecord
**nrp
= &ResponseRecords
;
4781 CacheRecord
*ExpectedAnswers
= mDNSNULL
; // Records in our cache we expect to see updated
4782 CacheRecord
**eap
= &ExpectedAnswers
;
4783 DNSQuestion
*DupQuestions
= mDNSNULL
; // Our questions that are identical to questions in this packet
4784 DNSQuestion
**dqp
= &DupQuestions
;
4785 mDNSs32 delayresponse
= 0;
4786 mDNSBool SendLegacyResponse
= mDNSfalse
;
4787 const mDNSu8
*ptr
= query
->data
;
4788 mDNSu8
*responseptr
= mDNSNULL
;
4793 // *** 1. Parse Question Section and mark potential answers
4795 for (i
=0; i
<query
->h
.numQuestions
; i
++) // For each question...
4797 mDNSBool QuestionNeedsMulticastResponse
;
4798 int NumAnswersForThisQuestion
= 0;
4799 DNSQuestion pktq
, *q
;
4800 ptr
= getQuestion(query
, ptr
, end
, InterfaceID
, &pktq
); // get the question...
4801 if (!ptr
) goto exit
;
4803 // The only queries that *need* a multicast response are:
4804 // * Queries sent via multicast
4806 // * that don't have the kDNSQClass_UnicastResponse bit set
4807 // These queries need multicast responses because other clients will:
4808 // * suppress their own identical questions when they see these questions, and
4809 // * expire their cache records if they don't see the expected responses
4810 // For other queries, we may still choose to send the occasional multicast response anyway,
4811 // to keep our neighbours caches warm, and for ongoing conflict detection.
4812 QuestionNeedsMulticastResponse
= QueryWasMulticast
&& !LegacyQuery
&& !(pktq
.qclass
& kDNSQClass_UnicastResponse
);
4813 // Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
4814 pktq
.qclass
&= ~kDNSQClass_UnicastResponse
;
4816 // Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
4817 // can result in user callbacks which may change the record list and/or question list.
4818 // Also note: we just mark potential answer records here, without trying to build the
4819 // "ResponseRecords" list, because we don't want to risk user callbacks deleting records
4820 // from that list while we're in the middle of trying to build it.
4821 if (m
->CurrentRecord
) LogMsg("ProcessQuery ERROR m->CurrentRecord already set");
4822 m
->CurrentRecord
= m
->ResourceRecords
;
4823 while (m
->CurrentRecord
)
4825 rr
= m
->CurrentRecord
;
4826 m
->CurrentRecord
= rr
->next
;
4827 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && (QueryWasMulticast
|| QueryWasLocalUnicast
|| rr
->AllowRemoteQuery
))
4829 if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
4830 ResolveSimultaneousProbe(m
, query
, end
, &pktq
, rr
);
4831 else if (ResourceRecordIsValidAnswer(rr
))
4833 NumAnswersForThisQuestion
++;
4835 // NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
4836 // NR_AnswerTo == (mDNSu8*)~1 means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
4837 // NR_AnswerTo == (mDNSu8*)~0 means "definitely answer via multicast" (can't downgrade to unicast later)
4838 // If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
4839 // but the multicast querier is not on a matching subnet (e.g. because of overalyed subnets on one link)
4840 // then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
4841 if (QuestionNeedsMulticastResponse
|| (!FromLocalSubnet
&& QueryWasMulticast
&& !LegacyQuery
))
4843 // We only mark this question for sending if it is at least one second since the last time we multicast it
4844 // on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
4845 // This is to guard against the case where someone blasts us with queries as fast as they can.
4846 if (m
->timenow
- (rr
->LastMCTime
+ mDNSPlatformOneSecond
) >= 0 ||
4847 (rr
->LastMCInterface
!= mDNSInterfaceMark
&& rr
->LastMCInterface
!= InterfaceID
))
4848 rr
->NR_AnswerTo
= (mDNSu8
*)~0;
4850 else if (!rr
->NR_AnswerTo
) rr
->NR_AnswerTo
= LegacyQuery
? ptr
: (mDNSu8
*)~1;
4855 // If we couldn't answer this question, someone else might be able to,
4856 // so use random delay on response to reduce collisions
4857 if (NumAnswersForThisQuestion
== 0) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
4859 // We only do the following accelerated cache expiration processing and duplicate question suppression processing
4860 // for multicast queries with multicast responses.
4861 // For any query generating a unicast response we don't do this because we can't assume we will see the response
4862 if (QuestionNeedsMulticastResponse
)
4864 const mDNSu32 slot
= HashSlot(&pktq
.qname
);
4865 CacheGroup
*cg
= CacheGroupForName(m
, slot
, pktq
.qnamehash
, &pktq
.qname
);
4868 // Make a list indicating which of our own cache records we expect to see updated as a result of this query
4869 // Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
4870 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
4871 if (ResourceRecordAnswersQuestion(&rr
->resrec
, &pktq
) && rr
->resrec
.rdlength
<= SmallRecordLimit
)
4872 if (!rr
->NextInKAList
&& eap
!= &rr
->NextInKAList
)
4875 eap
= &rr
->NextInKAList
;
4876 if (rr
->MPUnansweredQ
== 0 || m
->timenow
- rr
->MPLastUnansweredQT
>= mDNSPlatformOneSecond
)
4878 // Although MPUnansweredQ is only really used for multi-packet query processing,
4879 // we increment it for both single-packet and multi-packet queries, so that it stays in sync
4880 // with the MPUnansweredKA value, which by necessity is incremented for both query types.
4881 rr
->MPUnansweredQ
++;
4882 rr
->MPLastUnansweredQT
= m
->timenow
;
4883 rr
->MPExpectingKA
= mDNStrue
;
4887 // Check if this question is the same as any of mine.
4888 // We only do this for non-truncated queries. Right now it would be too complicated to try
4889 // to keep track of duplicate suppression state between multiple packets, especially when we
4890 // can't guarantee to receive all of the Known Answer packets that go with a particular query.
4891 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
4892 for (q
= m
->Questions
; q
; q
=q
->next
)
4893 if (!q
->Target
.type
&& ActiveQuestion(q
) && m
->timenow
- q
->LastQTxTime
> mDNSPlatformOneSecond
/ 4)
4894 if (!q
->InterfaceID
|| q
->InterfaceID
== InterfaceID
)
4895 if (q
->NextInDQList
== mDNSNULL
&& dqp
!= &q
->NextInDQList
)
4896 if (q
->qtype
== pktq
.qtype
&& q
->qclass
== pktq
.qclass
&& q
->qnamehash
== pktq
.qnamehash
&& SameDomainName(&q
->qname
, &pktq
.qname
))
4897 { *dqp
= q
; dqp
= &q
->NextInDQList
; }
4902 // *** 2. Now we can safely build the list of marked answers
4904 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) // Now build our list of potential answers
4905 if (rr
->NR_AnswerTo
) // If we marked the record...
4906 AddRecordToResponseList(&nrp
, rr
, mDNSNULL
); // ... add it to the list
4909 // *** 3. Add additional records
4911 AddAdditionalsToResponseList(m
, ResponseRecords
, &nrp
, InterfaceID
);
4914 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
4916 for (i
=0; i
<query
->h
.numAnswers
; i
++) // For each record in the query's answer section...
4918 // Get the record...
4920 CacheRecord
*ourcacherr
;
4921 ptr
= GetLargeResourceRecord(m
, query
, ptr
, end
, InterfaceID
, kDNSRecordTypePacketAns
, &m
->rec
);
4922 if (!ptr
) goto exit
;
4924 // See if this Known-Answer suppresses any of our currently planned answers
4925 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4926 if (MustSendRecord(rr
) && ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
4927 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
4929 // See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
4930 for (rr
=m
->ResourceRecords
; rr
; rr
=rr
->next
)
4932 // If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
4933 if (rr
->ImmedAnswer
== InterfaceID
&& ShouldSuppressKnownAnswer(&m
->rec
.r
, rr
))
4935 if (srcaddr
->type
== mDNSAddrType_IPv4
)
4937 if (mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= zerov4Addr
;
4939 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
4941 if (mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= zerov6Addr
;
4943 if (mDNSIPv4AddressIsZero(rr
->v4Requester
) && mDNSIPv6AddressIsZero(rr
->v6Requester
))
4945 rr
->ImmedAnswer
= mDNSNULL
;
4946 rr
->ImmedUnicast
= mDNSfalse
;
4947 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
4948 LogMsg("Suppressed after%4d: %s", m
->timenow
- rr
->ImmedAnswerMarkTime
, ARDisplayString(m
, rr
));
4954 // See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
4955 // 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).
4956 ourcacherr
= FindIdenticalRecordInCache(m
, &m
->rec
.r
.resrec
);
4957 if (ourcacherr
&& ourcacherr
->MPExpectingKA
&& m
->timenow
- ourcacherr
->MPLastUnansweredQT
< mDNSPlatformOneSecond
)
4959 ourcacherr
->MPUnansweredKA
++;
4960 ourcacherr
->MPExpectingKA
= mDNSfalse
;
4963 // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
4964 // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
4965 // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
4966 eap
= &ExpectedAnswers
;
4969 CacheRecord
*rr
= *eap
;
4970 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
4971 { *eap
= rr
->NextInKAList
; rr
->NextInKAList
= mDNSNULL
; }
4972 else eap
= &rr
->NextInKAList
;
4975 // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
4978 dqp
= &DupQuestions
;
4981 DNSQuestion
*q
= *dqp
;
4982 if (ResourceRecordAnswersQuestion(&m
->rec
.r
.resrec
, q
))
4983 { *dqp
= q
->NextInDQList
; q
->NextInDQList
= mDNSNULL
; }
4984 else dqp
= &q
->NextInDQList
;
4987 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
4991 // *** 5. Cancel any additionals that were added because of now-deleted records
4993 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
4994 if (rr
->NR_AdditionalTo
&& !MustSendRecord(rr
->NR_AdditionalTo
))
4995 { rr
->NR_AnswerTo
= mDNSNULL
; rr
->NR_AdditionalTo
= mDNSNULL
; }
4998 // *** 6. Mark the send flags on the records we plan to send
5000 for (rr
=ResponseRecords
; rr
; rr
=rr
->NextResponse
)
5002 if (rr
->NR_AnswerTo
)
5004 mDNSBool SendMulticastResponse
= mDNSfalse
; // Send modern multicast response
5005 mDNSBool SendUnicastResponse
= mDNSfalse
; // Send modern unicast response (not legacy unicast response)
5007 // If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
5008 if (m
->timenow
- (rr
->LastMCTime
+ TicksTTL(rr
)/4) >= 0)
5010 SendMulticastResponse
= mDNStrue
;
5011 // If this record was marked for modern (delayed) unicast response, then mark it as promoted to
5012 // multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
5013 // If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
5014 if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) rr
->NR_AnswerTo
= (mDNSu8
*)~0;
5017 // If the client insists on a multicast response, then we'd better send one
5018 if (rr
->NR_AnswerTo
== (mDNSu8
*)~0) SendMulticastResponse
= mDNStrue
;
5019 else if (rr
->NR_AnswerTo
== (mDNSu8
*)~1) SendUnicastResponse
= mDNStrue
;
5020 else if (rr
->NR_AnswerTo
) SendLegacyResponse
= mDNStrue
;
5022 if (SendMulticastResponse
|| SendUnicastResponse
)
5024 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5025 rr
->ImmedAnswerMarkTime
= m
->timenow
;
5027 m
->NextScheduledResponse
= m
->timenow
;
5028 // If we're already planning to send this on another interface, just send it on all interfaces
5029 if (rr
->ImmedAnswer
&& rr
->ImmedAnswer
!= InterfaceID
)
5030 rr
->ImmedAnswer
= mDNSInterfaceMark
;
5033 rr
->ImmedAnswer
= InterfaceID
; // Record interface to send it on
5034 if (SendUnicastResponse
) rr
->ImmedUnicast
= mDNStrue
;
5035 if (srcaddr
->type
== mDNSAddrType_IPv4
)
5037 if (mDNSIPv4AddressIsZero(rr
->v4Requester
)) rr
->v4Requester
= srcaddr
->ip
.v4
;
5038 else if (!mDNSSameIPv4Address(rr
->v4Requester
, srcaddr
->ip
.v4
)) rr
->v4Requester
= onesIPv4Addr
;
5040 else if (srcaddr
->type
== mDNSAddrType_IPv6
)
5042 if (mDNSIPv6AddressIsZero(rr
->v6Requester
)) rr
->v6Requester
= srcaddr
->ip
.v6
;
5043 else if (!mDNSSameIPv6Address(rr
->v6Requester
, srcaddr
->ip
.v6
)) rr
->v6Requester
= onesIPv6Addr
;
5047 // If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
5048 // so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
5049 // else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
5050 // else, for a simple unique record reply, we can reply immediately; no need for delay
5051 if (query
->h
.flags
.b
[0] & kDNSFlag0_TC
) delayresponse
= mDNSPlatformOneSecond
* 20; // Divided by 50 = 400ms
5052 else if (rr
->resrec
.RecordType
== kDNSRecordTypeShared
) delayresponse
= mDNSPlatformOneSecond
; // Divided by 50 = 20ms
5054 else if (rr
->NR_AdditionalTo
&& rr
->NR_AdditionalTo
->NR_AnswerTo
== (mDNSu8
*)~0)
5056 // Since additional records are an optimization anyway, we only ever send them on one interface at a time
5057 // If two clients on different interfaces do queries that invoke the same optional additional answer,
5058 // then the earlier client is out of luck
5059 rr
->ImmedAdditional
= InterfaceID
;
5060 // No need to set m->NextScheduledResponse here
5061 // We'll send these additional records when we send them, or not, as the case may be
5066 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5068 if (delayresponse
&& (!m
->SuppressSending
|| (m
->SuppressSending
- m
->timenow
) < (delayresponse
+ 49) / 50))
5070 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5071 mDNSs32 oldss
= m
->SuppressSending
;
5072 if (oldss
&& delayresponse
)
5073 LogMsg("Current SuppressSending delay%5ld; require%5ld", m
->SuppressSending
- m
->timenow
, (delayresponse
+ 49) / 50);
5075 // Pick a random delay:
5076 // We start with the base delay chosen above (typically either 1 second or 20 seconds),
5077 // and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
5078 // This is an integer value, with resolution determined by the platform clock rate.
5079 // We then divide that by 50 to get the delay value in ticks. We defer the division until last
5080 // to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
5081 // The +49 before dividing is to ensure we round up, not down, to ensure that even
5082 // on platforms where the native clock rate is less than fifty ticks per second,
5083 // we still guarantee that the final calculated delay is at least one platform tick.
5084 // We want to make sure we don't ever allow the delay to be zero ticks,
5085 // because if that happens we'll fail the Bonjour Conformance Test.
5086 // Our final computed delay is 20-120ms for normal delayed replies,
5087 // or 400-500ms in the case of multi-packet known-answer lists.
5088 m
->SuppressSending
= m
->timenow
+ (delayresponse
+ (mDNSs32
)mDNSRandom((mDNSu32
)mDNSPlatformOneSecond
*5) + 49) / 50;
5089 if (m
->SuppressSending
== 0) m
->SuppressSending
= 1;
5090 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5091 if (oldss
&& delayresponse
)
5092 LogMsg("Set SuppressSending to %5ld", m
->SuppressSending
- m
->timenow
);
5097 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
5099 if (SendLegacyResponse
)
5100 responseptr
= GenerateUnicastResponse(query
, end
, InterfaceID
, LegacyQuery
, response
, ResponseRecords
);
5103 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5106 // *** 9. Finally, clear our link chains ready for use next time
5108 while (ResponseRecords
)
5110 rr
= ResponseRecords
;
5111 ResponseRecords
= rr
->NextResponse
;
5112 rr
->NextResponse
= mDNSNULL
;
5113 rr
->NR_AnswerTo
= mDNSNULL
;
5114 rr
->NR_AdditionalTo
= mDNSNULL
;
5117 while (ExpectedAnswers
)
5120 rr
= ExpectedAnswers
;
5121 ExpectedAnswers
= rr
->NextInKAList
;
5122 rr
->NextInKAList
= mDNSNULL
;
5124 // For non-truncated queries, we can definitively say that we should expect
5125 // to be seeing a response for any records still left in the ExpectedAnswers list
5126 if (!(query
->h
.flags
.b
[0] & kDNSFlag0_TC
))
5127 if (rr
->UnansweredQueries
== 0 || m
->timenow
- rr
->LastUnansweredTime
>= mDNSPlatformOneSecond
)
5129 rr
->UnansweredQueries
++;
5130 rr
->LastUnansweredTime
= m
->timenow
;
5131 if (rr
->UnansweredQueries
> 1)
5132 debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
5133 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5134 SetNextCacheCheckTime(m
, rr
);
5137 // If we've seen multiple unanswered queries for this record,
5138 // then mark it to expire in five seconds if we don't get a response by then.
5139 if (rr
->UnansweredQueries
>= MaxUnansweredQueries
)
5141 // Only show debugging message if this record was not about to expire anyway
5142 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
5143 debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5144 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5145 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
5147 // Make a guess, based on the multi-packet query / known answer counts, whether we think we
5148 // should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
5149 // possible packet loss of up to 20% of the additional KA packets.)
5150 else if (rr
->MPUnansweredQ
* 4 > rr
->MPUnansweredKA
* 5 + 8)
5152 // We want to do this conservatively.
5153 // If there are so many machines on the network that they have to use multi-packet known-answer lists,
5154 // then we don't want them to all hit the network simultaneously with their final expiration queries.
5155 // By setting the record to expire in four minutes, we achieve two things:
5156 // (a) the 90-95% final expiration queries will be less bunched together
5157 // (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
5158 mDNSu32 remain
= (mDNSu32
)(RRExpireTime(rr
) - m
->timenow
) / 4;
5159 if (remain
> 240 * (mDNSu32
)mDNSPlatformOneSecond
)
5160 remain
= 240 * (mDNSu32
)mDNSPlatformOneSecond
;
5162 // Only show debugging message if this record was not about to expire anyway
5163 if (RRExpireTime(rr
) - m
->timenow
> 4 * mDNSPlatformOneSecond
)
5164 debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
5165 rr
->UnansweredQueries
, rr
->MPUnansweredQ
, rr
->MPUnansweredKA
, CRDisplayString(m
, rr
));
5167 if (remain
<= 60 * (mDNSu32
)mDNSPlatformOneSecond
)
5168 rr
->UnansweredQueries
++; // Treat this as equivalent to one definite unanswered query
5169 rr
->MPUnansweredQ
= 0; // Clear MPQ/MPKA statistics
5170 rr
->MPUnansweredKA
= 0;
5171 rr
->MPExpectingKA
= mDNSfalse
;
5173 if (remain
< kDefaultReconfirmTimeForNoAnswer
)
5174 remain
= kDefaultReconfirmTimeForNoAnswer
;
5175 mDNS_Reconfirm_internal(m
, rr
, remain
);
5179 while (DupQuestions
)
5182 DNSQuestion
*q
= DupQuestions
;
5183 DupQuestions
= q
->NextInDQList
;
5184 q
->NextInDQList
= mDNSNULL
;
5185 i
= RecordDupSuppressInfo(q
->DupSuppress
, m
->timenow
, InterfaceID
, srcaddr
->type
);
5186 debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q
->qname
.c
, DNSTypeName(q
->qtype
), InterfaceID
,
5187 srcaddr
->type
== mDNSAddrType_IPv4
? "v4" : "v6", i
);
5190 return(responseptr
);
5193 mDNSlocal
void mDNSCoreReceiveQuery(mDNS
*const m
, const DNSMessage
*const msg
, const mDNSu8
*const end
,
5194 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
5195 const mDNSInterfaceID InterfaceID
)
5197 mDNSu8
*responseend
= mDNSNULL
;
5198 mDNSBool QueryWasLocalUnicast
= !mDNSAddrIsDNSMulticast(dstaddr
) && AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
);
5200 if (!InterfaceID
&& mDNSAddrIsDNSMulticast(dstaddr
))
5202 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)",
5203 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
5204 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
5205 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
5206 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5207 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
5211 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",
5212 srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
,
5213 msg
->h
.numQuestions
, msg
->h
.numQuestions
== 1 ? ", " : "s,",
5214 msg
->h
.numAnswers
, msg
->h
.numAnswers
== 1 ? ", " : "s,",
5215 msg
->h
.numAuthorities
, msg
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5216 msg
->h
.numAdditionals
, msg
->h
.numAdditionals
== 1 ? "" : "s");
5218 responseend
= ProcessQuery(m
, msg
, end
, srcaddr
, InterfaceID
,
5219 (srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
), mDNSAddrIsDNSMulticast(dstaddr
), QueryWasLocalUnicast
, &m
->omsg
);
5221 if (responseend
) // If responseend is non-null, that means we built a unicast response packet
5223 debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
5224 m
->omsg
.h
.numQuestions
, m
->omsg
.h
.numQuestions
== 1 ? "" : "s",
5225 m
->omsg
.h
.numAnswers
, m
->omsg
.h
.numAnswers
== 1 ? "" : "s",
5226 m
->omsg
.h
.numAdditionals
, m
->omsg
.h
.numAdditionals
== 1 ? "" : "s",
5227 srcaddr
, mDNSVal16(srcport
), InterfaceID
, srcaddr
->type
);
5228 mDNSSendDNSMessage(m
, &m
->omsg
, responseend
, InterfaceID
, srcaddr
, srcport
, -1, mDNSNULL
);
5232 // NOTE: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
5233 // the record list and/or question list.
5234 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
5235 mDNSlocal
void mDNSCoreReceiveResponse(mDNS
*const m
,
5236 const DNSMessage
*const response
, const mDNSu8
*end
,
5237 const mDNSAddr
*srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*dstaddr
, mDNSIPPort dstport
,
5238 const mDNSInterfaceID InterfaceID
)
5241 const mDNSu8
*ptr
= LocateAnswers(response
, end
); // We ignore questions (if any) in a DNS response packet
5242 CacheRecord
*CacheFlushRecords
= (CacheRecord
*)1; // "(CacheRecord*)1" is special (non-zero) end-of-list marker
5243 CacheRecord
**cfp
= &CacheFlushRecords
;
5245 // All records in a DNS response packet are treated as equally valid statements of truth. If we want
5246 // to guard against spoof responses, then the only credible protection against that is cryptographic
5247 // security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
5248 int totalrecords
= response
->h
.numAnswers
+ response
->h
.numAuthorities
+ response
->h
.numAdditionals
;
5250 (void)srcaddr
; // Currently used only for display in debugging message
5254 verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with %2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
5255 srcaddr
, dstaddr
, InterfaceID
,
5256 response
->h
.numQuestions
, response
->h
.numQuestions
== 1 ? ", " : "s,",
5257 response
->h
.numAnswers
, response
->h
.numAnswers
== 1 ? ", " : "s,",
5258 response
->h
.numAuthorities
, response
->h
.numAuthorities
== 1 ? "y, " : "ies,",
5259 response
->h
.numAdditionals
, response
->h
.numAdditionals
== 1 ? "" : "s");
5261 // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us
5262 if (!mDNSAddrIsDNSMulticast(dstaddr
))
5264 if (!AddressIsLocalSubnet(m
, InterfaceID
, srcaddr
) || (mDNSu32
)(m
->timenow
- m
->ExpectUnicastResponse
) > (mDNSu32
)(mDNSPlatformOneSecond
*2))
5266 // For now we don't put standard wide-area unicast responses in our main cache
5267 // (Later we should fix this and cache all known results in a unified manner.)
5268 if (response
->h
.id
.NotAnInteger
!= 0 || srcport
.NotAnInteger
!= MulticastDNSPort
.NotAnInteger
)
5272 for (i
= 0; i
< totalrecords
&& ptr
&& ptr
< end
; i
++)
5274 const mDNSu8 RecordType
= (mDNSu8
)((i
< response
->h
.numAnswers
) ? kDNSRecordTypePacketAns
: kDNSRecordTypePacketAdd
);
5275 ptr
= GetLargeResourceRecord(m
, response
, ptr
, end
, InterfaceID
, RecordType
, &m
->rec
);
5276 if (!ptr
) goto exit
; // Break out of the loop and clean up our CacheFlushRecords list before exiting
5278 // 1. Check that this packet resource record does not conflict with any of ours
5279 if (m
->CurrentRecord
) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
5280 m
->CurrentRecord
= m
->ResourceRecords
;
5281 while (m
->CurrentRecord
)
5283 AuthRecord
*rr
= m
->CurrentRecord
;
5284 m
->CurrentRecord
= rr
->next
;
5285 if (PacketRRMatchesSignature(&m
->rec
.r
, rr
)) // If interface, name, type (if shared record) and class match...
5287 // ... check to see if type and rdata are identical
5288 if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
&& SameRData(&m
->rec
.r
.resrec
, &rr
->resrec
))
5290 // If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
5291 if (m
->rec
.r
.resrec
.rroriginalttl
>= rr
->resrec
.rroriginalttl
/2 || m
->SleepState
)
5293 // If we were planning to send on this -- and only this -- interface, then we don't need to any more
5294 if (rr
->ImmedAnswer
== InterfaceID
) { rr
->ImmedAnswer
= mDNSNULL
; rr
->ImmedUnicast
= mDNSfalse
; }
5298 if (rr
->ImmedAnswer
== mDNSNULL
) { rr
->ImmedAnswer
= InterfaceID
; m
->NextScheduledResponse
= m
->timenow
; }
5299 else if (rr
->ImmedAnswer
!= InterfaceID
) { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
5302 // else, the packet RR has different type or different rdata -- check to see if this is a conflict
5303 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0 && PacketRRConflict(m
, rr
, &m
->rec
.r
))
5305 debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr
-> resrec
.rdatahash
, ARDisplayString(m
, rr
));
5306 debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m
->rec
.r
.resrec
.rdatahash
, CRDisplayString(m
, &m
->rec
.r
));
5308 // If this record is marked DependentOn another record for conflict detection purposes,
5309 // then *that* record has to be bumped back to probing state to resolve the conflict
5310 while (rr
->DependentOn
) rr
= rr
->DependentOn
;
5312 // If we've just whacked this record's ProbeCount, don't need to do it again
5313 if (rr
->ProbeCount
<= DefaultProbeCountForTypeUnique
)
5315 // If we'd previously verified this record, put it back to probing state and try again
5316 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
)
5318 debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5319 rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
5320 rr
->ProbeCount
= DefaultProbeCountForTypeUnique
+ 1;
5321 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(kDNSRecordTypeUnique
);
5322 InitializeLastAPTime(m
, rr
);
5323 RecordProbeFailure(m
, rr
); // Repeated late conflicts also cause us to back off to the slower probing rate
5325 // If we're probing for this record, we just failed
5326 else if (rr
->resrec
.RecordType
== kDNSRecordTypeUnique
)
5328 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5329 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
5331 // We assumed this record must be unique, but we were wrong.
5332 // (e.g. There are two mDNSResponders on the same machine giving
5333 // different answers for the reverse mapping record.)
5334 // This is simply a misconfiguration, and we don't try to recover from it.
5335 else if (rr
->resrec
.RecordType
== kDNSRecordTypeKnownUnique
)
5337 debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
5338 rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5339 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_conflict
);
5342 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
5343 rr
->resrec
.RecordType
, rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
));
5346 // Else, matching signature, different type or rdata, but not a considered a conflict.
5347 // If the packet record has the cache-flush bit set, then we check to see if we
5348 // have any record(s) of the same type that we should re-assert to rescue them
5349 // (see note about "multi-homing and bridged networks" at the end of this function).
5350 else if (m
->rec
.r
.resrec
.rrtype
== rr
->resrec
.rrtype
)
5351 if ((m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) && m
->timenow
- rr
->LastMCTime
> mDNSPlatformOneSecond
/2)
5352 { rr
->ImmedAnswer
= mDNSInterfaceMark
; m
->NextScheduledResponse
= m
->timenow
; }
5356 // 2. See if we want to add this packet resource record to our cache
5357 if (m
->rrcache_size
) // Only try to cache answers if we have a cache to put them in
5359 const mDNSu32 slot
= HashSlot(m
->rec
.r
.resrec
.name
);
5360 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &m
->rec
.r
.resrec
);
5362 // 2a. Check if this packet resource record is already in our cache
5363 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5365 // If we found this exact resource record, refresh its TTL
5366 if (rr
->resrec
.InterfaceID
== InterfaceID
&& IdenticalResourceRecord(&m
->rec
.r
.resrec
, &rr
->resrec
))
5368 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
5369 verbosedebugf("Found record size %5d interface %p already in cache: %s",
5370 m
->rec
.r
.resrec
.rdlength
, InterfaceID
, CRDisplayString(m
, &m
->rec
.r
));
5371 rr
->TimeRcvd
= m
->timenow
;
5373 if (m
->rec
.r
.resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
5375 // If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
5376 if (rr
->NextInCFList
== mDNSNULL
&& cfp
!= &rr
->NextInCFList
)
5377 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
5379 // If this packet record is marked unique, and our previous cached copy was not, then fix it
5380 if (!(rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
))
5383 for (q
= m
->Questions
; q
; q
=q
->next
) if (ResourceRecordAnswersQuestion(&rr
->resrec
, q
)) q
->UniqueAnswers
++;
5384 rr
->resrec
.RecordType
= m
->rec
.r
.resrec
.RecordType
;
5388 if (!mDNSPlatformMemSame(m
->rec
.r
.resrec
.rdata
->u
.data
, rr
->resrec
.rdata
->u
.data
, m
->rec
.r
.resrec
.rdlength
))
5390 // If the rdata of the packet record differs in name capitalization from the record in our cache
5391 // then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
5392 // a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
5393 rr
->resrec
.rroriginalttl
= 0;
5394 rr
->UnansweredQueries
= MaxUnansweredQueries
;
5395 SetNextCacheCheckTime(m
, rr
);
5396 // DO NOT break out here -- we want to continue as if we never found it
5398 else if (m
->rec
.r
.resrec
.rroriginalttl
> 0)
5400 rr
->resrec
.rroriginalttl
= m
->rec
.r
.resrec
.rroriginalttl
;
5401 rr
->UnansweredQueries
= 0;
5402 rr
->MPUnansweredQ
= 0;
5403 rr
->MPUnansweredKA
= 0;
5404 rr
->MPExpectingKA
= mDNSfalse
;
5405 SetNextCacheCheckTime(m
, rr
);
5410 // If the packet TTL is zero, that means we're deleting this record.
5411 // To give other hosts on the network a chance to protest, we push the deletion
5412 // out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
5413 // Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
5414 // lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
5415 rr
->resrec
.rroriginalttl
= 1;
5416 rr
->UnansweredQueries
= MaxUnansweredQueries
;
5417 SetNextCacheCheckTime(m
, rr
);
5423 // If packet resource record not in our cache, add it now
5424 // (unless it is just a deletion of a record we never had, in which case we don't care)
5425 if (!rr
&& m
->rec
.r
.resrec
.rroriginalttl
> 0)
5427 // If we don't have a CacheGroup for this name, make one now
5428 if (!cg
) cg
= GetCacheGroup(m
, slot
, &m
->rec
.r
.resrec
);
5429 if (cg
) rr
= GetCacheRecord(m
, cg
, m
->rec
.r
.resrec
.rdlength
); // Make a cache record, being careful not to recycle cg
5430 if (!rr
) NoCacheAnswer(m
, &m
->rec
.r
);
5433 RData
*saveptr
= rr
->resrec
.rdata
; // Save the rr->resrec.rdata pointer
5434 *rr
= m
->rec
.r
; // Block copy the CacheRecord object
5435 rr
->resrec
.rdata
= saveptr
; // Restore rr->resrec.rdata after the structure assignment
5436 rr
->resrec
.name
= cg
->name
; // And set rr->resrec.name to point into our CacheGroup header
5437 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
)
5438 { *cfp
= rr
; cfp
= &rr
->NextInCFList
; *cfp
= (CacheRecord
*)1; }
5439 // If this is an oversized record with external storage allocated, copy rdata to external storage
5440 if (rr
->resrec
.rdata
!= (RData
*)&rr
->rdatastorage
&& !(m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
))
5441 LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m
->rec
.r
.resrec
.name
->c
);
5442 if (m
->rec
.r
.resrec
.rdlength
> InlineCacheRDSize
)
5443 mDNSPlatformMemCopy(m
->rec
.r
.resrec
.rdata
, rr
->resrec
.rdata
, sizeofRDataHeader
+ m
->rec
.r
.resrec
.rdlength
);
5444 rr
->next
= mDNSNULL
; // Clear 'next' pointer
5445 *(cg
->rrcache_tail
) = rr
; // Append this record to tail of cache slot list
5446 cg
->rrcache_tail
= &(rr
->next
); // Advance tail pointer
5447 if (rr
->resrec
.RecordType
& kDNSRecordTypePacketUniqueMask
) // If marked unique, assume we may have
5448 rr
->DelayDelivery
= m
->timenow
+ mDNSPlatformOneSecond
; // to delay delivery of this 'add' event
5450 rr
->DelayDelivery
= CheckForSoonToExpireRecords(m
, rr
->resrec
.name
, rr
->resrec
.namehash
, slot
);
5451 CacheRecordAdd(m
, rr
);
5452 // MUST do this AFTER CacheRecordAdd(), because that's what sets CRActiveQuestion for us
5453 SetNextCacheCheckTime(m
, rr
);
5457 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5461 m
->rec
.r
.resrec
.RecordType
= 0; // Clear RecordType to show we're not still using it
5463 // If we've just received one or more records with their cache flush bits set,
5464 // then scan that cache slot to see if there are any old stale records we need to flush
5465 while (CacheFlushRecords
!= (CacheRecord
*)1)
5467 CacheRecord
*r1
= CacheFlushRecords
, *r2
;
5468 const mDNSu32 slot
= HashSlot(r1
->resrec
.name
);
5469 CacheGroup
*cg
= CacheGroupForRecord(m
, slot
, &r1
->resrec
);
5470 CacheFlushRecords
= CacheFlushRecords
->NextInCFList
;
5471 r1
->NextInCFList
= mDNSNULL
;
5472 for (r2
= cg
? cg
->members
: mDNSNULL
; r2
; r2
=r2
->next
)
5473 if (SameResourceRecordSignature(&r1
->resrec
, &r2
->resrec
))
5475 // If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
5476 // else, if record is old, mark it to be flushed
5477 if (m
->timenow
- r2
->TimeRcvd
< mDNSPlatformOneSecond
)
5478 r2
->resrec
.rroriginalttl
= r1
->resrec
.rroriginalttl
;
5481 verbosedebugf("Cache flush %p X %p %##s (%s)", r1
, r2
, r2
->resrec
.name
->c
, DNSTypeName(r2
->resrec
.rrtype
));
5482 // We set stale records to expire in one second.
5483 // This gives the owner a chance to rescue it if necessary.
5484 // This is important in the case of multi-homing and bridged networks:
5485 // Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
5486 // bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
5487 // set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
5488 // will promptly delete their cached copies of the (still valid) Ethernet IP address record.
5489 // By delaying the deletion by one second, we give X a change to notice that this bridging has
5490 // happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
5491 // We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
5492 // final expiration queries for this record.
5493 r2
->resrec
.rroriginalttl
= 1;
5494 r2
->TimeRcvd
= m
->timenow
;
5495 r2
->UnansweredQueries
= MaxUnansweredQueries
;
5496 SetNextCacheCheckTime(m
, r2
);
5499 if (r1
->DelayDelivery
) // If we were planning to delay delivery of this record, see if we still need to
5501 r1
->DelayDelivery
= CheckForSoonToExpireRecords(m
, r1
->resrec
.name
, r1
->resrec
.namehash
, slot
);
5502 if (!r1
->DelayDelivery
) CacheRecordDeferredAdd(m
, r1
);
5507 mDNSexport
void mDNSCoreReceive(mDNS
*const m
, void *const pkt
, const mDNSu8
*const end
,
5508 const mDNSAddr
*const srcaddr
, const mDNSIPPort srcport
, const mDNSAddr
*const dstaddr
, const mDNSIPPort dstport
,
5509 const mDNSInterfaceID InterfaceID
)
5511 DNSMessage
*msg
= (DNSMessage
*)pkt
;
5512 const mDNSu8 StdQ
= kDNSFlag0_QR_Query
| kDNSFlag0_OP_StdQuery
;
5513 const mDNSu8 StdR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_StdQuery
;
5515 mDNSu8
*ptr
= mDNSNULL
;
5516 const mDNSu8 UpdateR
= kDNSFlag0_QR_Response
| kDNSFlag0_OP_Update
;
5518 #ifndef UNICAST_DISABLED
5519 mDNSIPPort NATPort
= mDNSOpaque16fromIntVal(NATMAP_PORT
);
5521 if (srcport
.NotAnInteger
== NATPort
.NotAnInteger
)
5524 uDNS_ReceiveNATMap(m
, pkt
, (mDNSu16
)(end
- (mDNSu8
*)pkt
));
5529 if ((unsigned)(end
- (mDNSu8
*)pkt
) < sizeof(DNSMessageHeader
)) { LogMsg("DNS Message too short"); return; }
5530 QR_OP
= (mDNSu8
)(msg
->h
.flags
.b
[0] & kDNSFlag0_QROP_Mask
);
5531 // Read the integer parts which are in IETF byte-order (MSB first, LSB second)
5532 ptr
= (mDNSu8
*)&msg
->h
.numQuestions
;
5533 msg
->h
.numQuestions
= (mDNSu16
)((mDNSu16
)ptr
[0] << 8 | ptr
[1]);
5534 msg
->h
.numAnswers
= (mDNSu16
)((mDNSu16
)ptr
[2] << 8 | ptr
[3]);
5535 msg
->h
.numAuthorities
= (mDNSu16
)((mDNSu16
)ptr
[4] << 8 | ptr
[5]);
5536 msg
->h
.numAdditionals
= (mDNSu16
)((mDNSu16
)ptr
[6] << 8 | ptr
[7]);
5538 if (!m
) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
5540 // We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
5541 // If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
5542 if (!mDNSAddressIsValid(srcaddr
)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr
); return; }
5546 #ifndef UNICAST_DISABLED
5547 if (!mDNSAddressIsAllDNSLinkGroup(dstaddr
) && (QR_OP
== StdR
|| QR_OP
== UpdateR
))
5548 uDNS_ReceiveMsg(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5549 // Note: mDNSCore also needs to get access to received unicast responses
5551 if (QR_OP
== StdQ
) mDNSCoreReceiveQuery (m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5552 else if (QR_OP
== StdR
) mDNSCoreReceiveResponse(m
, msg
, end
, srcaddr
, srcport
, dstaddr
, dstport
, InterfaceID
);
5553 else if (QR_OP
!= UpdateR
)
5554 LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
5555 msg
->h
.flags
.b
[0], msg
->h
.flags
.b
[1], srcaddr
, mDNSVal16(srcport
), dstaddr
, mDNSVal16(dstport
), InterfaceID
);
5557 // Packet reception often causes a change to the task list:
5558 // 1. Inbound queries can cause us to need to send responses
5559 // 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
5560 // 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
5561 // 4. Response packets that answer questions may cause our client to issue new questions
5565 // ***************************************************************************
5566 #if COMPILER_LIKES_PRAGMA_MARK
5569 #pragma mark - Searcher Functions
5572 #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5574 mDNSlocal DNSQuestion
*FindDuplicateQuestion(const mDNS
*const m
, const DNSQuestion
*const question
)
5577 // Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
5578 // This prevents circular references, where two questions are each marked as a duplicate of the other.
5579 // Accordingly, we break out of the loop when we get to 'question', because there's no point searching
5580 // further in the list.
5581 for (q
= m
->Questions
; q
&& q
!= question
; q
=q
->next
) // Scan our list of questions
5582 if (q
->InterfaceID
== question
->InterfaceID
&& // for another question with the same InterfaceID,
5583 SameQTarget(q
, question
) && // and same unicast/multicast target settings
5584 q
->qtype
== question
->qtype
&& // type,
5585 q
->qclass
== question
->qclass
&& // class,
5586 q
->qnamehash
== question
->qnamehash
&&
5587 SameDomainName(&q
->qname
, &question
->qname
)) // and name
5592 // This is called after a question is deleted, in case other identical questions were being
5593 // suppressed as duplicates
5594 mDNSlocal
void UpdateQuestionDuplicates(mDNS
*const m
, const DNSQuestion
*const question
)
5597 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5598 if (q
->DuplicateOf
== question
) // To see if any questions were referencing this as their duplicate
5600 q
->ThisQInterval
= question
->ThisQInterval
;
5601 q
->RequestUnicast
= question
->RequestUnicast
;
5602 q
->LastQTime
= question
->LastQTime
;
5603 q
->RecentAnswerPkts
= 0;
5604 q
->DuplicateOf
= FindDuplicateQuestion(m
, q
);
5605 q
->LastQTxTime
= question
->LastQTxTime
;
5606 SetNextQueryTime(m
,q
);
5610 #define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
5611 ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger))
5613 mDNSlocal mStatus
mDNS_StartQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5615 if (question
->Target
.type
&& !ValidQuestionTarget(question
))
5617 LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
5618 question
->Target
.type
, mDNSVal16(question
->TargetPort
));
5619 question
->Target
.type
= mDNSAddrType_None
;
5622 if (!question
->Target
.type
) // No question->Target specified, so clear TargetPort and TargetQID
5624 question
->TargetPort
= zeroIPPort
;
5625 question
->TargetQID
= zeroID
;
5628 #ifndef UNICAST_DISABLED
5629 // If the client has specified 'kDNSServiceFlagsForceMulticast'
5630 // then we do a multicast query on that interface, even for unicast domains.
5631 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5632 question
->uDNS_info
.id
= zeroID
;
5633 else return uDNS_StartQuery(m
, question
);
5635 question
->uDNS_info
.id
= zeroID
;
5636 #endif // UNICAST_DISABLED
5638 //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5640 if (m
->rrcache_size
== 0) // Can't do queries if we have no cache space allocated
5641 return(mStatus_NoCache
);
5645 // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
5646 DNSQuestion
**q
= &m
->Questions
;
5647 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5648 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5652 LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
5653 question
->qname
.c
, DNSTypeName(question
->qtype
));
5654 return(mStatus_AlreadyRegistered
);
5657 // If this question is referencing a specific interface, make sure it exists
5658 if (question
->InterfaceID
&& question
->InterfaceID
!= mDNSInterface_LocalOnly
)
5660 NetworkInterfaceInfo
*intf
;
5661 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5662 if (intf
->InterfaceID
== question
->InterfaceID
) break;
5665 debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question
->qname
.c
, question
->InterfaceID
);
5666 return(mStatus_BadInterfaceErr
);
5670 if (!ValidateDomainName(&question
->qname
))
5672 LogMsg("Attempt to start query with invalid qname %##s %s", question
->qname
.c
, DNSTypeName(question
->qtype
));
5673 return(mStatus_Invalid
);
5676 // Note: In the case where we already have the answer to this question in our cache, that may be all the client
5677 // wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
5678 // be a waste. For that reason, we schedule our first query to go out in half a second. If AnswerNewQuestion() finds
5679 // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately.
5680 if (!m
->RandomQueryDelay
) m
->RandomQueryDelay
= 1 + (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
5682 question
->next
= mDNSNULL
;
5683 question
->qnamehash
= DomainNameHashValue(&question
->qname
); // MUST do this before FindDuplicateQuestion()
5684 question
->DelayAnswering
= CheckForSoonToExpireRecords(m
, &question
->qname
, question
->qnamehash
, HashSlot(&question
->qname
));
5685 question
->ThisQInterval
= InitialQuestionInterval
* 2; // MUST be > zero for an active question
5686 question
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
5687 question
->LastQTime
= m
->timenow
- m
->RandomQueryDelay
; // Avoid inter-machine synchronization
5688 question
->LastAnswerPktNum
= m
->PktNum
;
5689 question
->RecentAnswerPkts
= 0;
5690 question
->CurrentAnswers
= 0;
5691 question
->LargeAnswers
= 0;
5692 question
->UniqueAnswers
= 0;
5693 question
->DuplicateOf
= FindDuplicateQuestion(m
, question
);
5694 question
->NextInDQList
= mDNSNULL
;
5695 for (i
=0; i
<DupSuppressInfoSize
; i
++)
5696 question
->DupSuppress
[i
].InterfaceID
= mDNSNULL
;
5697 // question->InterfaceID must be already set by caller
5698 question
->SendQNow
= mDNSNULL
;
5699 question
->SendOnAll
= mDNSfalse
;
5700 question
->LastQTxTime
= m
->timenow
;
5702 if (!question
->DuplicateOf
)
5703 verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p %d (%p) started",
5704 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
, question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
);
5706 verbosedebugf("mDNS_StartQuery_internal: Question %##s %s %p %d (%p) duplicate of (%p)",
5707 question
->qname
.c
, DNSTypeName(question
->qtype
), question
->InterfaceID
, question
->LastQTime
+ question
->ThisQInterval
- m
->timenow
, question
, question
->DuplicateOf
);
5710 if (question
->InterfaceID
== mDNSInterface_LocalOnly
)
5712 if (!m
->NewLocalOnlyQuestions
) m
->NewLocalOnlyQuestions
= question
;
5716 if (!m
->NewQuestions
) m
->NewQuestions
= question
;
5717 SetNextQueryTime(m
,question
);
5720 return(mStatus_NoError
);
5724 mDNSlocal mStatus
mDNS_StopQuery_internal(mDNS
*const m
, DNSQuestion
*const question
)
5726 const mDNSu32 slot
= HashSlot(&question
->qname
);
5727 CacheGroup
*cg
= CacheGroupForName(m
, slot
, question
->qnamehash
, &question
->qname
);
5729 DNSQuestion
**q
= &m
->Questions
;
5731 if (uDNS_IsActiveQuery(question
, &m
->uDNS_info
)) return uDNS_StopQuery(m
, question
);
5733 if (question
->InterfaceID
== mDNSInterface_LocalOnly
) q
= &m
->LocalOnlyQuestions
;
5734 while (*q
&& *q
!= question
) q
=&(*q
)->next
;
5735 if (*q
) *q
= (*q
)->next
;
5738 if (question
->ThisQInterval
>= 0) // Only log error message if the query was supposed to be active
5739 LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
5740 question
->qname
.c
, DNSTypeName(question
->qtype
));
5741 return(mStatus_BadReferenceErr
);
5744 // Take care to cut question from list *before* calling UpdateQuestionDuplicates
5745 UpdateQuestionDuplicates(m
, question
);
5746 // But don't trash ThisQInterval until afterwards.
5747 question
->ThisQInterval
= -1;
5749 // If there are any cache records referencing this as their active question, then see if any other
5750 // question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
5751 for (rr
= cg
? cg
->members
: mDNSNULL
; rr
; rr
=rr
->next
)
5753 if (rr
->CRActiveQuestion
== question
)
5756 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
5757 if (ActiveQuestion(q
) && ResourceRecordAnswersQuestion(&rr
->resrec
, q
))
5759 verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), q
);
5760 rr
->CRActiveQuestion
= q
; // Question used to be active; new value may or may not be null
5761 if (!q
) m
->rrcache_active
--; // If no longer active, decrement rrcache_active count
5765 // If we just deleted the question that CacheRecordAdd() or CacheRecordRmv()is about to look at,
5766 // bump its pointer forward one question.
5767 if (m
->CurrentQuestion
== question
)
5769 debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
5770 question
->qname
.c
, DNSTypeName(question
->qtype
));
5771 m
->CurrentQuestion
= question
->next
;
5774 if (m
->NewQuestions
== question
)
5776 debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
5777 question
->qname
.c
, DNSTypeName(question
->qtype
));
5778 m
->NewQuestions
= question
->next
;
5781 if (m
->NewLocalOnlyQuestions
== question
) m
->NewLocalOnlyQuestions
= question
->next
;
5783 // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
5784 question
->next
= mDNSNULL
;
5785 return(mStatus_NoError
);
5788 mDNSexport mStatus
mDNS_StartQuery(mDNS
*const m
, DNSQuestion
*const question
)
5792 status
= mDNS_StartQuery_internal(m
, question
);
5797 mDNSexport mStatus
mDNS_StopQuery(mDNS
*const m
, DNSQuestion
*const question
)
5801 status
= mDNS_StopQuery_internal(m
, question
);
5806 mDNSexport mStatus
mDNS_Reconfirm(mDNS
*const m
, CacheRecord
*const rr
)
5810 status
= mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForNoAnswer
);
5815 mDNSexport mStatus
mDNS_ReconfirmByValue(mDNS
*const m
, ResourceRecord
*const rr
)
5817 mStatus status
= mStatus_BadReferenceErr
;
5820 cr
= FindIdenticalRecordInCache(m
, rr
);
5821 if (cr
) status
= mDNS_Reconfirm_internal(m
, cr
, kDefaultReconfirmTimeForNoAnswer
);
5826 mDNSexport mStatus
mDNS_StartBrowse(mDNS
*const m
, DNSQuestion
*const question
,
5827 const domainname
*const srv
, const domainname
*const domain
,
5828 const mDNSInterfaceID InterfaceID
, mDNSBool ForceMCast
, mDNSQuestionCallback
*Callback
, void *Context
)
5830 question
->InterfaceID
= InterfaceID
;
5831 question
->Target
= zeroAddr
;
5832 question
->qtype
= kDNSType_PTR
;
5833 question
->qclass
= kDNSClass_IN
;
5834 question
->LongLived
= mDNSfalse
;
5835 question
->ExpectUnique
= mDNSfalse
;
5836 question
->ForceMCast
= ForceMCast
;
5837 question
->QuestionCallback
= Callback
;
5838 question
->QuestionContext
= Context
;
5839 if (!ConstructServiceName(&question
->qname
, mDNSNULL
, srv
, domain
)) return(mStatus_BadParamErr
);
5841 #ifndef UNICAST_DISABLED
5842 if (question
->InterfaceID
== mDNSInterface_LocalOnly
|| question
->ForceMCast
|| IsLocalDomain(&question
->qname
))
5844 question
->LongLived
= mDNSfalse
;
5845 question
->uDNS_info
.id
= zeroID
;
5846 return(mDNS_StartQuery(m
, question
));
5851 // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5853 question
->LongLived
= mDNStrue
;
5854 status
= uDNS_StartQuery(m
, question
);
5859 return(mDNS_StartQuery(m
, question
));
5860 #endif // UNICAST_DISABLED
5863 mDNSlocal mDNSBool
MachineHasActiveIPv6(mDNS
*const m
)
5865 NetworkInterfaceInfo
*intf
;
5866 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
5867 if (intf
->ip
.type
== mDNSAddrType_IPv6
) return(mDNStrue
);
5871 mDNSlocal
void FoundServiceInfoSRV(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
5873 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5874 mDNSBool PortChanged
= (mDNSBool
)(query
->info
->port
.NotAnInteger
!= answer
->rdata
->u
.srv
.port
.NotAnInteger
);
5875 if (!AddRecord
) return;
5876 if (answer
->rrtype
!= kDNSType_SRV
) return;
5878 query
->info
->port
= answer
->rdata
->u
.srv
.port
;
5880 // If this is our first answer, then set the GotSRV flag and start the address query
5883 query
->GotSRV
= mDNStrue
;
5884 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5885 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5886 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5887 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5888 mDNS_StartQuery(m
, &query
->qAv4
);
5889 // Only do the AAAA query if this machine actually has IPv6 active
5890 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5892 // If this is not our first answer, only re-issue the address query if the target host name has changed
5893 else if ((query
->qAv4
.InterfaceID
!= query
->qSRV
.InterfaceID
&& query
->qAv4
.InterfaceID
!= answer
->InterfaceID
) ||
5894 !SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
))
5896 mDNS_StopQuery(m
, &query
->qAv4
);
5897 if (query
->qAv6
.ThisQInterval
>= 0) mDNS_StopQuery(m
, &query
->qAv6
);
5898 if (SameDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
) && !PortChanged
)
5900 // If we get here, it means:
5901 // 1. This is not our first SRV answer
5902 // 2. The interface ID is different, but the target host and port are the same
5903 // This implies that we're seeing the exact same SRV record on more than one interface, so we should
5904 // make our address queries at least as broad as the original SRV query so that we catch all the answers.
5905 query
->qAv4
.InterfaceID
= query
->qSRV
.InterfaceID
; // Will be mDNSInterface_Any, or a specific interface
5906 query
->qAv6
.InterfaceID
= query
->qSRV
.InterfaceID
;
5910 query
->qAv4
.InterfaceID
= answer
->InterfaceID
;
5911 AssignDomainName(&query
->qAv4
.qname
, &answer
->rdata
->u
.srv
.target
);
5912 query
->qAv6
.InterfaceID
= answer
->InterfaceID
;
5913 AssignDomainName(&query
->qAv6
.qname
, &answer
->rdata
->u
.srv
.target
);
5915 debugf("FoundServiceInfoSRV: Restarting address queries for %##s", query
->qAv4
.qname
.c
);
5916 mDNS_StartQuery(m
, &query
->qAv4
);
5917 // Only do the AAAA query if this machine actually has IPv6 active
5918 if (MachineHasActiveIPv6(m
)) mDNS_StartQuery(m
, &query
->qAv6
);
5920 else if (query
->ServiceInfoQueryCallback
&& query
->GotADD
&& query
->GotTXT
&& PortChanged
)
5922 if (++query
->Answers
>= 100)
5923 debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
5924 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.srv
.target
.c
,
5925 mDNSVal16(answer
->rdata
->u
.srv
.port
));
5926 query
->ServiceInfoQueryCallback(m
, query
);
5928 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5929 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5932 mDNSlocal
void FoundServiceInfoTXT(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
5934 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5935 if (!AddRecord
) return;
5936 if (answer
->rrtype
!= kDNSType_TXT
) return;
5937 if (answer
->rdlength
> sizeof(query
->info
->TXTinfo
)) return;
5939 query
->GotTXT
= mDNStrue
;
5940 query
->info
->TXTlen
= answer
->rdlength
;
5941 query
->info
->TXTinfo
[0] = 0; // In case answer->rdlength is zero
5942 mDNSPlatformMemCopy(answer
->rdata
->u
.txt
.c
, query
->info
->TXTinfo
, answer
->rdlength
);
5944 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query
->info
->name
.c
, query
->GotADD
);
5946 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5947 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5948 if (query
->ServiceInfoQueryCallback
&& query
->GotADD
)
5950 if (++query
->Answers
>= 100)
5951 debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
5952 query
->Answers
, query
->qSRV
.qname
.c
, answer
->rdata
->u
.txt
.c
);
5953 query
->ServiceInfoQueryCallback(m
, query
);
5957 mDNSlocal
void FoundServiceInfo(mDNS
*const m
, DNSQuestion
*question
, const ResourceRecord
*const answer
, mDNSBool AddRecord
)
5959 ServiceInfoQuery
*query
= (ServiceInfoQuery
*)question
->QuestionContext
;
5960 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
5961 if (!AddRecord
) return;
5963 if (answer
->rrtype
== kDNSType_A
)
5965 query
->info
->ip
.type
= mDNSAddrType_IPv4
;
5966 query
->info
->ip
.ip
.v4
= answer
->rdata
->u
.ipv4
;
5968 else if (answer
->rrtype
== kDNSType_AAAA
)
5970 query
->info
->ip
.type
= mDNSAddrType_IPv6
;
5971 query
->info
->ip
.ip
.v6
= answer
->rdata
->u
.ipv6
;
5975 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer
->name
->c
, answer
->rrtype
, DNSTypeName(answer
->rrtype
));
5979 query
->GotADD
= mDNStrue
;
5980 query
->info
->InterfaceID
= answer
->InterfaceID
;
5982 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query
->info
->ip
.type
, query
->info
->name
.c
, query
->GotTXT
);
5984 // CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
5985 // callback function is allowed to do anything, including deleting this query and freeing its memory.
5986 if (query
->ServiceInfoQueryCallback
&& query
->GotTXT
)
5988 if (++query
->Answers
>= 100)
5990 if (answer
->rrtype
== kDNSType_A
)
5991 debugf("**** WARNING **** have given %lu answers for %##s (A) %.4a", query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.ipv4
);
5993 debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query
->Answers
, query
->qSRV
.qname
.c
, &answer
->rdata
->u
.ipv6
);
5995 query
->ServiceInfoQueryCallback(m
, query
);
5999 // On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
6000 // If the query is not interface-specific, then InterfaceID may be zero
6001 // Each time the Callback is invoked, the remainder of the fields will have been filled in
6002 // In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
6003 mDNSexport mStatus
mDNS_StartResolveService(mDNS
*const m
,
6004 ServiceInfoQuery
*query
, ServiceInfo
*info
, mDNSServiceInfoQueryCallback
*Callback
, void *Context
)
6009 query
->qSRV
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6010 query
->qSRV
.InterfaceID
= info
->InterfaceID
;
6011 query
->qSRV
.Target
= zeroAddr
;
6012 AssignDomainName(&query
->qSRV
.qname
, &info
->name
);
6013 query
->qSRV
.qtype
= kDNSType_SRV
;
6014 query
->qSRV
.qclass
= kDNSClass_IN
;
6015 query
->qSRV
.LongLived
= mDNSfalse
;
6016 query
->qSRV
.ExpectUnique
= mDNStrue
;
6017 query
->qSRV
.ForceMCast
= mDNSfalse
;
6018 query
->qSRV
.QuestionCallback
= FoundServiceInfoSRV
;
6019 query
->qSRV
.QuestionContext
= query
;
6021 query
->qTXT
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6022 query
->qTXT
.InterfaceID
= info
->InterfaceID
;
6023 query
->qTXT
.Target
= zeroAddr
;
6024 AssignDomainName(&query
->qTXT
.qname
, &info
->name
);
6025 query
->qTXT
.qtype
= kDNSType_TXT
;
6026 query
->qTXT
.qclass
= kDNSClass_IN
;
6027 query
->qTXT
.LongLived
= mDNSfalse
;
6028 query
->qTXT
.ExpectUnique
= mDNStrue
;
6029 query
->qTXT
.ForceMCast
= mDNSfalse
;
6030 query
->qTXT
.QuestionCallback
= FoundServiceInfoTXT
;
6031 query
->qTXT
.QuestionContext
= query
;
6033 query
->qAv4
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6034 query
->qAv4
.InterfaceID
= info
->InterfaceID
;
6035 query
->qAv4
.Target
= zeroAddr
;
6036 query
->qAv4
.qname
.c
[0] = 0;
6037 query
->qAv4
.qtype
= kDNSType_A
;
6038 query
->qAv4
.qclass
= kDNSClass_IN
;
6039 query
->qAv4
.LongLived
= mDNSfalse
;
6040 query
->qAv4
.ExpectUnique
= mDNStrue
;
6041 query
->qAv4
.ForceMCast
= mDNSfalse
;
6042 query
->qAv4
.QuestionCallback
= FoundServiceInfo
;
6043 query
->qAv4
.QuestionContext
= query
;
6045 query
->qAv6
.ThisQInterval
= -1; // So that mDNS_StopResolveService() knows whether to cancel this question
6046 query
->qAv6
.InterfaceID
= info
->InterfaceID
;
6047 query
->qAv6
.Target
= zeroAddr
;
6048 query
->qAv6
.qname
.c
[0] = 0;
6049 query
->qAv6
.qtype
= kDNSType_AAAA
;
6050 query
->qAv6
.qclass
= kDNSClass_IN
;
6051 query
->qAv6
.LongLived
= mDNSfalse
;
6052 query
->qAv6
.ExpectUnique
= mDNStrue
;
6053 query
->qAv6
.ForceMCast
= mDNSfalse
;
6054 query
->qAv6
.QuestionCallback
= FoundServiceInfo
;
6055 query
->qAv6
.QuestionContext
= query
;
6057 query
->GotSRV
= mDNSfalse
;
6058 query
->GotTXT
= mDNSfalse
;
6059 query
->GotADD
= mDNSfalse
;
6063 query
->ServiceInfoQueryCallback
= Callback
;
6064 query
->ServiceInfoQueryContext
= Context
;
6066 // info->name = Must already be set up by client
6067 // info->interface = Must already be set up by client
6068 info
->ip
= zeroAddr
;
6069 info
->port
= zeroIPPort
;
6072 // We use mDNS_StartQuery_internal here because we're already holding the lock
6073 status
= mDNS_StartQuery_internal(m
, &query
->qSRV
);
6074 if (status
== mStatus_NoError
) status
= mDNS_StartQuery_internal(m
, &query
->qTXT
);
6075 if (status
!= mStatus_NoError
) mDNS_StopResolveService(m
, query
);
6081 mDNSexport
void mDNS_StopResolveService (mDNS
*const m
, ServiceInfoQuery
*q
)
6084 // We use mDNS_StopQuery_internal here because we're already holding the lock
6085 if (q
->qSRV
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qSRV
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qSRV
);
6086 if (q
->qTXT
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qTXT
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qTXT
);
6087 if (q
->qAv4
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qAv4
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qAv4
);
6088 if (q
->qAv6
.ThisQInterval
>= 0 || uDNS_IsActiveQuery(&q
->qAv6
, &m
->uDNS_info
)) mDNS_StopQuery_internal(m
, &q
->qAv6
);
6092 mDNSexport mStatus
mDNS_GetDomains(mDNS
*const m
, DNSQuestion
*const question
, mDNS_DomainType DomainType
, const domainname
*dom
,
6093 const mDNSInterfaceID InterfaceID
, mDNSQuestionCallback
*Callback
, void *Context
)
6095 question
->InterfaceID
= InterfaceID
;
6096 question
->Target
= zeroAddr
;
6097 question
->qtype
= kDNSType_PTR
;
6098 question
->qclass
= kDNSClass_IN
;
6099 question
->LongLived
= mDNSfalse
;
6100 question
->ExpectUnique
= mDNSfalse
;
6101 question
->ForceMCast
= mDNSfalse
;
6102 question
->QuestionCallback
= Callback
;
6103 question
->QuestionContext
= Context
;
6104 if (DomainType
> mDNS_DomainTypeMax
) return(mStatus_BadParamErr
);
6105 if (!MakeDomainNameFromDNSNameString(&question
->qname
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
6106 if (!dom
) dom
= &localdomain
;
6107 if (!AppendDomainName(&question
->qname
, dom
)) return(mStatus_BadParamErr
);
6108 return(mDNS_StartQuery(m
, question
));
6111 // ***************************************************************************
6112 #if COMPILER_LIKES_PRAGMA_MARK
6114 #pragma mark - Responder Functions
6117 mDNSexport mStatus
mDNS_Register(mDNS
*const m
, AuthRecord
*const rr
)
6121 status
= mDNS_Register_internal(m
, rr
);
6126 mDNSexport mStatus
mDNS_Update(mDNS
*const m
, AuthRecord
*const rr
, mDNSu32 newttl
,
6127 const mDNSu16 newrdlength
, RData
*const newrdata
, mDNSRecordUpdateCallback
*Callback
)
6129 #ifndef UNICAST_DISABLED
6130 mDNSBool unicast
= !(rr
->resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(rr
->resrec
.name
));
6132 mDNSBool unicast
= mDNSfalse
;
6135 if (!ValidateRData(rr
->resrec
.rrtype
, newrdlength
, newrdata
))
6136 { LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr
->resrec
, &newrdata
->u
, m
->MsgBuffer
)); return(mStatus_Invalid
); }
6140 // If TTL is unspecified, leave TTL unchanged
6141 if (newttl
== 0) newttl
= rr
->resrec
.rroriginalttl
;
6143 // If we already have an update queued up which has not gone through yet,
6144 // give the client a chance to free that memory
6145 if (!unicast
&& rr
->NewRData
)
6147 RData
*n
= rr
->NewRData
;
6148 rr
->NewRData
= mDNSNULL
; // Clear the NewRData pointer ...
6149 if (rr
->UpdateCallback
)
6150 rr
->UpdateCallback(m
, rr
, n
); // ...and let the client free this memory, if necessary
6153 rr
->NewRData
= newrdata
;
6154 rr
->newrdlength
= newrdlength
;
6155 rr
->UpdateCallback
= Callback
;
6157 if (unicast
) { mStatus status
= uDNS_UpdateRecord(m
, rr
); mDNS_Unlock(m
); return(status
); }
6159 if (rr
->resrec
.rroriginalttl
== newttl
&& rr
->resrec
.rdlength
== newrdlength
&& mDNSPlatformMemSame(rr
->resrec
.rdata
->u
.data
, newrdata
->u
.data
, newrdlength
))
6160 CompleteRDataUpdate(m
, rr
);
6164 domainname type
, domain
;
6165 DeconstructServiceName(rr
->resrec
.name
, &name
, &type
, &domain
);
6166 rr
->AnnounceCount
= InitialAnnounceCount
;
6167 // iChat often does suprious record updates where no data has changed. For the _presence service type, using
6168 // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
6169 // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
6170 // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
6171 // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
6172 if (SameDomainLabel(type
.c
, (mDNSu8
*)"\x6_ichat")) rr
->AnnounceCount
= 1;
6173 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6174 InitializeLastAPTime(m
, rr
);
6175 while (rr
->NextUpdateCredit
&& m
->timenow
- rr
->NextUpdateCredit
>= 0) GrantUpdateCredit(rr
);
6176 if (!rr
->UpdateBlocked
&& rr
->UpdateCredits
) rr
->UpdateCredits
--;
6177 if (!rr
->NextUpdateCredit
) rr
->NextUpdateCredit
= NonZeroTime(m
->timenow
+ kUpdateCreditRefreshInterval
);
6178 if (rr
->AnnounceCount
> rr
->UpdateCredits
+ 1) rr
->AnnounceCount
= (mDNSu8
)(rr
->UpdateCredits
+ 1);
6179 if (rr
->UpdateCredits
<= 5)
6181 mDNSu32 delay
= 6 - rr
->UpdateCredits
; // Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
6182 if (!rr
->UpdateBlocked
) rr
->UpdateBlocked
= NonZeroTime(m
->timenow
+ (mDNSs32
)delay
* mDNSPlatformOneSecond
);
6183 rr
->ThisAPInterval
*= 4;
6184 rr
->LastAPTime
= rr
->UpdateBlocked
- rr
->ThisAPInterval
;
6185 LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s", rr
->resrec
.name
->c
, delay
, delay
> 1 ? "s" : "");
6187 rr
->resrec
.rroriginalttl
= newttl
;
6191 return(mStatus_NoError
);
6194 // NOTE: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
6195 // the record list and/or question list.
6196 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6197 mDNSexport mStatus
mDNS_Deregister(mDNS
*const m
, AuthRecord
*const rr
)
6201 status
= mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
6206 mDNSexport
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
);
6208 mDNSlocal NetworkInterfaceInfo
*FindFirstAdvertisedInterface(mDNS
*const m
)
6210 NetworkInterfaceInfo
*intf
;
6211 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6212 if (intf
->Advertise
) break;
6216 mDNSlocal
void AdvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6219 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
6220 if (!primary
) primary
= set
; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
6222 // Send dynamic update for non-linklocal IPv4 Addresses
6223 mDNS_SetupResourceRecord(&set
->RR_A
, mDNSNULL
, set
->InterfaceID
, kDNSType_A
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNS_HostNameCallback
, set
);
6224 mDNS_SetupResourceRecord(&set
->RR_PTR
, mDNSNULL
, set
->InterfaceID
, kDNSType_PTR
, kHostNameTTL
, kDNSRecordTypeKnownUnique
, mDNSNULL
, mDNSNULL
);
6225 mDNS_SetupResourceRecord(&set
->RR_HINFO
, mDNSNULL
, set
->InterfaceID
, kDNSType_HINFO
, kHostNameTTL
, kDNSRecordTypeUnique
, mDNSNULL
, mDNSNULL
);
6227 #if ANSWER_REMOTE_HOSTNAME_QUERIES
6228 set
->RR_A
.AllowRemoteQuery
= mDNStrue
;
6229 set
->RR_PTR
.AllowRemoteQuery
= mDNStrue
;
6230 set
->RR_HINFO
.AllowRemoteQuery
= mDNStrue
;
6232 // 1. Set up Address record to map from host name ("foo.local.") to IP address
6233 // 2. Set up reverse-lookup PTR record to map from our address back to our host name
6234 AssignDomainName(set
->RR_A
.resrec
.name
, &m
->MulticastHostname
);
6235 if (set
->ip
.type
== mDNSAddrType_IPv4
)
6237 set
->RR_A
.resrec
.rrtype
= kDNSType_A
;
6238 set
->RR_A
.resrec
.rdata
->u
.ipv4
= set
->ip
.ip
.v4
;
6239 // Note: This is reverse order compared to a normal dotted-decimal IP address
6240 mDNS_snprintf(buffer
, sizeof(buffer
), "%d.%d.%d.%d.in-addr.arpa.",
6241 set
->ip
.ip
.v4
.b
[3], set
->ip
.ip
.v4
.b
[2], set
->ip
.ip
.v4
.b
[1], set
->ip
.ip
.v4
.b
[0]);
6243 else if (set
->ip
.type
== mDNSAddrType_IPv6
)
6246 set
->RR_A
.resrec
.rrtype
= kDNSType_AAAA
;
6247 set
->RR_A
.resrec
.rdata
->u
.ipv6
= set
->ip
.ip
.v6
;
6248 for (i
= 0; i
< 16; i
++)
6250 static const char hexValues
[] = "0123456789ABCDEF";
6251 buffer
[i
* 4 ] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] & 0x0F];
6252 buffer
[i
* 4 + 1] = '.';
6253 buffer
[i
* 4 + 2] = hexValues
[set
->ip
.ip
.v6
.b
[15 - i
] >> 4];
6254 buffer
[i
* 4 + 3] = '.';
6256 mDNS_snprintf(&buffer
[64], sizeof(buffer
)-64, "ip6.arpa.");
6259 MakeDomainNameFromDNSNameString(set
->RR_PTR
.resrec
.name
, buffer
);
6260 set
->RR_PTR
.HostTarget
= mDNStrue
; // Tell mDNS that the target of this PTR is to be kept in sync with our host name
6261 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
6263 set
->RR_A
.RRSet
= &primary
->RR_A
; // May refer to self
6265 mDNS_Register_internal(m
, &set
->RR_A
);
6266 mDNS_Register_internal(m
, &set
->RR_PTR
);
6268 if (m
->HIHardware
.c
[0] > 0 && m
->HISoftware
.c
[0] > 0 && m
->HIHardware
.c
[0] + m
->HISoftware
.c
[0] <= 254)
6270 mDNSu8
*p
= set
->RR_HINFO
.resrec
.rdata
->u
.data
;
6271 AssignDomainName(set
->RR_HINFO
.resrec
.name
, &m
->MulticastHostname
);
6272 set
->RR_HINFO
.DependentOn
= &set
->RR_A
;
6273 mDNSPlatformMemCopy(&m
->HIHardware
, p
, 1 + (mDNSu32
)m
->HIHardware
.c
[0]);
6275 mDNSPlatformMemCopy(&m
->HISoftware
, p
, 1 + (mDNSu32
)m
->HISoftware
.c
[0]);
6276 mDNS_Register_internal(m
, &set
->RR_HINFO
);
6280 debugf("Not creating HINFO record: platform support layer provided no information");
6281 set
->RR_HINFO
.resrec
.RecordType
= kDNSRecordTypeUnregistered
;
6285 mDNSlocal
void DeadvertiseInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6287 NetworkInterfaceInfo
*intf
;
6289 // If we still have address records referring to this one, update them
6290 NetworkInterfaceInfo
*primary
= FindFirstAdvertisedInterface(m
);
6291 AuthRecord
*A
= primary
? &primary
->RR_A
: mDNSNULL
;
6292 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6293 if (intf
->RR_A
.RRSet
== &set
->RR_A
)
6294 intf
->RR_A
.RRSet
= A
;
6296 // Unregister these records.
6297 // When doing the mDNS_Close processing, we first call DeadvertiseInterface for each interface, so by the time the platform
6298 // support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
6299 // Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
6300 // To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
6301 if (set
->RR_A
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_A
, mDNS_Dereg_normal
);
6302 if (set
->RR_PTR
. resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_PTR
, mDNS_Dereg_normal
);
6303 if (set
->RR_HINFO
.resrec
.RecordType
) mDNS_Deregister_internal(m
, &set
->RR_HINFO
, mDNS_Dereg_normal
);
6306 mDNSexport
void mDNS_SetFQDN(mDNS
*const m
)
6308 domainname newmname
;
6309 NetworkInterfaceInfo
*intf
;
6313 if (!AppendDomainLabel(&newmname
, &m
->hostlabel
)) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6314 if (!AppendLiteralLabelString(&newmname
, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
6315 if (SameDomainName(&m
->MulticastHostname
, &newmname
)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
6318 AssignDomainName(&m
->MulticastHostname
, &newmname
);
6320 // 1. Stop advertising our address records on all interfaces
6321 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6322 if (intf
->Advertise
) DeadvertiseInterface(m
, intf
);
6324 // 2. Start advertising our address records using the new name
6325 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6326 if (intf
->Advertise
) AdvertiseInterface(m
, intf
);
6328 // 3. Make sure that any SRV records (and the like) that reference our
6329 // host name in their rdata get updated to reference this new host name
6330 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
) if (rr
->HostTarget
) SetTargetToHostName(m
, rr
);
6331 for (rr
= m
->DuplicateRecords
; rr
; rr
=rr
->next
) if (rr
->HostTarget
) SetTargetToHostName(m
, rr
);
6336 mDNSexport
void mDNS_HostNameCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6338 (void)rr
; // Unused parameter
6342 char *msg
= "Unknown result";
6343 if (result
== mStatus_NoError
) msg
= "Name registered";
6344 else if (result
== mStatus_NameConflict
) msg
= "Name conflict";
6345 debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6349 if (result
== mStatus_NoError
)
6351 // Notify the client that the host name is successfully registered
6352 if (m
->MainCallback
)
6354 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
6355 m
->MainCallback(m
, result
);
6356 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
6359 else if (result
== mStatus_NameConflict
)
6361 domainlabel oldlabel
= m
->hostlabel
;
6363 // 1. First give the client callback a chance to pick a new name
6364 if (m
->MainCallback
)
6366 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
6367 m
->MainCallback(m
, mStatus_NameConflict
);
6368 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
6371 // 2. If the client callback didn't do it, add (or increment) an index ourselves
6372 if (SameDomainLabel(m
->hostlabel
.c
, oldlabel
.c
))
6373 IncrementLabelSuffix(&m
->hostlabel
, mDNSfalse
);
6375 // 3. Generate the FQDNs from the hostlabel,
6376 // and make sure all SRV records, etc., are updated to reference our new hostname
6378 LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel
.c
, m
->hostlabel
.c
);
6380 else if (result
== mStatus_MemFree
)
6382 // .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
6383 // mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
6384 debugf("mDNS_HostNameCallback: MemFree (ignored)");
6387 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result
, rr
->resrec
.name
->c
);
6390 mDNSlocal
void UpdateInterfaceProtocols(mDNS
*const m
, NetworkInterfaceInfo
*active
)
6392 NetworkInterfaceInfo
*intf
;
6393 active
->IPv4Available
= mDNSfalse
;
6394 active
->IPv6Available
= mDNSfalse
;
6395 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6396 if (intf
->InterfaceID
== active
->InterfaceID
)
6398 if (intf
->ip
.type
== mDNSAddrType_IPv4
&& intf
->McastTxRx
) active
->IPv4Available
= mDNStrue
;
6399 if (intf
->ip
.type
== mDNSAddrType_IPv6
&& intf
->McastTxRx
) active
->IPv6Available
= mDNStrue
;
6403 mDNSexport mStatus
mDNS_RegisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
, mDNSs32 delay
)
6405 mDNSBool FirstOfType
= mDNStrue
;
6406 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6408 if (!set
->InterfaceID
)
6409 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set
->ip
); return(mStatus_Invalid
); }
6411 if (!mDNSAddressIsValidNonZero(&set
->mask
))
6412 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set
->ip
, &set
->mask
); return(mStatus_Invalid
); }
6416 // Assume this interface will be active
6417 set
->InterfaceActive
= mDNStrue
;
6418 set
->IPv4Available
= (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
);
6419 set
->IPv6Available
= (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
);
6425 LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
6427 return(mStatus_AlreadyRegistered
);
6430 // This InterfaceID is already in the list, so mark this interface inactive for now
6431 if ((*p
)->InterfaceID
== set
->InterfaceID
)
6433 set
->InterfaceActive
= mDNSfalse
;
6434 if (set
->ip
.type
== (*p
)->ip
.type
) FirstOfType
= mDNSfalse
;
6435 if (set
->ip
.type
== mDNSAddrType_IPv4
&& set
->McastTxRx
) (*p
)->IPv4Available
= mDNStrue
;
6436 if (set
->ip
.type
== mDNSAddrType_IPv6
&& set
->McastTxRx
) (*p
)->IPv6Available
= mDNStrue
;
6442 set
->next
= mDNSNULL
;
6446 AdvertiseInterface(m
, set
);
6448 debugf("mDNS_RegisterInterface: InterfaceID %p %#a %s", set
->InterfaceID
, &set
->ip
,
6449 set
->InterfaceActive
?
6450 "not represented in list; marking active and retriggering queries" :
6451 "already represented in list; marking inactive for now");
6453 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6454 // giving the false impression that there's an active representative of this interface when there really isn't.
6455 // Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
6456 // even if we believe that we previously had an active representative of this interface.
6457 if (set
->McastTxRx
&& ((m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) || FirstOfType
|| set
->InterfaceActive
))
6461 mDNSs32 initial
= InitialQuestionInterval
;
6463 // Use a small amount of randomness:
6464 // In the case of a network administrator turning on an Ethernet hub so that all the connected machines establish link at
6465 // exactly the same time, we don't want them to all go and hit the network with identical queries at exactly the same moment.
6466 if (!m
->SuppressSending
) m
->SuppressSending
= m
->timenow
+ (mDNSs32
)mDNSRandom((mDNSu32
)InitialQuestionInterval
);
6470 LogMsg("Repeated transitions for interface %s (%#a); delaying packets by %d seconds",
6471 set
->ifname
, &set
->ip
, delay
/mDNSPlatformOneSecond
);
6472 initial
= InitialQuestionInterval
* 8; // Delay between first and second queries is eight seconds
6473 if (!m
->SuppressProbes
||
6474 m
->SuppressProbes
- (m
->timenow
+ delay
) < 0)
6475 m
->SuppressProbes
= (m
->timenow
+ delay
);
6477 for (q
= m
->Questions
; q
; q
=q
->next
) // Scan our list of questions
6478 if (!q
->InterfaceID
|| q
->InterfaceID
== set
->InterfaceID
) // If non-specific Q, or Q on this specific interface,
6479 { // then reactivate this question
6480 q
->ThisQInterval
= initial
; // MUST be > zero for an active question
6481 q
->RequestUnicast
= 2; // Set to 2 because is decremented once *before* we check it
6482 q
->LastQTime
= m
->timenow
- q
->ThisQInterval
+ delay
;
6483 q
->RecentAnswerPkts
= 0;
6484 SetNextQueryTime(m
,q
);
6487 // For all our non-specific authoritative resource records (and any dormant records specific to this interface)
6488 // we now need them to re-probe if necessary, and then re-announce.
6489 for (rr
= m
->ResourceRecords
; rr
; rr
=rr
->next
)
6490 if (!rr
->resrec
.InterfaceID
|| rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6492 if (rr
->resrec
.RecordType
== kDNSRecordTypeVerified
&& !rr
->DependentOn
) rr
->resrec
.RecordType
= kDNSRecordTypeUnique
;
6493 rr
->ProbeCount
= DefaultProbeCountForRecordType(rr
->resrec
.RecordType
);
6494 rr
->AnnounceCount
= delay
? (mDNSu8
)1 : InitialAnnounceCount
;
6495 rr
->ThisAPInterval
= DefaultAPIntervalForRecordType(rr
->resrec
.RecordType
);
6496 InitializeLastAPTime(m
, rr
);
6501 return(mStatus_NoError
);
6504 // NOTE: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
6505 // the record list and/or question list.
6506 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6507 mDNSexport
void mDNS_DeregisterInterface(mDNS
*const m
, NetworkInterfaceInfo
*set
)
6509 NetworkInterfaceInfo
**p
= &m
->HostInterfaces
;
6511 mDNSBool revalidate
= mDNSfalse
;
6512 // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
6513 // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
6514 // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
6515 if (m
->KnownBugs
& mDNS_KnownBug_PhantomInterfaces
) revalidate
= mDNStrue
;
6519 // Find this record in our list
6520 while (*p
&& *p
!= set
) p
=&(*p
)->next
;
6521 if (!*p
) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m
); return; }
6523 // Unlink this record from our list
6525 set
->next
= mDNSNULL
;
6527 if (!set
->InterfaceActive
)
6529 // If this interface not the active member of its set, update the v4/v6Available flags for the active member
6530 NetworkInterfaceInfo
*intf
;
6531 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6532 if (intf
->InterfaceActive
&& intf
->InterfaceID
== set
->InterfaceID
)
6533 UpdateInterfaceProtocols(m
, intf
);
6537 NetworkInterfaceInfo
*intf
;
6538 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6539 if (intf
->InterfaceID
== set
->InterfaceID
)
6543 debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
6545 intf
->InterfaceActive
= mDNStrue
;
6546 UpdateInterfaceProtocols(m
, intf
);
6548 // See if another representative *of the same type* exists. If not, we mave have gone from
6549 // dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
6550 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
6551 if (intf
->InterfaceID
== set
->InterfaceID
&& intf
->ip
.type
== set
->ip
.type
)
6553 if (!intf
) revalidate
= mDNStrue
;
6561 debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
6564 // 1. Deactivate any questions specific to this interface
6565 for (q
= m
->Questions
; q
; q
=q
->next
)
6566 if (q
->InterfaceID
== set
->InterfaceID
)
6567 q
->ThisQInterval
= 0;
6569 // 2. Flush any cache records received on this interface
6570 revalidate
= mDNSfalse
; // Don't revalidate if we're flushing the records
6571 FORALL_CACHERECORDS(slot
, cg
, rr
)
6572 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6573 PurgeCacheResourceRecord(m
, rr
);
6577 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6578 if (set
->Advertise
) DeadvertiseInterface(m
, set
);
6580 // If we have any cache records received on this interface that went away, then re-verify them.
6581 // In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
6582 // giving the false impression that there's an active representative of this interface when there really isn't.
6583 // Don't need to do this when shutting down, because *all* interfaces are about to go away
6584 if (revalidate
&& !m
->mDNS_shutdown
)
6589 m
->NextCacheCheck
= m
->timenow
;
6590 FORALL_CACHERECORDS(slot
, cg
, rr
)
6591 if (rr
->resrec
.InterfaceID
== set
->InterfaceID
)
6592 mDNS_Reconfirm_internal(m
, rr
, kDefaultReconfirmTimeForCableDisconnect
);
6598 mDNSlocal
void ServiceCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6600 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6601 (void)m
; // Unused parameter
6605 char *msg
= "Unknown result";
6606 if (result
== mStatus_NoError
) msg
= "Name Registered";
6607 else if (result
== mStatus_NameConflict
) msg
= "Name Conflict";
6608 else if (result
== mStatus_MemFree
) msg
= "Memory Free";
6609 debugf("ServiceCallback: %##s (%s) %s (%ld)", rr
->resrec
.name
->c
, DNSTypeName(rr
->resrec
.rrtype
), msg
, result
);
6613 // Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
6614 if (result
== mStatus_NoError
&& rr
!= &sr
->RR_SRV
) return;
6616 // If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
6617 if (result
== mStatus_NameConflict
)
6619 sr
->Conflict
= mDNStrue
; // Record that this service set had a conflict
6620 mDNS_DeregisterService(m
, sr
); // Unlink the records from our list
6624 if (result
== mStatus_MemFree
)
6626 // If the PTR record or any of the subtype PTR records are still in the process of deregistering,
6627 // don't pass on the NameConflict/MemFree message until every record is finished cleaning up.
6629 if (sr
->RR_PTR
.resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6630 for (i
=0; i
<sr
->NumSubTypes
; i
++) if (sr
->SubTypes
[i
].resrec
.RecordType
!= kDNSRecordTypeUnregistered
) return;
6632 // If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
6633 // then we can now report the NameConflict to the client
6634 if (sr
->Conflict
) result
= mStatus_NameConflict
;
6637 // CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
6638 // function is allowed to do anything, including deregistering this service and freeing its memory.
6639 if (sr
->ServiceCallback
)
6640 sr
->ServiceCallback(m
, sr
, result
);
6643 mDNSlocal
void NSSCallback(mDNS
*const m
, AuthRecord
*const rr
, mStatus result
)
6645 ServiceRecordSet
*sr
= (ServiceRecordSet
*)rr
->RecordContext
;
6646 if (sr
->ServiceCallback
)
6647 sr
->ServiceCallback(m
, sr
, result
);
6651 // Name is first label of domain name (any dots in the name are actual dots, not label separators)
6652 // Type is service type (e.g. "_ipp._tcp.")
6653 // Domain is fully qualified domain name (i.e. ending with a null label)
6654 // We always register a TXT, even if it is empty (so that clients are not
6655 // left waiting forever looking for a nonexistent record.)
6656 // If the host parameter is mDNSNULL or the root domain (ASCII NUL),
6657 // then the default host name (m->MulticastHostname) is automatically used
6658 mDNSexport mStatus
mDNS_RegisterService(mDNS
*const m
, ServiceRecordSet
*sr
,
6659 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6660 const domainname
*const host
, mDNSIPPort port
, const mDNSu8 txtinfo
[], mDNSu16 txtlen
,
6661 AuthRecord
*SubTypes
, mDNSu32 NumSubTypes
,
6662 const mDNSInterfaceID InterfaceID
, mDNSServiceCallback Callback
, void *Context
)
6667 sr
->ServiceCallback
= Callback
;
6668 sr
->ServiceContext
= Context
;
6669 sr
->Extras
= mDNSNULL
;
6670 sr
->NumSubTypes
= NumSubTypes
;
6671 sr
->SubTypes
= SubTypes
;
6672 sr
->Conflict
= mDNSfalse
;
6673 if (host
&& host
->c
[0]) sr
->Host
= *host
;
6674 else sr
->Host
.c
[0] = 0;
6676 // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
6677 if (!port
.NotAnInteger
) return(mDNS_RegisterNoSuchService(m
, &sr
->RR_SRV
, name
, type
, domain
, mDNSNULL
, mDNSInterface_Any
, NSSCallback
, sr
));
6679 // Initialize the AuthRecord objects to sane values
6680 mDNS_SetupResourceRecord(&sr
->RR_ADV
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeAdvisory
, ServiceCallback
, sr
);
6681 mDNS_SetupResourceRecord(&sr
->RR_PTR
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6682 mDNS_SetupResourceRecord(&sr
->RR_SRV
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6683 mDNS_SetupResourceRecord(&sr
->RR_TXT
, mDNSNULL
, InterfaceID
, kDNSType_TXT
, kStandardTTL
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6685 // If the client is registering an oversized TXT record,
6686 // it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
6687 if (sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
< txtlen
)
6688 sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
= txtlen
;
6690 // Set up the record names
6691 // For now we only create an advisory record for the main type, not for subtypes
6692 // We need to gain some operational experience before we decide if there's a need to create them for subtypes too
6693 if (ConstructServiceName(sr
->RR_ADV
.resrec
.name
, (domainlabel
*)"\x09_services", (domainname
*)"\x07_dns-sd\x04_udp", domain
) == mDNSNULL
)
6694 return(mStatus_BadParamErr
);
6695 if (ConstructServiceName(sr
->RR_PTR
.resrec
.name
, mDNSNULL
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6696 if (ConstructServiceName(sr
->RR_SRV
.resrec
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6697 AssignDomainName(sr
->RR_TXT
.resrec
.name
, sr
->RR_SRV
.resrec
.name
);
6699 // 1. Set up the ADV record rdata to advertise our service type
6700 AssignDomainName(&sr
->RR_ADV
.resrec
.rdata
->u
.name
, sr
->RR_PTR
.resrec
.name
);
6702 // 2. Set up the PTR record rdata to point to our service name
6703 // We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
6704 AssignDomainName(&sr
->RR_PTR
.resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6705 sr
->RR_PTR
.Additional1
= &sr
->RR_SRV
;
6706 sr
->RR_PTR
.Additional2
= &sr
->RR_TXT
;
6708 // 2a. Set up any subtype PTRs to point to our service name
6709 // If the client is using subtypes, it is the client's responsibility to have
6710 // already set the first label of the record name to the subtype being registered
6711 for (i
=0; i
<NumSubTypes
; i
++)
6714 AssignDomainName(&st
, sr
->SubTypes
[i
].resrec
.name
);
6715 st
.c
[1+st
.c
[0]] = 0; // Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
6716 AppendDomainName(&st
, type
);
6717 mDNS_SetupResourceRecord(&sr
->SubTypes
[i
], mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, ServiceCallback
, sr
);
6718 if (ConstructServiceName(sr
->SubTypes
[i
].resrec
.name
, mDNSNULL
, &st
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6719 AssignDomainName(&sr
->SubTypes
[i
].resrec
.rdata
->u
.name
, sr
->RR_SRV
.resrec
.name
);
6720 sr
->SubTypes
[i
].Additional1
= &sr
->RR_SRV
;
6721 sr
->SubTypes
[i
].Additional2
= &sr
->RR_TXT
;
6724 // 3. Set up the SRV record rdata.
6725 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.priority
= 0;
6726 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.weight
= 0;
6727 sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
= port
;
6729 // Setting HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
6730 if (sr
->Host
.c
[0]) AssignDomainName(&sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
, &sr
->Host
);
6731 else { sr
->RR_SRV
.HostTarget
= mDNStrue
; sr
->RR_SRV
.resrec
.rdata
->u
.srv
.target
.c
[0] = '\0'; }
6733 // 4. Set up the TXT record rdata,
6734 // and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
6735 if (txtinfo
== mDNSNULL
) sr
->RR_TXT
.resrec
.rdlength
= 0;
6736 else if (txtinfo
!= sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
)
6738 sr
->RR_TXT
.resrec
.rdlength
= txtlen
;
6739 if (sr
->RR_TXT
.resrec
.rdlength
> sr
->RR_TXT
.resrec
.rdata
->MaxRDLength
) return(mStatus_BadParamErr
);
6740 mDNSPlatformMemCopy(txtinfo
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, txtlen
);
6742 sr
->RR_TXT
.DependentOn
= &sr
->RR_SRV
;
6744 #ifndef UNICAST_DISABLED
6745 // If the client has specified an explicit InterfaceID,
6746 // then we do a multicast registration on that interface, even for unicast domains.
6747 if (!(InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6751 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6752 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6753 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6754 // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
6755 if (!sr
->RR_TXT
.resrec
.rdlength
) { sr
->RR_TXT
.resrec
.rdlength
= 1; sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6756 status
= uDNS_RegisterService(m
, sr
);
6762 err
= mDNS_Register_internal(m
, &sr
->RR_SRV
);
6763 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_TXT
);
6764 // We register the RR_PTR last, because we want to be sure that in the event of a forced call to
6765 // mDNS_Close, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
6766 // the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
6767 // the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
6768 // make sure we've deregistered all our records and done any other necessary cleanup before that happens.
6769 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_ADV
);
6770 for (i
=0; i
<NumSubTypes
; i
++) if (!err
) err
= mDNS_Register_internal(m
, &sr
->SubTypes
[i
]);
6771 if (!err
) err
= mDNS_Register_internal(m
, &sr
->RR_PTR
);
6775 if (err
) mDNS_DeregisterService(m
, sr
);
6779 mDNSexport mStatus
mDNS_AddRecordToService(mDNS
*const m
, ServiceRecordSet
*sr
,
6780 ExtraResourceRecord
*extra
, RData
*rdata
, mDNSu32 ttl
)
6782 ExtraResourceRecord
**e
;
6785 extra
->next
= mDNSNULL
;
6786 mDNS_SetupResourceRecord(&extra
->r
, rdata
, sr
->RR_PTR
.resrec
.InterfaceID
, extra
->r
.resrec
.rrtype
, ttl
, kDNSRecordTypeUnique
, ServiceCallback
, sr
);
6787 AssignDomainName(extra
->r
.resrec
.name
, sr
->RR_SRV
.resrec
.name
);
6789 #ifndef UNICAST_DISABLED
6790 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6793 // BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
6794 // since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
6795 // Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
6796 // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck)
6797 if (extra
->r
.resrec
.rrtype
== kDNSType_TXT
&& extra
->r
.resrec
.rdlength
== 0)
6798 { extra
->r
.resrec
.rdlength
= 1; extra
->r
.resrec
.rdata
->u
.txt
.c
[0] = 0; }
6799 status
= uDNS_AddRecordToService(m
, sr
, extra
);
6807 while (*e
) e
= &(*e
)->next
;
6809 if (ttl
== 0) ttl
= kStandardTTL
;
6811 extra
->r
.DependentOn
= &sr
->RR_SRV
;
6813 debugf("mDNS_AddRecordToService adding record to %##s", extra
->r
.resrec
.name
->c
);
6815 status
= mDNS_Register_internal(m
, &extra
->r
);
6816 if (status
== mStatus_NoError
) *e
= extra
;
6821 mDNSexport mStatus
mDNS_RemoveRecordFromService(mDNS
*const m
, ServiceRecordSet
*sr
, ExtraResourceRecord
*extra
, mDNSRecordCallback MemFreeCallback
, void *Context
)
6823 ExtraResourceRecord
**e
;
6828 while (*e
&& *e
!= extra
) e
= &(*e
)->next
;
6831 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra
->r
.resrec
.name
->c
);
6832 status
= mStatus_BadReferenceErr
;
6836 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra
->r
.resrec
.name
->c
);
6837 extra
->r
.RecordCallback
= MemFreeCallback
;
6838 extra
->r
.RecordContext
= Context
;
6840 #ifndef UNICAST_DISABLED
6841 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6842 status
= uDNS_DeregisterRecord(m
, &extra
->r
);
6845 status
= mDNS_Deregister_internal(m
, &extra
->r
, mDNS_Dereg_normal
);
6851 mDNSexport mStatus
mDNS_RenameAndReregisterService(mDNS
*const m
, ServiceRecordSet
*const sr
, const domainlabel
*newname
)
6853 // NOTE: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
6854 // mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
6855 domainlabel name1
, name2
;
6856 domainname type
, domain
;
6857 domainname
*host
= mDNSNULL
;
6858 ExtraResourceRecord
*extras
= sr
->Extras
;
6861 DeconstructServiceName(sr
->RR_SRV
.resrec
.name
, &name1
, &type
, &domain
);
6865 IncrementLabelSuffix(&name2
, mDNStrue
);
6868 LogMsg("Service \"%##s\" renamed to \"%#s\"", sr
->RR_SRV
.resrec
.name
->c
, newname
->c
);
6869 if (sr
->RR_SRV
.HostTarget
== mDNSfalse
&& sr
->Host
.c
[0]) host
= &sr
->Host
;
6871 err
= mDNS_RegisterService(m
, sr
, newname
, &type
, &domain
,
6872 host
, sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
, sr
->RR_TXT
.resrec
.rdata
->u
.txt
.c
, sr
->RR_TXT
.resrec
.rdlength
,
6873 sr
->SubTypes
, sr
->NumSubTypes
,
6874 sr
->RR_PTR
.resrec
.InterfaceID
, sr
->ServiceCallback
, sr
->ServiceContext
);
6876 // mDNS_RegisterService() just reset sr->Extras to NULL.
6877 // Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
6878 // through the old list of extra records, and re-add them to our freshly created service registration
6879 while (!err
&& extras
)
6881 ExtraResourceRecord
*e
= extras
;
6882 extras
= extras
->next
;
6883 err
= mDNS_AddRecordToService(m
, sr
, e
, e
->r
.resrec
.rdata
, e
->r
.resrec
.rroriginalttl
);
6889 // NOTE: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
6890 // which may change the record list and/or question list.
6891 // Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
6892 mDNSexport mStatus
mDNS_DeregisterService(mDNS
*const m
, ServiceRecordSet
*sr
)
6894 // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
6895 if (!sr
->RR_SRV
.resrec
.rdata
->u
.srv
.port
.NotAnInteger
) return(mDNS_DeregisterNoSuchService(m
, &sr
->RR_SRV
));
6897 #ifndef UNICAST_DISABLED
6898 if (!(sr
->RR_SRV
.resrec
.InterfaceID
== mDNSInterface_LocalOnly
|| IsLocalDomain(sr
->RR_SRV
.resrec
.name
)))
6902 status
= uDNS_DeregisterService(m
, sr
);
6907 if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeUnregistered
)
6909 debugf("Service set for %##s already deregistered", sr
->RR_SRV
.resrec
.name
->c
);
6910 return(mStatus_BadReferenceErr
);
6912 else if (sr
->RR_PTR
.resrec
.RecordType
== kDNSRecordTypeDeregistering
)
6914 debugf("Service set for %##s already in the process of deregistering", sr
->RR_SRV
.resrec
.name
->c
);
6915 return(mStatus_NoError
);
6921 ExtraResourceRecord
*e
;
6925 // We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
6926 // SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
6927 mDNS_Deregister_internal(m
, &sr
->RR_SRV
, mDNS_Dereg_repeat
);
6928 mDNS_Deregister_internal(m
, &sr
->RR_TXT
, mDNS_Dereg_repeat
);
6930 mDNS_Deregister_internal(m
, &sr
->RR_ADV
, mDNS_Dereg_normal
);
6932 // We deregister all of the extra records, but we leave the sr->Extras list intact
6933 // in case the client wants to do a RenameAndReregister and reinstate the registration
6936 mDNS_Deregister_internal(m
, &e
->r
, mDNS_Dereg_repeat
);
6940 for (i
=0; i
<sr
->NumSubTypes
; i
++)
6941 mDNS_Deregister_internal(m
, &sr
->SubTypes
[i
], mDNS_Dereg_normal
);
6943 // Be sure to deregister the PTR last!
6944 // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
6945 // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) back to the client callback,
6946 // which is then at liberty to free the ServiceRecordSet memory at will. We need to make sure
6947 // we've deregistered all our records and done any other necessary cleanup before that happens.
6948 status
= mDNS_Deregister_internal(m
, &sr
->RR_PTR
, mDNS_Dereg_normal
);
6954 // Create a registration that asserts that no such service exists with this name.
6955 // This can be useful where there is a given function is available through several protocols.
6956 // For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
6957 // protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
6958 // "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
6959 // could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
6960 mDNSexport mStatus
mDNS_RegisterNoSuchService(mDNS
*const m
, AuthRecord
*const rr
,
6961 const domainlabel
*const name
, const domainname
*const type
, const domainname
*const domain
,
6962 const domainname
*const host
,
6963 const mDNSInterfaceID InterfaceID
, mDNSRecordCallback Callback
, void *Context
)
6965 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_SRV
, kHostNameTTL
, kDNSRecordTypeUnique
, Callback
, Context
);
6966 if (ConstructServiceName(rr
->resrec
.name
, name
, type
, domain
) == mDNSNULL
) return(mStatus_BadParamErr
);
6967 rr
->resrec
.rdata
->u
.srv
.priority
= 0;
6968 rr
->resrec
.rdata
->u
.srv
.weight
= 0;
6969 rr
->resrec
.rdata
->u
.srv
.port
= zeroIPPort
;
6970 if (host
&& host
->c
[0]) AssignDomainName(&rr
->resrec
.rdata
->u
.srv
.target
, host
);
6971 else rr
->HostTarget
= mDNStrue
;
6972 return(mDNS_Register(m
, rr
));
6975 mDNSexport mStatus
mDNS_AdvertiseDomains(mDNS
*const m
, AuthRecord
*rr
,
6976 mDNS_DomainType DomainType
, const mDNSInterfaceID InterfaceID
, char *domname
)
6978 mDNS_SetupResourceRecord(rr
, mDNSNULL
, InterfaceID
, kDNSType_PTR
, kStandardTTL
, kDNSRecordTypeShared
, mDNSNULL
, mDNSNULL
);
6979 if (!MakeDomainNameFromDNSNameString(rr
->resrec
.name
, mDNS_DomainTypeNames
[DomainType
])) return(mStatus_BadParamErr
);
6980 if (!MakeDomainNameFromDNSNameString(&rr
->resrec
.rdata
->u
.name
, domname
)) return(mStatus_BadParamErr
);
6981 return(mDNS_Register(m
, rr
));
6984 // ***************************************************************************
6985 #if COMPILER_LIKES_PRAGMA_MARK
6988 #pragma mark - Startup and Shutdown
6991 mDNSlocal
void mDNS_GrowCache_internal(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
6993 if (storage
&& numrecords
)
6996 debugf("Adding cache storage for %d more records (%d bytes)", numrecords
, numrecords
*sizeof(CacheEntity
));
6997 for (i
=0; i
<numrecords
; i
++) storage
[i
].next
= &storage
[i
+1];
6998 storage
[numrecords
-1].next
= m
->rrcache_free
;
6999 m
->rrcache_free
= storage
;
7000 m
->rrcache_size
+= numrecords
;
7004 mDNSexport
void mDNS_GrowCache(mDNS
*const m
, CacheEntity
*storage
, mDNSu32 numrecords
)
7007 mDNS_GrowCache_internal(m
, storage
, numrecords
);
7011 mDNSexport mStatus
mDNS_Init(mDNS
*const m
, mDNS_PlatformSupport
*const p
,
7012 CacheEntity
*rrcachestorage
, mDNSu32 rrcachesize
,
7013 mDNSBool AdvertiseLocalAddresses
, mDNSCallback
*Callback
, void *Context
)
7016 mDNSs32 timenow
, timenow_adjust
;
7019 if (!rrcachestorage
) rrcachesize
= 0;
7023 m
->CanReceiveUnicastOn5353
= mDNSfalse
; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
7024 m
->AdvertiseLocalAddresses
= AdvertiseLocalAddresses
;
7025 m
->mDNSPlatformStatus
= mStatus_Waiting
;
7026 m
->UnicastPort4
= zeroIPPort
;
7027 m
->UnicastPort6
= zeroIPPort
;
7028 m
->MainCallback
= Callback
;
7029 m
->MainContext
= Context
;
7030 m
->rec
.r
.resrec
.RecordType
= 0;
7032 // For debugging: To catch and report locking failures
7034 m
->mDNS_reentrancy
= 0;
7035 m
->mDNS_shutdown
= mDNSfalse
;
7036 m
->lock_rrcache
= 0;
7037 m
->lock_Questions
= 0;
7038 m
->lock_Records
= 0;
7040 // Task Scheduling variables
7041 result
= mDNSPlatformTimeInit();
7042 if (result
!= mStatus_NoError
) return(result
);
7043 timenow_adjust
= (mDNSs32
)mDNSRandom(0xFFFFFFFF);
7044 timenow
= mDNSPlatformRawTime() + timenow_adjust
;
7046 m
->timenow
= 0; // MUST only be set within mDNS_Lock/mDNS_Unlock section
7047 m
->timenow_last
= timenow
;
7048 m
->timenow_adjust
= timenow_adjust
;
7049 m
->NextScheduledEvent
= timenow
;
7050 m
->SuppressSending
= timenow
;
7051 m
->NextCacheCheck
= timenow
+ 0x78000000;
7052 m
->NextScheduledQuery
= timenow
+ 0x78000000;
7053 m
->NextScheduledProbe
= timenow
+ 0x78000000;
7054 m
->NextScheduledResponse
= timenow
+ 0x78000000;
7055 m
->ExpectUnicastResponse
= timenow
+ 0x78000000;
7056 m
->RandomQueryDelay
= 0;
7058 m
->SendDeregistrations
= mDNSfalse
;
7059 m
->SendImmediateAnswers
= mDNSfalse
;
7060 m
->SleepState
= mDNSfalse
;
7062 // These fields only required for mDNS Searcher...
7063 m
->Questions
= mDNSNULL
;
7064 m
->NewQuestions
= mDNSNULL
;
7065 m
->CurrentQuestion
= mDNSNULL
;
7066 m
->LocalOnlyQuestions
= mDNSNULL
;
7067 m
->NewLocalOnlyQuestions
= mDNSNULL
;
7068 m
->rrcache_size
= 0;
7069 m
->rrcache_totalused
= 0;
7070 m
->rrcache_active
= 0;
7071 m
->rrcache_report
= 10;
7072 m
->rrcache_free
= mDNSNULL
;
7074 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++) m
->rrcache_hash
[slot
] = mDNSNULL
;
7076 mDNS_GrowCache_internal(m
, rrcachestorage
, rrcachesize
);
7078 // Fields below only required for mDNS Responder...
7079 m
->hostlabel
.c
[0] = 0;
7080 m
->nicelabel
.c
[0] = 0;
7081 m
->MulticastHostname
.c
[0] = 0;
7082 m
->HIHardware
.c
[0] = 0;
7083 m
->HISoftware
.c
[0] = 0;
7084 m
->ResourceRecords
= mDNSNULL
;
7085 m
->DuplicateRecords
= mDNSNULL
;
7086 m
->NewLocalRecords
= mDNSNULL
;
7087 m
->CurrentRecord
= mDNSNULL
;
7088 m
->HostInterfaces
= mDNSNULL
;
7089 m
->ProbeFailTime
= 0;
7090 m
->NumFailedProbes
= 0;
7091 m
->SuppressProbes
= 0;
7093 #ifndef UNICAST_DISABLED
7096 result
= mDNSPlatformInit(m
);
7101 mDNSexport
void mDNSCoreInitComplete(mDNS
*const m
, mStatus result
)
7103 m
->mDNSPlatformStatus
= result
;
7104 if (m
->MainCallback
)
7107 m
->mDNS_reentrancy
++; // Increment to allow client to legally make mDNS API calls from the callback
7108 m
->MainCallback(m
, mStatus_NoError
);
7109 m
->mDNS_reentrancy
--; // Decrement to block mDNS API calls again
7114 mDNSexport
void mDNS_Close(mDNS
*const m
)
7116 mDNSu32 rrcache_active
= 0;
7117 mDNSu32 rrcache_totalused
= 0;
7119 NetworkInterfaceInfo
*intf
;
7122 m
->mDNS_shutdown
= mDNStrue
;
7124 #ifndef UNICAST_DISABLED
7127 rrcache_totalused
= m
->rrcache_totalused
;
7128 for (slot
= 0; slot
< CACHE_HASH_SLOTS
; slot
++)
7130 while(m
->rrcache_hash
[slot
])
7132 CacheGroup
*cg
= m
->rrcache_hash
[slot
];
7135 CacheRecord
*rr
= cg
->members
;
7136 cg
->members
= cg
->members
->next
;
7137 if (rr
->CRActiveQuestion
) rrcache_active
++;
7138 ReleaseCacheRecord(m
, rr
);
7140 cg
->rrcache_tail
= &cg
->members
;
7141 ReleaseCacheGroup(m
, &m
->rrcache_hash
[slot
]);
7144 debugf("mDNS_Close: RR Cache was using %ld records, %lu active", rrcache_totalused
, rrcache_active
);
7145 if (rrcache_active
!= m
->rrcache_active
)
7146 LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active
, m
->rrcache_active
);
7148 for (intf
= m
->HostInterfaces
; intf
; intf
= intf
->next
)
7149 if (intf
->Advertise
)
7150 DeadvertiseInterface(m
, intf
);
7152 // Make sure there are nothing but deregistering records remaining in the list
7153 if (m
->CurrentRecord
) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
7154 m
->CurrentRecord
= m
->ResourceRecords
;
7155 while (m
->CurrentRecord
)
7157 AuthRecord
*rr
= m
->CurrentRecord
;
7158 if (rr
->resrec
.RecordType
!= kDNSRecordTypeDeregistering
)
7160 debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr
->resrec
.RecordType
, rr
->resrec
.name
->c
);
7161 mDNS_Deregister_internal(m
, rr
, mDNS_Dereg_normal
);
7164 m
->CurrentRecord
= rr
->next
;
7167 if (m
->ResourceRecords
) debugf("mDNS_Close: Sending final packets for deregistering records");
7168 else debugf("mDNS_Close: No deregistering records remain");
7170 // If any deregistering records remain, send their deregistration announcements before we exit
7171 if (m
->mDNSPlatformStatus
!= mStatus_NoError
) DiscardDeregistrations(m
);
7172 else if (m
->ResourceRecords
) SendResponses(m
);
7173 if (m
->ResourceRecords
) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m
, m
->ResourceRecords
));
7176 debugf("mDNS_Close: mDNSPlatformClose");
7177 mDNSPlatformClose(m
);
7178 debugf("mDNS_Close: done");