]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSCore/mDNS.c
mDNSResponder-107.tar.gz
[apple/mdnsresponder.git] / mDNSCore / mDNS.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
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
12 * file.
13 *
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.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 *
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.
26 *
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).
31
32 * Formatting notes:
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.)
44
45 Change History (most recent first):
46
47 $Log: mDNS.c,v $
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
50
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
53
54 Revision 1.520 2005/02/16 01:14:11 cheshire
55 Convert RR Cache LogOperation() calls to debugf()
56
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
59
60 Revision 1.518 2005/02/10 22:35:17 cheshire
61 <rdar://problem/3727944> Update name
62
63 Revision 1.517 2005/02/03 00:21:21 cheshire
64 Update comments about BIND named and zero-length TXT records
65
66 Revision 1.516 2005/01/28 06:06:32 cheshire
67 Update comment
68
69 Revision 1.515 2005/01/27 00:21:49 cheshire
70 <rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
71
72 Revision 1.514 2005/01/21 01:33:45 cheshire
73 <rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
74
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
77
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
81
82 Revision 1.511 2005/01/19 22:48:53 cheshire
83 <rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
84
85 Revision 1.510 2005/01/19 03:12:45 cheshire
86 Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
87
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
91
92 Revision 1.508 2005/01/18 18:56:32 cheshire
93 <rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
94
95 Revision 1.507 2005/01/18 01:12:07 cheshire
96 <rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
97
98 Revision 1.506 2005/01/17 23:28:53 cheshire
99 Fix compile error
100
101 Revision 1.505 2005/01/11 02:02:56 shersche
102 Move variable declaration to the beginning of statement block
103
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
106
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
109
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
112
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
116
117 Revision 1.500 2004/12/18 03:13:46 cheshire
118 <rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
119
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)
123
124 Revision 1.498 2004/12/17 05:25:46 cheshire
125 <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
126
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
129
130 Revision 1.496 2004/12/16 22:18:26 cheshire
131 Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
132
133 Revision 1.495 2004/12/16 21:27:37 ksekar
134 Fixed build failures when compiled with verbose debugging messages
135
136 Revision 1.494 2004/12/16 20:46:56 cheshire
137 Fix compiler warnings
138
139 Revision 1.493 2004/12/16 20:13:00 cheshire
140 <rdar://problem/3324626> Cache memory management improvements
141
142 Revision 1.492 2004/12/16 08:03:24 shersche
143 Fix compilation error when UNICAST_DISABLED is set
144
145 Revision 1.491 2004/12/11 01:52:11 cheshire
146 <rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
147
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
151
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
155
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
159
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
163
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.
168
169 Revision 1.485 2004/12/10 02:09:24 cheshire
170 <rdar://problem/3898376> Modify default TTLs
171
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
174
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
178
179 Revision 1.482 2004/12/07 22:49:06 cheshire
180 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
181
182 Revision 1.481 2004/12/07 21:26:04 ksekar
183 <rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
184
185 Revision 1.480 2004/12/07 20:42:33 cheshire
186 Add explicit context parameter to mDNS_RemoveRecordFromService()
187
188 Revision 1.479 2004/12/07 17:50:49 ksekar
189 <rdar://problem/3908850> BIND doesn't allow zero-length TXT records
190
191 Revision 1.478 2004/12/06 21:15:22 ksekar
192 <rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
193
194 Revision 1.477 2004/12/04 02:12:45 cheshire
195 <rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
196
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.
201
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.)
207
208 Revision 1.474 2004/11/29 17:18:12 cheshire
209 Remove "Unknown DNS packet type" message for update responses
210
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
213
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)
216
217 Revision 1.471 2004/11/25 01:10:13 cheshire
218 Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
219
220 Revision 1.470 2004/11/24 21:54:44 cheshire
221 <rdar://problem/3894475> mDNSCore not receiving unicast responses properly
222
223 Revision 1.469 2004/11/24 04:50:39 cheshire
224 Minor tidying
225
226 Revision 1.468 2004/11/24 01:47:07 cheshire
227 <rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
228
229 Revision 1.467 2004/11/24 01:41:28 cheshire
230 Rename CompleteProbing() to AcknowledgeRecord()
231
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
234
235 Revision 1.465 2004/11/15 20:09:21 ksekar
236 <rdar://problem/3719050> Wide Area support for Add/Remove record
237
238 Revision 1.464 2004/11/03 01:44:36 cheshire
239 Update debugging messages
240
241 Revision 1.463 2004/10/29 02:38:48 cheshire
242 Fix Windows compile errors
243
244 Revision 1.462 2004/10/28 19:21:07 cheshire
245 Guard against registering interface with zero InterfaceID
246
247 Revision 1.461 2004/10/28 19:02:16 cheshire
248 Remove \n from LogMsg() call
249
250 Revision 1.460 2004/10/28 03:24:40 cheshire
251 Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
252
253 Revision 1.459 2004/10/26 22:34:37 cheshire
254 <rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
255
256 Revision 1.458 2004/10/26 20:45:28 cheshire
257 Show mask in "invalid mask" message
258
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
261
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)
264
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
267
268 Revision 1.454 2004/10/23 01:16:00 cheshire
269 <rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
270
271 Revision 1.453 2004/10/22 20:52:06 ksekar
272 <rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
273
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
277
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
282
283 Revision 1.450 2004/10/19 17:42:59 ksekar
284 Fixed compiler warnings for non-debug builds.
285
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
288
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
291
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
294
295 Revision 1.446 2004/10/14 00:43:34 cheshire
296 <rdar://problem/3815984> Services continue to announce SRV and HINFO
297
298 Revision 1.445 2004/10/12 21:07:09 cheshire
299 Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
300
301 Revision 1.444 2004/10/11 17:54:16 ksekar
302 Changed hashtable pointer output from debugf to verbosedebugf.
303
304 Revision 1.443 2004/10/10 07:05:45 cheshire
305 For consistency, use symbol "localdomain" instead of literal string
306
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
309
310 Revision 1.441 2004/10/08 03:25:01 ksekar
311 <rdar://problem/3831716> domain enumeration should use LLQs
312
313 Revision 1.440 2004/10/06 01:44:19 cheshire
314 <rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
315
316 Revision 1.439 2004/10/03 23:14:11 cheshire
317 Add "mDNSEthAddr" type and "zeroEthAddr" constant
318
319 Revision 1.438 2004/09/29 23:07:04 cheshire
320 Patch from Pavel Repin to fix compile error on Windows
321
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
326
327 Revision 1.436 2004/09/28 01:27:04 cheshire
328 Update incorrect log message
329
330 Revision 1.435 2004/09/25 02:41:39 cheshire
331 <rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
332
333 Revision 1.434 2004/09/25 02:32:06 cheshire
334 Update comments
335
336 Revision 1.433 2004/09/25 02:24:27 cheshire
337 Removed unused rr->UseCount
338
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
342
343 Revision 1.431 2004/09/24 21:33:12 cheshire
344 Adjust comment
345
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
348
349 Revision 1.429 2004/09/24 00:20:21 cheshire
350 <rdar://problem/3483349> Any rrtype is a conflict for unique records
351
352 Revision 1.428 2004/09/24 00:12:25 cheshire
353 Get rid of unused RRUniqueOrKnownUnique(RR)
354
355 Revision 1.427 2004/09/23 20:44:11 cheshire
356 <rdar://problem/3813148> Reduce timeout before expiring records on failure
357
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.
362
363 Revision 1.425 2004/09/23 20:14:38 cheshire
364 Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
365
366 Revision 1.424 2004/09/23 00:58:36 cheshire
367 <rdar://problem/3781269> Rate limiting interferes with updating TXT records
368
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
371
372 Revision 1.422 2004/09/22 02:34:46 cheshire
373 Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
374
375 Revision 1.421 2004/09/21 23:29:49 cheshire
376 <rdar://problem/3680045> DNSServiceResolve should delay sending packets
377
378 Revision 1.420 2004/09/21 23:01:42 cheshire
379 Update debugf messages
380
381 Revision 1.419 2004/09/21 19:51:14 cheshire
382 Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
383
384 Revision 1.418 2004/09/21 18:40:17 cheshire
385 <rdar://problem/3376752> Adjust default record TTLs
386
387 Revision 1.417 2004/09/21 17:32:16 cheshire
388 <rdar://problem/3809484> Rate limiting imposed too soon
389
390 Revision 1.416 2004/09/20 23:52:01 cheshire
391 CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
392
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
395
396 Revision 1.414 2004/09/18 01:06:48 cheshire
397 Add comments
398
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.
404
405 Revision 1.412 2004/09/17 00:46:33 cheshire
406 mDNS_TimeNow should take const mDNS parameter
407
408 Revision 1.411 2004/09/17 00:31:51 cheshire
409 For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
410
411 Revision 1.410 2004/09/17 00:19:10 cheshire
412 For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
413
414 Revision 1.409 2004/09/16 21:59:15 cheshire
415 For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
416
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
420
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
424
425 Revision 1.406 2004/09/16 01:58:14 cheshire
426 Fix compiler warnings
427
428 Revision 1.405 2004/09/16 00:24:48 cheshire
429 <rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
430
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
434
435 Revision 1.403 2004/09/15 00:46:32 ksekar
436 Changed debugf to verbosedebugf in CheckCacheExpiration
437
438 Revision 1.402 2004/09/14 23:59:55 cheshire
439 <rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
440
441 Revision 1.401 2004/09/14 23:27:46 cheshire
442 Fix compile errors
443
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
450
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
453
454 Revision 1.398 2004/09/01 03:59:29 ksekar
455 <rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
456
457 Revision 1.397 2004/08/25 22:04:25 rpantos
458 Fix the standard Windows compile error.
459
460 Revision 1.396 2004/08/25 00:37:27 ksekar
461 <rdar://problem/3774635>: Cleanup DynDNS hostname registration code
462
463 Revision 1.395 2004/08/18 17:21:18 ksekar
464 Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
465
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
471
472 Revision 1.393 2004/08/13 23:42:52 cheshire
473 Removed unused "zeroDomainNamePtr"
474
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
478
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
482
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
485
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
489
490 Revision 1.388 2004/07/30 17:40:06 ksekar
491 <rdar://problem/3739115>: TXT Record updates not available for wide-area services
492
493 Revision 1.387 2004/07/26 22:49:30 ksekar
494 <rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
495
496 Revision 1.386 2004/07/13 21:24:24 rpantos
497 Fix for <rdar://problem/3701120>.
498
499 Revision 1.385 2004/06/18 19:09:59 cheshire
500 <rdar://problem/3588761> Current method of doing subtypes causes name collisions
501
502 Revision 1.384 2004/06/15 04:31:23 cheshire
503 Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
504
505 Revision 1.383 2004/06/11 00:04:59 cheshire
506 <rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
507
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
510
511 Revision 1.381 2004/06/05 00:57:30 cheshire
512 Remove incorrect LogMsg()
513
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
516
517 Revision 1.379 2004/05/28 23:42:36 ksekar
518 <rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
519
520 Revision 1.378 2004/05/25 17:25:25 cheshire
521 Remove extraneous blank lines and white space
522
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
525
526 Revision 1.376 2004/05/05 18:30:44 ksekar
527 Restored surpressed Cache Tail debug messages.
528
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
532
533 Revision 1.374 2004/04/21 02:53:26 cheshire
534 Typo in debugf statement
535
536 Revision 1.373 2004/04/21 02:49:11 cheshire
537 To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
538
539 Revision 1.372 2004/04/21 02:38:51 cheshire
540 Add debugging checks
541
542 Revision 1.371 2004/04/14 23:09:28 ksekar
543 Support for TSIG signed dynamic updates.
544
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
547
548 Revision 1.369 2004/04/09 16:34:00 cheshire
549 Debugging code for later; currently unused
550
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
553
554 Revision 1.367 2004/03/20 03:16:10 cheshire
555 Minor refinement to "Excessive update rate" message
556
557 Revision 1.366 2004/03/20 03:12:57 cheshire
558 <rdar://problem/3587619>: UpdateCredits not granted promptly enough
559
560 Revision 1.365 2004/03/19 23:51:22 cheshire
561 Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
562
563 Revision 1.364 2004/03/13 01:57:33 ksekar
564 <rdar://problem/3192546>: DynDNS: Dynamic update of service records
565
566 Revision 1.363 2004/03/12 21:00:51 cheshire
567 Also show port numbers when logging "apparent spoof mDNS Response" messages
568
569 Revision 1.362 2004/03/12 08:58:18 cheshire
570 Guard against empty TXT records
571
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.
574
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
577
578 Revision 1.359 2004/03/02 03:21:56 cheshire
579 <rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
580
581 Revision 1.358 2004/02/20 08:18:34 cheshire
582 <rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
583
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
586
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)
590
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.
594
595 Revision 1.354 2004/02/05 09:30:22 cheshire
596 Update comments
597
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
600
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.
605
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
608
609 Revision 1.350 2004/01/24 23:38:16 cheshire
610 Use mDNSVal16() instead of shifting and ORing operations
611
612 Revision 1.349 2004/01/23 23:23:14 ksekar
613 Added TCP support for truncated unicast messages.
614
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"
618
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
621
622 Revision 1.346 2004/01/22 03:48:41 cheshire
623 Make sure uDNS client doesn't accidentally use query ID zero
624
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
627
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
630
631 Revision 1.343 2003/12/23 00:07:47 cheshire
632 Make port number in debug message be five-character field, left justified
633
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.
638
639 Revision 1.341 2003/12/18 22:56:12 cheshire
640 <rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
641
642 Revision 1.340 2003/12/16 02:31:37 cheshire
643 Minor update to comments
644
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.
649
650 Revision 1.338 2003/12/13 03:05:27 ksekar
651 <rdar://problem/3192548>: DynDNS: Unicast query of service records
652
653 Revision 1.337 2003/12/01 21:46:05 cheshire
654 mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
655
656 Revision 1.336 2003/12/01 21:26:19 cheshire
657 Guard against zero-length sbuffer in mDNS_vsnprintf()
658
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
661
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.
665
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
668
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).
674
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
677
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())
681
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.
685
686 Revision 1.328 2003/11/19 22:06:38 cheshire
687 Show log messages when a service or hostname is renamed
688
689 Revision 1.327 2003/11/19 22:03:44 cheshire
690 Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
691
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.
695
696 Revision 1.325 2003/11/17 20:41:44 cheshire
697 Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
698
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.
702
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.
706
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
709
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
712
713 Revision 1.320 2003/11/13 06:45:04 cheshire
714 Fix compiler warning on certain compilers
715
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.
718
719 Revision 1.318 2003/11/13 00:33:26 cheshire
720 Change macro "RRIsAddressType" to "RRTypeIsAddressType"
721
722 Revision 1.317 2003/11/13 00:10:49 cheshire
723 <rdar://problem/3436412>: Verify that rr data is different before updating.
724
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.)
728
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.
735
736 Revision 1.314 2003/11/07 03:19:49 cheshire
737 Minor variable renaming for clarity
738
739 Revision 1.313 2003/11/07 03:14:49 cheshire
740 Previous checkin proved to be overly simplistic; reversing
741
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.)
746
747 Revision 1.311 2003/10/09 18:00:11 cheshire
748 Another compiler warning fix.
749
750 Revision 1.310 2003/10/07 20:27:05 cheshire
751 Patch from Bob Bradley, to fix warning and compile error on Windows
752
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
756
757 Revision 1.308 2003/09/23 01:05:01 cheshire
758 Minor changes to comments and debugf() message
759
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
764
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
768
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
771
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
774
775 Revision 1.303 2003/09/05 19:55:02 cheshire
776 <rdar://problem/3409533> Include address records when announcing SRV records
777
778 Revision 1.302 2003/09/05 00:01:36 cheshire
779 <rdar://problem/3407549> Don't accelerate queries that have large KA lists
780
781 Revision 1.301 2003/09/04 22:51:13 cheshire
782 <rdar://problem/3398213> Group probes and goodbyes better
783
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.
788
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();
792
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
796
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
802
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.
806
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
809
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
813
814 Revision 1.293 2003/08/27 02:25:31 cheshire
815 <rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
816
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
819
820 Revision 1.291 2003/08/21 18:57:44 cheshire
821 <rdar://problem/3387140> Synchronized queries on the network
822
823 Revision 1.290 2003/08/21 02:25:23 cheshire
824 Minor changes to comments and debugf() messages
825
826 Revision 1.289 2003/08/21 02:21:50 cheshire
827 <rdar://problem/3386473> Efficiency: Reduce repeated queries
828
829 Revision 1.288 2003/08/20 23:39:30 cheshire
830 <rdar://problem/3344098> Review syslog messages, and remove as appropriate
831
832 Revision 1.287 2003/08/20 20:47:18 cheshire
833 Fix compiler warning
834
835 Revision 1.286 2003/08/20 02:18:51 cheshire
836 <rdar://problem/3344098> Cleanup: Review syslog messages
837
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
841
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
845
846 Revision 1.283 2003/08/19 22:16:27 cheshire
847 Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
848
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.
856
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.
862
863 Revision 1.280 2003/08/19 02:33:36 cheshire
864 Update comments
865
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.
870
871 Revision 1.278 2003/08/18 22:53:37 cheshire
872 <rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
873
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
877
878 Revision 1.276 2003/08/16 03:39:00 cheshire
879 <rdar://problem/3338440> InterfaceID -1 indicates "local only"
880
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
884
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.
892
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.
900
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
904
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
907
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.
911
912 Revision 1.269 2003/08/12 19:56:23 cheshire
913 Update to APSL 2.0
914
915 Revision 1.268 2003/08/12 15:01:10 cheshire
916 Add comments
917
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.
922
923 Revision 1.266 2003/08/12 12:47:16 cheshire
924 In mDNSCoreMachineSleep debugf message, display value of m->timenow
925
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
928
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.
932
933 Revision 1.263 2003/08/09 00:35:29 cheshire
934 Moved AnswerNewQuestion() later in the file, in preparation for next checkin
935
936 Revision 1.262 2003/08/08 19:50:33 cheshire
937 <rdar://problem/3370332> Remove "Cache size now xxx" messages
938
939 Revision 1.261 2003/08/08 19:18:45 cheshire
940 <rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
941
942 Revision 1.260 2003/08/08 18:55:48 cheshire
943 <rdar://problem/3370365> Guard against time going backwards
944
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
947
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
951
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)
954
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
957
958 Revision 1.255 2003/08/06 23:22:50 cheshire
959 Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
960
961 Revision 1.254 2003/08/06 21:33:39 cheshire
962 Fix compiler warnings on PocketPC 2003 (Windows CE)
963
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()
967
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
971
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
975
976 Revision 1.250 2003/08/06 19:01:55 cheshire
977 Update comments
978
979 Revision 1.249 2003/08/06 00:13:28 cheshire
980 Tidy up debugf messages
981
982 Revision 1.248 2003/08/05 22:20:15 cheshire
983 <rdar://problem/3330324> Need to check IP TTL on responses
984
985 Revision 1.247 2003/08/05 00:56:39 cheshire
986 <rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
987
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
990
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
993
994 Revision 1.244 2003/08/01 23:55:32 cheshire
995 Fix for compiler warnings on Windows, submitted by Bob Bradley
996
997 Revision 1.243 2003/07/25 02:26:09 cheshire
998 Typo: FIxed missing semicolon
999
1000 Revision 1.242 2003/07/25 01:18:41 cheshire
1001 Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
1002
1003 Revision 1.241 2003/07/23 21:03:42 cheshire
1004 Only show "Found record..." debugf message in verbose mode
1005
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.
1009
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
1012
1013 Revision 1.238 2003/07/22 00:10:20 cheshire
1014 <rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
1015
1016 Revision 1.237 2003/07/19 03:23:13 cheshire
1017 <rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
1018
1019 Revision 1.236 2003/07/19 03:04:55 cheshire
1020 Fix warnings; some debugf message improvements
1021
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.
1026
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
1031
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
1034
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)
1038
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->"
1041
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
1045
1046 Revision 1.229 2003/07/17 17:35:04 cheshire
1047 <rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
1048
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
1051
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
1055
1056 Revision 1.226 2003/07/16 04:51:44 cheshire
1057 Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
1058
1059 Revision 1.225 2003/07/16 04:46:41 cheshire
1060 Minor wording cleanup: The correct DNS term is "response", not "reply"
1061
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)"
1065
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)"
1072
1073 Revision 1.222 2003/07/15 23:40:46 cheshire
1074 Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
1075
1076 Revision 1.221 2003/07/15 22:17:56 cheshire
1077 <rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
1078
1079 Revision 1.220 2003/07/15 02:12:51 cheshire
1080 Slight tidy-up of debugf messages and comments
1081
1082 Revision 1.219 2003/07/15 01:55:12 cheshire
1083 <rdar://problem/3315777> Need to implement service registration with subtypes
1084
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.
1091
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
1095
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
1099
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
1103
1104 Revision 1.214 2003/07/13 01:47:53 cheshire
1105 Fix one error and one warning in the Windows build
1106
1107 Revision 1.213 2003/07/12 04:25:48 cheshire
1108 Fix minor signed/unsigned warnings
1109
1110 Revision 1.212 2003/07/12 01:59:11 cheshire
1111 Minor changes to debugf messages
1112
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
1115
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.
1121
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()
1125
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".
1129
1130 Revision 1.207 2003/07/11 01:28:00 cheshire
1131 <rdar://problem/3161289> No more local.arpa
1132
1133 Revision 1.206 2003/07/11 00:45:02 cheshire
1134 <rdar://problem/3321909> Client should get callback confirming successful host name registration
1135
1136 Revision 1.205 2003/07/11 00:40:18 cheshire
1137 Tidy up debug message in HostNameCallback()
1138
1139 Revision 1.204 2003/07/11 00:20:32 cheshire
1140 <rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
1141
1142 Revision 1.203 2003/07/10 23:53:41 cheshire
1143 <rdar://problem/3320079> Hostname conflict naming should not use two hyphens
1144
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.
1148
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
1152
1153 Revision 1.200 2003/07/03 23:55:26 cheshire
1154 Minor change to wording of syslog warning messages
1155
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
1159
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.
1163
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
1166 Minor refinements:
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
1169
1170 Revision 1.196 2003/07/02 21:19:45 cheshire
1171 <rdar://problem/3313413> Update copyright notices, etc., in source code comments
1172
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
1177
1178 Revision 1.194 2003/07/02 18:47:40 cheshire
1179 Minor wording change to log messages
1180
1181 Revision 1.193 2003/07/02 02:44:13 cheshire
1182 Fix warning in non-debug build
1183
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
1186
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.
1189
1190 Revision 1.190 2003/06/27 00:03:05 vlubet
1191 <rdar://problem/3304625> Merge of build failure fix for gcc 3.3
1192
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
1196
1197 Revision 1.188 2003/06/10 20:33:28 cheshire
1198 <rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
1199
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.
1203
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
1209
1210 Revision 1.185 2003/06/10 03:52:49 cheshire
1211 Update comments and debug messages
1212
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
1216
1217 Revision 1.183 2003/06/09 18:53:13 cheshire
1218 Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
1219
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.
1223
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
1226
1227 Revision 1.180 2003/06/07 06:31:07 cheshire
1228 Create little four-line helper function "FindIdenticalRecordInCache()"
1229
1230 Revision 1.179 2003/06/07 06:28:13 cheshire
1231 For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
1232
1233 Revision 1.178 2003/06/07 06:25:12 cheshire
1234 Update some comments
1235
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
1238
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()
1243
1244 Revision 1.175 2003/06/07 04:11:52 cheshire
1245 Minor changes to comments and debug messages
1246
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
1249
1250 Revision 1.173 2003/06/07 01:22:13 cheshire
1251 <rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
1252
1253 Revision 1.172 2003/06/07 00:59:42 cheshire
1254 <rdar://problem/3283454> Need some randomness to spread queries on the network
1255
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
1258
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.)
1262
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)
1266
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"
1269
1270 Revision 1.167 2003/06/06 21:30:42 cheshire
1271 <rdar://problem/3282962> Don't delay queries for shared record types
1272
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.)
1276
1277 Revision 1.165 2003/06/04 02:53:21 cheshire
1278 Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
1279
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
1283
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.
1288
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
1292
1293 Revision 1.161 2003/06/03 18:29:03 cheshire
1294 Minor changes to comments and debugf() messages
1295
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
1298
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
1301
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()
1305
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
1308
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
1312
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().
1318
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]
1322
1323 Revision 1.153 2003/05/30 19:10:56 cheshire
1324 <rdar://problem/3274153> ConstructServiceName needs to be more restrictive
1325
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
1328
1329 Revision 1.151 2003/05/29 06:35:42 cheshire
1330 <rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
1331
1332 Revision 1.150 2003/05/29 06:25:45 cheshire
1333 <rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
1334
1335 Revision 1.149 2003/05/29 06:18:39 cheshire
1336 <rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
1337
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
1340
1341 Revision 1.147 2003/05/29 06:01:18 cheshire
1342 Change some debugf() calls to LogMsg() calls to help with debugging
1343
1344 Revision 1.146 2003/05/28 21:00:44 cheshire
1345 Re-enable "immediate answer burst" debugf message
1346
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.
1351
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
1355
1356 Revision 1.143 2003/05/28 04:31:29 cheshire
1357 <rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
1358
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
1362
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.
1366
1367 Revision 1.140 2003/05/28 01:55:24 cheshire
1368 Minor change to log messages
1369
1370 Revision 1.139 2003/05/28 01:52:51 cheshire
1371 <rdar://problem/3270634> Misleading messages generated by iChat
1372
1373 Revision 1.138 2003/05/27 22:35:00 cheshire
1374 <rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
1375
1376 Revision 1.137 2003/05/27 20:04:33 cheshire
1377 <rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
1378
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
1381
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
1384
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
1388
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
1394
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
1397
1398 Revision 1.131 2003/05/26 00:42:05 cheshire
1399 <rdar://problem/3268876> Temporarily include mDNSResponder version in packets
1400
1401 Revision 1.130 2003/05/24 16:39:48 cheshire
1402 <rdar://problem/3268631> SendResponses also needs to handle multihoming better
1403
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.)
1409
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
1412
1413 Revision 1.127 2003/05/23 01:02:15 ksekar
1414 <rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
1415
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 :-)
1419
1420 Revision 1.125 2003/05/22 01:50:45 cheshire
1421 Fix warnings, and improve log messages
1422
1423 Revision 1.124 2003/05/22 01:41:50 cheshire
1424 DiscardDeregistrations doesn't need InterfaceID parameter
1425
1426 Revision 1.123 2003/05/22 01:38:55 cheshire
1427 Change bracketing of #pragma mark
1428
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
1432
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)"
1436
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
1439
1440 Revision 1.119 2003/05/16 01:34:10 cheshire
1441 Fix some warnings
1442
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
1448
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.
1456
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
1459
1460 Revision 1.115 2003/05/14 06:44:31 cheshire
1461 Improve debugging message
1462
1463 Revision 1.114 2003/05/07 01:47:03 cheshire
1464 <rdar://problem/3250330> Also protect against NULL domainlabels
1465
1466 Revision 1.113 2003/05/07 00:28:18 cheshire
1467 <rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
1468
1469 Revision 1.112 2003/05/06 00:00:46 cheshire
1470 <rdar://problem/3248914> Rationalize naming of domainname manipulation functions
1471
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"
1476
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
1479
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
1482
1483 Revision 1.108 2003/04/25 01:45:56 cheshire
1484 <rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
1485
1486 Revision 1.107 2003/04/25 00:41:31 cheshire
1487 <rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
1488
1489 Revision 1.106 2003/04/22 03:14:45 cheshire
1490 <rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
1491
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
1495
1496 Revision 1.104 2003/04/21 19:15:52 cheshire
1497 Fix some compiler warnings
1498
1499 Revision 1.103 2003/04/19 02:26:35 cheshire
1500 <rdar://problem/3233804> Incorrect goodbye packet after conflict
1501
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
1505
1506 Revision 1.101 2003/04/15 20:58:31 jgraessl
1507 <rdar://problem/3229014> Added a hash to lookup records in the cache.
1508
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.
1512
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.
1518
1519 Revision 1.98 2003/04/03 03:43:55 cheshire
1520 <rdar://problem/3216837> Off-by-one error in probe rate limiting
1521
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
1526
1527 Revision 1.96 2003/04/01 23:58:55 cheshire
1528 Minor comment changes
1529
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.
1536
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
1540
1541 Revision 1.93 2003/03/28 01:54:36 cheshire
1542 Minor tidyup of IPv6 (AAAA) code
1543
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
1547 Fixes:
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)
1551
1552 Revision 1.91 2003/03/15 04:40:36 cheshire
1553 Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
1554
1555 Revision 1.90 2003/03/14 20:26:37 cheshire
1556 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1557
1558 Revision 1.89 2003/03/12 19:57:50 cheshire
1559 Fixed typo in debug message
1560
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
1563
1564 Revision 1.87 2003/03/11 01:27:20 cheshire
1565 Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
1566
1567 Revision 1.86 2003/03/06 20:44:33 cheshire
1568 Comment tidyup
1569
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
1573
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
1577
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
1581
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
1586
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
1589
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
1595
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")
1599
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
1603
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
1608
1609 Revision 1.76 2003/01/29 02:46:37 cheshire
1610 Fix for IPv6:
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.
1615
1616 Revision 1.75 2003/01/29 01:47:40 cheshire
1617 Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
1618
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
1622
1623 Revision 1.73 2003/01/28 03:45:12 cheshire
1624 Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
1625
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.
1630
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()
1636
1637 Revision 1.70 2003/01/23 19:00:20 cheshire
1638 Protect against infinite loops in mDNS_Execute
1639
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
1645 branch for SU.
1646
1647 Revision 1.68 2003/01/17 04:09:27 cheshire
1648 <rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
1649
1650 Revision 1.67 2003/01/17 03:56:45 cheshire
1651 Default 24-hour TTL is far too long. Changing to two hours.
1652
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
1658
1659 Revision 1.65 2002/12/23 22:13:28 jgraessl
1660 Reviewed by: Stuart Cheshire
1661 Initial IPv6 support for mDNSResponder.
1662
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
1665
1666 Revision 1.63 2002/09/21 20:44:49 zarzycki
1667 Added APSL info
1668
1669 Revision 1.62 2002/09/20 03:25:37 cheshire
1670 Fix some compiler warnings
1671
1672 Revision 1.61 2002/09/20 01:05:24 cheshire
1673 Don't kill the Extras list in mDNS_DeregisterService()
1674
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
1678
1679 Revision 1.59 2002/09/19 21:25:34 cheshire
1680 mDNS_snprintf() doesn't need to be in a separate file
1681
1682 Revision 1.58 2002/09/19 04:20:43 cheshire
1683 Remove high-ascii characters that confuse some systems
1684
1685 Revision 1.57 2002/09/17 01:07:08 cheshire
1686 Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
1687
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
1690 */
1691
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)
1700
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)
1707 #endif
1708
1709 // ***************************************************************************
1710 #if COMPILER_LIKES_PRAGMA_MARK
1711 #pragma mark -
1712 #pragma mark - Program Constants
1713 #endif
1714
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 }}} };
1722
1723 mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
1724 mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1;
1725
1726 mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0;
1727
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 } } } };
1737
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 } };
1743
1744 // Any records bigger than this are considered 'large' records
1745 #define SmallRecordLimit 1024
1746
1747 #define kMaxUpdateCredits 10
1748 #define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
1749
1750 mDNSexport const char *const mDNS_DomainTypeNames[] =
1751 {
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
1757 };
1758
1759 #ifdef UNICAST_DISABLED
1760 #define uDNS_IsActiveQuery(q, u) mDNSfalse
1761 #endif
1762
1763 // ***************************************************************************
1764 #if COMPILER_LIKES_PRAGMA_MARK
1765 #pragma mark -
1766 #pragma mark - Specialized mDNS version of vsnprintf
1767 #endif
1768
1769 static const struct mDNSprintf_format
1770 {
1771 unsigned leftJustify : 1;
1772 unsigned forceSign : 1;
1773 unsigned zeroPad : 1;
1774 unsigned havePrecision : 1;
1775 unsigned hSize : 1;
1776 unsigned lSize : 1;
1777 char altForm;
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 };
1782
1783 mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
1784 {
1785 mDNSu32 nwritten = 0;
1786 int c;
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;
1790
1791 for (c = *fmt; c != 0; c = *++fmt)
1792 {
1793 if (c != '%')
1794 {
1795 *sbuffer++ = (char)c;
1796 if (++nwritten >= buflen) goto exit;
1797 }
1798 else
1799 {
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;
1810
1811 while (1) // decode flags
1812 {
1813 c = *++fmt;
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;
1819 else break;
1820 }
1821
1822 if (c == '*') // decode field width
1823 {
1824 int f = va_arg(arg, int);
1825 if (f < 0) { f = -f; F.leftJustify = 1; }
1826 F.fieldWidth = (unsigned int)f;
1827 c = *++fmt;
1828 }
1829 else
1830 {
1831 for (; c >= '0' && c <= '9'; c = *++fmt)
1832 F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
1833 }
1834
1835 if (c == '.') // decode precision
1836 {
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;
1842 }
1843
1844 if (F.leftJustify) F.zeroPad = 0;
1845
1846 conv:
1847 switch (c) // perform appropriate conversion
1848 {
1849 unsigned long n;
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;
1853 case 'd' :
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 = '+';
1859 goto decimal;
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;
1863 F.sign = 0;
1864 goto decimal;
1865 decimal: if (!F.havePrecision)
1866 {
1867 if (F.zeroPad)
1868 {
1869 F.precision = F.fieldWidth;
1870 if (F.sign) --F.precision;
1871 }
1872 if (F.precision < 1) F.precision = 1;
1873 }
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++; }
1879 break;
1880
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)
1885 {
1886 if (F.zeroPad) F.precision = F.fieldWidth;
1887 if (F.precision < 1) F.precision = 1;
1888 }
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';
1894 break;
1895
1896 case 'a' : {
1897 unsigned char *a = va_arg(arg, unsigned char *);
1898 if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
1899 else
1900 {
1901 s = mDNS_VACB; // Adjust s to point to the start of the buffer, not the end
1902 if (F.altForm)
1903 {
1904 mDNSAddr *ip = (mDNSAddr*)a;
1905 switch (ip->type)
1906 {
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;
1910 }
1911 }
1912 switch (F.precision)
1913 {
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;
1924 }
1925 }
1926 }
1927 break;
1928
1929 case 'p' : F.havePrecision = F.lSize = 1;
1930 F.precision = 8;
1931 case 'X' : digits = "0123456789ABCDEF";
1932 goto hexadecimal;
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)
1938 {
1939 if (F.zeroPad)
1940 {
1941 F.precision = F.fieldWidth;
1942 if (F.altForm) F.precision -= 2;
1943 }
1944 if (F.precision < 1) F.precision = 1;
1945 }
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; }
1951 break;
1952
1953 case 'c' : *--s = (char)va_arg(arg, int); i = 1; break;
1954
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)
1958 {
1959 case 0: i=0;
1960 if (!F.havePrecision) // C string
1961 while(s[i]) i++;
1962 else
1963 {
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
1977 {
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;
1985 }
1986 }
1987 break;
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
1993 while (*a)
1994 {
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);
1998 a += 1 + *a;
1999 }
2000 i = (mDNSu32)(s - mDNS_VACB);
2001 s = mDNS_VACB; // Reset s back to the start of the buffer
2002 break;
2003 }
2004 }
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--; }
2008 break;
2009
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;
2014 continue;
2015
2016 default: s = mDNS_VACB;
2017 i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
2018
2019 case '%' : *sbuffer++ = (char)c;
2020 if (++nwritten >= buflen) goto exit;
2021 break;
2022 }
2023
2024 if (i < F.fieldWidth && !F.leftJustify) // Pad on the left
2025 do {
2026 *sbuffer++ = ' ';
2027 if (++nwritten >= buflen) goto exit;
2028 } while (i < --F.fieldWidth);
2029
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
2037 nwritten += i;
2038 if (nwritten >= buflen) goto exit;
2039
2040 for (; i < F.fieldWidth; i++) // Pad on the right
2041 {
2042 *sbuffer++ = ' ';
2043 if (++nwritten >= buflen) goto exit;
2044 }
2045 }
2046 }
2047 exit:
2048 *sbuffer++ = 0;
2049 return(nwritten);
2050 }
2051
2052 mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
2053 {
2054 mDNSu32 length;
2055
2056 va_list ptr;
2057 va_start(ptr,fmt);
2058 length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
2059 va_end(ptr);
2060
2061 return(length);
2062 }
2063
2064 // ***************************************************************************
2065 #if COMPILER_LIKES_PRAGMA_MARK
2066 #pragma mark -
2067 #pragma mark - General Utility Functions
2068 #endif
2069
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)
2073
2074 mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
2075 {
2076 if (ActiveQuestion(q))
2077 if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0)
2078 m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval);
2079 }
2080
2081 mDNSlocal CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
2082 {
2083 CacheGroup *cg;
2084 for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
2085 if (cg->namehash == namehash && SameDomainName(cg->name, name))
2086 break;
2087 return(cg);
2088 }
2089
2090 mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
2091 {
2092 return(CacheGroupForName(m, slot, rr->namehash, rr->name));
2093 }
2094
2095 mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
2096 {
2097 NetworkInterfaceInfo *intf;
2098
2099 if (addr->type == mDNSAddrType_IPv4)
2100 {
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)
2105 return(mDNStrue);
2106 }
2107
2108 if (addr->type == mDNSAddrType_IPv6)
2109 {
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))
2117 return(mDNStrue);
2118 }
2119
2120 return(mDNSfalse);
2121 }
2122
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)
2127 {
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);
2134
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
2145
2146 if (RDataStorage)
2147 rr->resrec.rdata = RDataStorage;
2148 else
2149 {
2150 rr->resrec.rdata = &rr->rdatastorage;
2151 rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
2152 }
2153
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;
2161
2162 rr->HostTarget = mDNSfalse;
2163 rr->AllowRemoteQuery = mDNSfalse;
2164 rr->ForceMCast = mDNSfalse;
2165
2166 // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
2167
2168 rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
2169 }
2170
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)
2174 {
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
2181 }
2182
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)
2188 {
2189 if (m->CurrentQuestion) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
2190
2191 m->CurrentQuestion = m->LocalOnlyQuestions;
2192 while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
2193 {
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
2198 }
2199
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)
2202 {
2203 m->CurrentQuestion = m->Questions;
2204 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
2205 {
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
2210 }
2211 }
2212
2213 m->CurrentQuestion = mDNSNULL;
2214 }
2215
2216 // ***************************************************************************
2217 #if COMPILER_LIKES_PRAGMA_MARK
2218 #pragma mark -
2219 #pragma mark - Resource Record Utility Functions
2220 #endif
2221
2222 #define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
2223
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)) )
2228
2229 #define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
2230 (ResourceRecordIsValidAnswer(RR) && \
2231 ((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
2232
2233 #define DefaultProbeCountForTypeUnique ((mDNSu8)3)
2234 #define DefaultProbeCountForRecordType(X) ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
2235
2236 #define InitialAnnounceCount ((mDNSu8)10)
2237
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)
2245
2246 #define DefaultAPIntervalForRecordType(X) ((X) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
2247 (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
2248 (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? DefaultAnnounceIntervalForTypeUnique : 0)
2249
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))
2254
2255 #define MaxUnansweredQueries 4
2256
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)
2264 {
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 &&
2268 r2->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));
2271 }
2272
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)
2279 {
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));
2287 }
2288
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)
2292 {
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));
2297 }
2298
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)
2307 {
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);
2310
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);
2321 }
2322
2323 mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
2324 {
2325 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2326 {
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);
2330 }
2331 else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr))
2332 {
2333 if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
2334 m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
2335 }
2336 }
2337
2338 mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
2339 {
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)
2346
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)
2349 {
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;
2357 }
2358
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;
2368
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;
2378
2379 SetNextAnnounceProbeTime(m, rr);
2380 }
2381
2382 #define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
2383
2384 mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
2385 {
2386 domainname *target = GetRRDomainNameTarget(&rr->resrec);
2387
2388 if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype);
2389
2390 if (target && SameDomainName(target, &m->MulticastHostname))
2391 debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
2392
2393 if (target && !SameDomainName(target, &m->MulticastHostname))
2394 {
2395 AssignDomainName(target, &m->MulticastHostname);
2396 SetNewRData(&rr->resrec, mDNSNULL, 0);
2397
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);
2402
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));
2408
2409 rr->AnnounceCount = InitialAnnounceCount;
2410 rr->RequireGoodbye = mDNSfalse;
2411 rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
2412 InitializeLastAPTime(m,rr);
2413 }
2414 }
2415
2416 mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
2417 {
2418 if (!rr->Acknowledged && rr->RecordCallback)
2419 {
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
2426 }
2427 }
2428
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))
2432
2433 mDNSlocal mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
2434 {
2435 domainname *target = GetRRDomainNameTarget(&rr->resrec);
2436 AuthRecord *r;
2437 AuthRecord **p = &m->ResourceRecords;
2438 AuthRecord **d = &m->DuplicateRecords;
2439
2440 mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo));
2441
2442 if ((mDNSs32)rr->resrec.rroriginalttl <= 0)
2443 { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
2444
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);
2449 #endif
2450
2451 while (*p && *p != rr) p=&(*p)->next;
2452 while (*d && *d != rr) d=&(*d)->next;
2453 if (*d || *p)
2454 {
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);
2457 }
2458
2459 if (rr->DependentOn)
2460 {
2461 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
2462 rr->resrec.RecordType = kDNSRecordTypeVerified;
2463 else
2464 {
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);
2468 }
2469 if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified)))
2470 {
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);
2474 }
2475 }
2476
2477 // If this resource record is referencing a specific interface, make sure it exists
2478 if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
2479 {
2480 NetworkInterfaceInfo *intf;
2481 for (intf = m->HostInterfaces; intf; intf = intf->next)
2482 if (intf->InterfaceID == rr->resrec.InterfaceID) break;
2483 if (!intf)
2484 {
2485 debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID);
2486 return(mStatus_BadReferenceErr);
2487 }
2488 }
2489
2490 rr->next = mDNSNULL;
2491
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;
2504
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;
2533
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
2540
2541 if (rr->HostTarget)
2542 SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
2543 else
2544 {
2545 rr->resrec.rdlength = GetRDLength(&rr->resrec, mDNSfalse);
2546 rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
2547 }
2548
2549 if (!ValidateDomainName(rr->resrec.name))
2550 { LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
2551
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; }
2556
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); }
2560
2561 rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
2562 rr->resrec.rdatahash = target ? DomainNameHashValue(target) : RDataHashValue(rr->resrec.rdlength, &rr->resrec.rdata->u);
2563
2564 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
2565 {
2566 // If this is supposed to be unique, make sure we don't have any name conflicts
2567 if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
2568 {
2569 const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
2570 for (r = m->ResourceRecords; r; r=r->next)
2571 {
2572 const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
2573 if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec))
2574 break;
2575 }
2576 if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
2577 {
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;
2583 }
2584 }
2585 }
2586
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;
2589
2590 if (r)
2591 {
2592 debugf("Adding to duplicate list %p %s", rr, ARDisplayString(m,rr));
2593 *d = 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)
2599 rr->ProbeCount = 0;
2600 }
2601 else
2602 {
2603 debugf("Adding to active record list %p %s", rr, ARDisplayString(m,rr));
2604 if (!m->NewLocalRecords) m->NewLocalRecords = rr;
2605 *p = rr;
2606 }
2607
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);
2611
2612 return(mStatus_NoError);
2613 }
2614
2615 mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
2616 {
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)
2627 {
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));
2631 }
2632 }
2633
2634 mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr)
2635 {
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
2641 }
2642
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;
2647
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)
2651 {
2652 AuthRecord *r2;
2653 mDNSu8 RecordType = rr->resrec.RecordType;
2654 AuthRecord **p = &m->ResourceRecords; // Find this record in our list of active records
2655
2656 #ifndef UNICAST_DISABLED
2657 if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)))
2658 return uDNS_DeregisterRecord(m, rr);
2659 #endif
2660
2661 while (*p && *p != rr) p=&(*p)->next;
2662
2663 if (*p)
2664 {
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
2667 {
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;
2671 }
2672 else
2673 {
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;
2678 if (*d)
2679 {
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;
2700 }
2701 }
2702 }
2703 else
2704 {
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));
2711 }
2712
2713 if (!*p)
2714 {
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);
2719 }
2720
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)
2724 {
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);
2731 }
2732 else
2733 {
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;
2739
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));
2746 else
2747 {
2748 verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2749 rr->resrec.RecordType = kDNSRecordTypeUnregistered;
2750 }
2751
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));
2755
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
2758
2759 if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse);
2760
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)
2767 {
2768 if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this
2769 }
2770 else
2771 {
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;
2778 while (r2)
2779 {
2780 if (r2->ProbeCount != 0xFF) r2 = r2->next;
2781 else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; }
2782 }
2783 }
2784 m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
2785 }
2786 return(mStatus_NoError);
2787 }
2788
2789 // ***************************************************************************
2790 #if COMPILER_LIKES_PRAGMA_MARK
2791 #pragma mark -
2792 #pragma mark -
2793 #pragma mark - Packet Sending Functions
2794 #endif
2795
2796 mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add)
2797 {
2798 if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse)
2799 {
2800 **nrpp = rr;
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;
2807 }
2808 debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
2809 }
2810
2811 mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID)
2812 {
2813 AuthRecord *rr, *rr2;
2814 for (rr=ResponseRecords; rr; rr=rr->NextResponse) // For each record we plan to put
2815 {
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);
2820
2821 if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID))
2822 AddRecordToResponseList(nrpp, rr->Additional2, rr);
2823
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);
2832 }
2833 }
2834
2835 mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID)
2836 {
2837 AuthRecord *rr;
2838 AuthRecord *ResponseRecords = mDNSNULL;
2839 AuthRecord **nrp = &ResponseRecords;
2840
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)
2843 {
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;
2849
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)))
2853 {
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; }
2860 }
2861 }
2862
2863 AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
2864
2865 while (ResponseRecords)
2866 {
2867 mDNSu8 *responseptr = m->omsg.data;
2868 mDNSu8 *newptr;
2869 InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
2870
2871 // Put answers in the packet
2872 while (ResponseRecords && ResponseRecords->NR_AnswerTo)
2873 {
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;
2886 }
2887
2888 // Add additionals, if there's space
2889 while (ResponseRecords && !ResponseRecords->NR_AnswerTo)
2890 {
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
2896
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;
2904 }
2905
2906 if (m->omsg.h.numAnswers) mDNSSendDNSMessage(m, &m->omsg, responseptr, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL);
2907 }
2908 }
2909
2910 mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *rr)
2911 {
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
2917 }
2918
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)
2923 {
2924 if (m->CurrentRecord) LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set");
2925 m->CurrentRecord = m->ResourceRecords;
2926
2927 while (m->CurrentRecord)
2928 {
2929 AuthRecord *rr = m->CurrentRecord;
2930 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
2931 CompleteDeregistration(m, rr); // Don't touch rr after this
2932 else
2933 m->CurrentRecord = rr->next;
2934 }
2935 }
2936
2937 mDNSlocal void GrantUpdateCredit(AuthRecord *rr)
2938 {
2939 if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0;
2940 else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
2941 }
2942
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.
2955
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)
2960 {
2961 int pktcount = 0;
2962 AuthRecord *rr, *r2;
2963 mDNSs32 maxExistingAnnounceInterval = 0;
2964 const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
2965
2966 m->NextScheduledResponse = m->timenow + 0x78000000;
2967
2968 for (rr = m->ResourceRecords; rr; rr=rr->next)
2969 if (rr->ImmedUnicast)
2970 {
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)
2978 {
2979 LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr));
2980 rr->ImmedUnicast = mDNSfalse;
2981 }
2982 }
2983
2984 // ***
2985 // *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
2986 // ***
2987
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)
2990 {
2991 while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
2992 if (TimeToAnnounceThisRecord(rr, m->timenow) && ResourceRecordIsValidAnswer(rr))
2993 {
2994 rr->ImmedAnswer = mDNSInterfaceMark; // Send on all interfaces
2995 if (maxExistingAnnounceInterval < rr->ThisAPInterval)
2996 maxExistingAnnounceInterval = rr->ThisAPInterval;
2997 if (rr->UpdateBlocked) rr->UpdateBlocked = 0;
2998 }
2999 }
3000
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
3009
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
3024
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)
3033 {
3034 if (rr->ImmedAnswer) // If we're sending this as answer, see that its whole RRSet is similarly marked
3035 {
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;
3040 }
3041 else if (rr->ImmedAdditional) // If we're sending this as additional, see that its whole RRSet is similarly marked
3042 {
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;
3047 }
3048 }
3049
3050 // Now set SendRNow state appropriately
3051 for (rr = m->ResourceRecords; rr; rr=rr->next)
3052 {
3053 if (rr->ImmedAnswer == mDNSInterfaceMark) // Sending this record on all appropriate interfaces
3054 {
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))
3061 {
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);
3067 }
3068 }
3069 else if (rr->ImmedAnswer) // Else, just respond to a single query on single interface:
3070 {
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;
3075 }
3076 SetNextAnnounceProbeTime(m, rr);
3077 //if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
3078 }
3079
3080 // ***
3081 // *** 2. Loop through interface list, sending records as appropriate
3082 // ***
3083
3084 while (intf)
3085 {
3086 int numDereg = 0;
3087 int numAnnounce = 0;
3088 int numAnswer = 0;
3089 mDNSu8 *responseptr = m->omsg.data;
3090 mDNSu8 *newptr;
3091 InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
3092
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)
3103 {
3104 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3105 {
3106 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3107 if (!newptr && m->omsg.h.numAnswers) break;
3108 numDereg++;
3109 responseptr = newptr;
3110 }
3111 else if (rr->NewRData) // If we have new data for this record
3112 {
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)
3117 {
3118 newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
3119 if (!newptr && m->omsg.h.numAnswers) break;
3120 numDereg++;
3121 responseptr = newptr;
3122 }
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);
3131 }
3132 else
3133 {
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;
3142 }
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);
3146 else
3147 rr->SendRNow = mDNSNULL;
3148 }
3149
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))
3155 {
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);
3158
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))
3163 {
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; }
3169 }
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
3173 {
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
3178 if (newptr)
3179 {
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;
3189 }
3190 }
3191 }
3192
3193 if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals)
3194 {
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.
3205 }
3206 else // Nothing more to send on this interface; go to next
3207 {
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);
3212 #endif
3213 intf = next;
3214 }
3215 }
3216
3217 // ***
3218 // *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
3219 // ***
3220
3221 if (m->CurrentRecord) LogMsg("SendResponses: ERROR m->CurrentRecord already set");
3222 m->CurrentRecord = m->ResourceRecords;
3223 while (m->CurrentRecord)
3224 {
3225 rr = m->CurrentRecord;
3226 m->CurrentRecord = rr->next;
3227
3228 if (rr->SendRNow)
3229 {
3230 if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
3231 LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr));
3232 rr->SendRNow = mDNSNULL;
3233 }
3234
3235 if (rr->ImmedAnswer)
3236 {
3237 if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client
3238
3239 if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
3240 CompleteDeregistration(m, rr); // Don't touch rr after this
3241 else
3242 {
3243 rr->ImmedAnswer = mDNSNULL;
3244 rr->ImmedUnicast = mDNSfalse;
3245 rr->v4Requester = zerov4Addr;
3246 rr->v6Requester = zerov6Addr;
3247 }
3248 }
3249 }
3250 verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow);
3251 }
3252
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))
3268
3269 // Note: MUST call SetNextCacheCheckTime any time we change:
3270 // rr->TimeRcvd
3271 // rr->DelayDelivery
3272 // rr->resrec.rroriginalttl
3273 // rr->UnansweredQueries
3274 // rr->CRActiveQuestion
3275 mDNSlocal void SetNextCacheCheckTime(mDNS *const m, CacheRecord *const rr)
3276 {
3277 rr->NextRequiredQuery = RRExpireTime(rr);
3278
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)
3282 {
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);
3287 }
3288
3289 if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0)
3290 m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr));
3291
3292 if (rr->DelayDelivery)
3293 if (m->NextCacheCheck - rr->DelayDelivery > 0)
3294 m->NextCacheCheck = rr->DelayDelivery;
3295 }
3296
3297 #define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
3298 #define kDefaultReconfirmTimeForCableDisconnect ((mDNSu32)mDNSPlatformOneSecond * 5)
3299 #define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
3300
3301 mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
3302 {
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;
3307
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))
3310 {
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);
3316 }
3317 debugf("mDNS_Reconfirm_internal:%5ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr));
3318 return(mStatus_NoError);
3319 }
3320
3321 #define MaxQuestionInterval (3600 * mDNSPlatformOneSecond)
3322
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)
3328 {
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));
3333 if (!newptr)
3334 {
3335 debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c);
3336 return(mDNSfalse);
3337 }
3338 else if (newptr + *answerforecast >= limit)
3339 {
3340 verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d", q->qname.c, newptr + *answerforecast - query->data);
3341 query->h.numQuestions--;
3342 return(mDNSfalse);
3343 }
3344 else
3345 {
3346 mDNSu32 forecast = *answerforecast;
3347 const mDNSu32 slot = HashSlot(&q->qname);
3348 CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
3349 CacheRecord *rr;
3350 CacheRecord **ka = *kalistptrptr; // Make a working copy of the pointer we're going to update
3351
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
3358 {
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)
3366 {
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
3373 }
3374 }
3375
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)
3388 {
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
3393 }
3394
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;
3400
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
3405 {
3406 rr->UnansweredQueries++; // indicate that we're expecting a response
3407 rr->LastUnansweredTime = m->timenow;
3408 SetNextCacheCheckTime(m, rr);
3409 }
3410
3411 return(mDNStrue);
3412 }
3413 }
3414
3415 mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q)
3416 {
3417 mDNSu32 slot;
3418 CacheGroup *cg;
3419 CacheRecord *rr;
3420 domainname *target;
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);
3424 }
3425
3426 // Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
3427 mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time)
3428 {
3429 int i;
3430 for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
3431 }
3432
3433 mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID)
3434 {
3435 int i;
3436 for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
3437 }
3438
3439 mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf)
3440 {
3441 int i;
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)
3446 {
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);
3450 }
3451 return(mDNSfalse);
3452 }
3453
3454 mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type)
3455 {
3456 int i, j;
3457
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;
3460
3461 // If not, find a slot we can re-use
3462 if (i >= DupSuppressInfoSize)
3463 {
3464 i = 0;
3465 for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++)
3466 if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0)
3467 i = j;
3468 }
3469
3470 // Record the info about this query we saw
3471 ds[i].Time = Time;
3472 ds[i].InterfaceID = InterfaceID;
3473 ds[i].Type = Type;
3474
3475 return(i);
3476 }
3477
3478 mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
3479 {
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))
3482 return(mDNStrue);
3483
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))
3486 {
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);
3491 CacheRecord *rr;
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
3497 {
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
3501 }
3502 return(mDNStrue);
3503 }
3504
3505 return(mDNSfalse);
3506 }
3507
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
3511
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.
3519
3520 mDNSlocal void SendQueries(mDNS *const m)
3521 {
3522 int pktcount = 0;
3523 DNSQuestion *q;
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;
3528
3529 // 1. If time for a query, work out what we need to do
3530 if (m->timenow - m->NextScheduledQuery >= 0)
3531 {
3532 mDNSu32 slot;
3533 CacheGroup *cg;
3534 CacheRecord *rr;
3535 m->NextScheduledQuery = m->timenow + 0x78000000;
3536
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)
3542 {
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;
3548 }
3549
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)))
3553 {
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;
3567 }
3568
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))
3572 {
3573 q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
3574 if (maxExistingQuestionInterval < q->ThisQInterval)
3575 maxExistingQuestionInterval = q->ThisQInterval;
3576 }
3577
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)
3582 {
3583 if (q->SendQNow ||
3584 (!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q)))
3585 {
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)
3589 {
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)
3595 {
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
3598 }
3599 }
3600
3601 // Mark for sending. (If no active interfaces, then don't even try.)
3602 q->SendOnAll = (q->SendQNow == mDNSInterfaceMark);
3603 if (q->SendOnAll)
3604 {
3605 q->SendQNow = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID;
3606 q->LastQTime = m->timenow;
3607 }
3608
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);
3612
3613 q->LastQTxTime = m->timenow;
3614 q->RecentAnswerPkts = 0;
3615 if (q->RequestUnicast) q->RequestUnicast--;
3616 }
3617 // For all questions (not just the ones we're sending) check what the next scheduled event will be
3618 SetNextQueryTime(m,q);
3619 }
3620 }
3621
3622 // 2. Scan our authoritative RR list to see what probes we might need to send
3623 if (m->timenow - m->NextScheduledProbe >= 0)
3624 {
3625 m->NextScheduledProbe = m->timenow + 0x78000000;
3626
3627 if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set");
3628 m->CurrentRecord = m->ResourceRecords;
3629 while (m->CurrentRecord)
3630 {
3631 AuthRecord *rr = m->CurrentRecord;
3632 m->CurrentRecord = rr->next;
3633 if (rr->resrec.RecordType == kDNSRecordTypeUnique) // For all records that are still probing...
3634 {
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)
3637 {
3638 SetNextAnnounceProbeTime(m, rr);
3639 }
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)
3642 {
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;
3646 rr->ProbeCount--;
3647 SetNextAnnounceProbeTime(m, rr);
3648 }
3649 // else, if it has now finished probing, move it to state Verified, and update m->NextScheduledResponse so it will be announced
3650 else
3651 {
3652 AuthRecord *r2;
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))
3660 r2->ProbeCount = 0;
3661 AcknowledgeRecord(m, rr);
3662 }
3663 }
3664 }
3665 m->CurrentRecord = m->DuplicateRecords;
3666 while (m->CurrentRecord)
3667 {
3668 AuthRecord *rr = m->CurrentRecord;
3669 m->CurrentRecord = rr->next;
3670 if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0)
3671 AcknowledgeRecord(m, rr);
3672 }
3673 }
3674
3675 // 3. Now we know which queries and probes we're sending, go through our interface list sending the appropriate queries on each interface
3676 while (intf)
3677 {
3678 AuthRecord *rr;
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)
3683 {
3684 // Start a new known-answer list
3685 CacheRecord **kalistptr = &KnownAnswerList;
3686 mDNSu32 answerforecast = 0;
3687
3688 // Put query questions in this packet
3689 for (q = m->Questions; q; q=q->next)
3690 if (q->SendQNow == intf->InterfaceID)
3691 {
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);
3699 }
3700
3701 // Put probe questions in this packet
3702 for (rr = m->ResourceRecords; rr; rr=rr->next)
3703 if (rr->SendRNow == intf->InterfaceID)
3704 {
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)
3712 {
3713 queryptr = newptr;
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);
3718 }
3719 else
3720 {
3721 verbosedebugf("SendQueries: Retracting Question %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
3722 m->omsg.h.numQuestions--;
3723 }
3724 }
3725 }
3726
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)
3729 {
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);
3733 if (newptr)
3734 {
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);
3736 queryptr = newptr;
3737 KnownAnswerList = rr->NextInKAList;
3738 rr->NextInKAList = mDNSNULL;
3739 }
3740 else
3741 {
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;
3747 break;
3748 }
3749 }
3750
3751 for (rr = m->ResourceRecords; rr; rr=rr->next)
3752 if (rr->IncludeInProbe)
3753 {
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));
3759 }
3760
3761 if (queryptr > m->omsg.data)
3762 {
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.
3776 }
3777 else // Nothing more to send on this interface; go to next
3778 {
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);
3783 #endif
3784 intf = next;
3785 }
3786 }
3787
3788 // Final sanity check for debugging purposes
3789 {
3790 AuthRecord *rr;
3791 for (rr = m->ResourceRecords; rr; rr=rr->next)
3792 if (rr->SendRNow)
3793 {
3794 if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
3795 LogMsg("SendQueries: No active interface to send: %s", ARDisplayString(m, rr));
3796 rr->SendRNow = mDNSNULL;
3797 }
3798 }
3799 }
3800
3801 // ***************************************************************************
3802 #if COMPILER_LIKES_PRAGMA_MARK
3803 #pragma mark -
3804 #pragma mark - RR List Management & Task Management
3805 #endif
3806
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)
3810 {
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));
3813
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.
3817
3818 rr->LastUsed = m->timenow;
3819 if (ActiveQuestion(q) && rr->CRActiveQuestion != q)
3820 {
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);
3824 }
3825
3826 // If this is:
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))
3834 {
3835 q->LastQTime = m->timenow;
3836 q->LastQTxTime = m->timenow;
3837 q->RecentAnswerPkts = 0;
3838 q->ThisQInterval = MaxQuestionInterval;
3839 q->RequestUnicast = mDNSfalse;
3840 }
3841
3842 if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us
3843
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.
3853 }
3854
3855 mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
3856 {
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)
3861 {
3862 DNSQuestion *q = m->CurrentQuestion;
3863 m->CurrentQuestion = q->next;
3864 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3865 AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
3866 }
3867 m->CurrentQuestion = mDNSNULL;
3868 }
3869
3870 mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot)
3871 {
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);
3876 CacheRecord *rr;
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
3883 else return(0);
3884 }
3885
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)
3895 {
3896 if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
3897 m->CurrentQuestion = m->Questions;
3898 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3899 {
3900 DNSQuestion *q = m->CurrentQuestion;
3901 m->CurrentQuestion = q->next;
3902 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3903 {
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)
3910 {
3911 q->LastAnswerPktNum = m->PktNum;
3912 if (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 &&
3913 q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond)
3914 {
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);
3920 }
3921 }
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)
3927 {
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;
3934 }
3935 AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
3936 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
3937 }
3938 }
3939 m->CurrentQuestion = mDNSNULL;
3940 }
3941
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)
3953 {
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)
3958 {
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()
3964 }
3965 m->CurrentQuestion = mDNSNULL;
3966 }
3967
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)
3977 {
3978 if (m->CurrentQuestion) LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set");
3979 m->CurrentQuestion = m->Questions;
3980 while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
3981 {
3982 DNSQuestion *q = m->CurrentQuestion;
3983 m->CurrentQuestion = q->next;
3984 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
3985 {
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));
3989 else
3990 {
3991 q->CurrentAnswers--;
3992 if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
3993 if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
3994 }
3995 if (q->CurrentAnswers == 0)
3996 {
3997 debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents", q->qname.c, DNSTypeName(q->qtype));
3998 ReconfirmAntecedents(m, q);
3999 }
4000 AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse);
4001 // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
4002 }
4003 }
4004 m->CurrentQuestion = mDNSNULL;
4005 }
4006
4007 mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e)
4008 {
4009 #if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
4010 unsigned int i;
4011 for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
4012 #endif
4013 e->next = m->rrcache_free;
4014 m->rrcache_free = e;
4015 m->rrcache_totalused--;
4016 }
4017
4018 mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp)
4019 {
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);
4028 }
4029
4030 mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
4031 {
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);
4035 }
4036
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)
4040 {
4041 CacheRecord **rp = &cg->members;
4042
4043 if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
4044 m->lock_rrcache = 1;
4045
4046 while (*rp)
4047 {
4048 CacheRecord *const rr = *rp;
4049 mDNSs32 event = RRExpireTime(rr);
4050 if (m->timenow - event >= 0) // If expired, delete it
4051 {
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
4055 {
4056 CacheRecordRmv(m, rr);
4057 m->rrcache_active--;
4058 }
4059 ReleaseCacheRecord(m, rr);
4060 }
4061 else // else, not expired; see if we need to query
4062 {
4063 if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0)
4064 event = rr->DelayDelivery;
4065 else
4066 {
4067 if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr);
4068 if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
4069 {
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
4073 {
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;
4080 }
4081 }
4082 }
4083 if (m->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0)
4084 m->NextCacheCheck = (event + CacheCheckGracePeriod(rr));
4085 rp = &rr->next;
4086 }
4087 }
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;
4091 }
4092
4093 mDNSlocal void AnswerNewQuestion(mDNS *const m)
4094 {
4095 mDNSBool ShouldQueryImmediately = mDNStrue;
4096 CacheRecord *rr;
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);
4100
4101 verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4102
4103 if (cg) CheckCacheExpiration(m, cg);
4104 m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
4105
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
4114
4115 if (q->InterfaceID == mDNSInterface_Any) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
4116 {
4117 if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4118 m->CurrentRecord = m->ResourceRecords;
4119 while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
4120 {
4121 AuthRecord *rr = m->CurrentRecord;
4122 m->CurrentRecord = rr->next;
4123 if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
4124 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4125 {
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
4129 }
4130 }
4131 m->CurrentRecord = mDNSNULL;
4132 }
4133
4134 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4135 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4136 {
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)
4140 {
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
4144 }
4145
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
4156 }
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;
4160
4161 if (ShouldQueryImmediately && m->CurrentQuestion == q)
4162 {
4163 q->ThisQInterval = InitialQuestionInterval;
4164 q->LastQTime = m->timenow - q->ThisQInterval;
4165 m->NextScheduledQuery = m->timenow;
4166 }
4167 m->CurrentQuestion = mDNSNULL;
4168 m->lock_rrcache = 0;
4169 }
4170
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)
4174 {
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)
4177
4178 debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
4179
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
4182
4183 if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
4184 m->CurrentRecord = m->ResourceRecords;
4185 while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
4186 {
4187 AuthRecord *rr = m->CurrentRecord;
4188 m->CurrentRecord = rr->next;
4189 if (ResourceRecordAnswersQuestion(&rr->resrec, q))
4190 {
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
4194 }
4195 }
4196
4197 m->CurrentQuestion = mDNSNULL;
4198 m->CurrentRecord = mDNSNULL;
4199 }
4200
4201 mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG)
4202 {
4203 CacheEntity *e = mDNSNULL;
4204
4205 if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
4206 m->lock_rrcache = 1;
4207
4208 // If we have no free records, ask the client layer to give us some more memory
4209 if (!m->rrcache_free && m->MainCallback)
4210 {
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);
4214
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);
4222 else
4223 {
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
4227 }
4228 }
4229
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)
4233 {
4234 #if MDNS_DEBUGMSGS
4235 mDNSu32 oldtotalused = m->rrcache_totalused;
4236 #endif
4237 mDNSu32 slot;
4238 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
4239 {
4240 CacheGroup **cp = &m->rrcache_hash[slot];
4241 while (*cp)
4242 {
4243 CacheRecord **rp = &(*cp)->members;
4244 while (*rp)
4245 {
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)
4249 rp=&(*rp)->next;
4250 else
4251 {
4252 CacheRecord *rr = *rp;
4253 *rp = (*rp)->next; // Cut record from list
4254 ReleaseCacheRecord(m, rr);
4255 }
4256 }
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);
4261 }
4262 }
4263 #if MDNS_DEBUGMSGS
4264 debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused);
4265 #endif
4266 }
4267
4268 if (m->rrcache_free) // If there are records in the free list, take one
4269 {
4270 e = m->rrcache_free;
4271 m->rrcache_free = e->next;
4272 if (++m->rrcache_totalused >= m->rrcache_report)
4273 {
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;
4277 }
4278 mDNSPlatformMemZero(e, sizeof(*e));
4279 }
4280
4281 m->lock_rrcache = 0;
4282
4283 return(e);
4284 }
4285
4286 mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength)
4287 {
4288 CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg);
4289 if (r)
4290 {
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
4293 {
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; }
4297 }
4298 }
4299 return(r);
4300 }
4301
4302 mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
4303 {
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);
4314 if (!cg->name)
4315 {
4316 LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c);
4317 ReleaseCacheEntity(m, (CacheEntity*)cg);
4318 return(mDNSNULL);
4319 }
4320 AssignDomainName(cg->name, rr->name);
4321
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);
4325
4326 return(cg);
4327 }
4328
4329 mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
4330 {
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);
4340 }
4341
4342 mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m)
4343 {
4344 mDNSs32 time;
4345 mDNSPlatformLock(m);
4346 if (m->mDNS_busy)
4347 {
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);
4350 }
4351
4352 if (m->timenow) time = m->timenow;
4353 else time = mDNSPlatformRawTime() + m->timenow_adjust;
4354 mDNSPlatformUnlock(m);
4355 return(time);
4356 }
4357
4358 mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
4359 {
4360 mDNS_Lock(m); // Must grab lock before trying to read m->timenow
4361
4362 if (m->timenow - m->NextScheduledEvent >= 0)
4363 {
4364 int i;
4365
4366 verbosedebugf("mDNS_Execute");
4367 if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
4368
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;
4371
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;
4374
4375 // 3. Purge our cache of stale old records
4376 if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
4377 {
4378 mDNSu32 slot;
4379 m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
4380 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
4381 {
4382 CacheGroup **cp = &m->rrcache_hash[slot];
4383 while (*cp)
4384 {
4385 CheckCacheExpiration(m, *cp);
4386 if ((*cp)->members) cp=&(*cp)->next;
4387 else ReleaseCacheGroup(m, cp);
4388 }
4389 }
4390 }
4391
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++)
4394 {
4395 if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break;
4396 AnswerNewQuestion(m);
4397 }
4398 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
4399
4400 for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
4401 if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
4402
4403 for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++)
4404 {
4405 AuthRecord *rr = m->NewLocalRecords;
4406 m->NewLocalRecords = m->NewLocalRecords->next;
4407 AnswerLocalQuestions(m, rr, mDNStrue);
4408 }
4409 if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
4410
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)
4414 {
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;
4422
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)
4426 {
4427 LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
4428 m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond;
4429 }
4430 if (m->timenow - m->NextScheduledProbe >= 0)
4431 {
4432 LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
4433 m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond;
4434 }
4435
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)
4439 {
4440 LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
4441 m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond;
4442 }
4443 }
4444
4445 m->RandomQueryDelay = 0; // Clear m->RandomQueryDelay, ready to pick a new different value, when necessary
4446 }
4447
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.
4451 //
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.
4461 //
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).
4467
4468 #ifndef UNICAST_DISABLED
4469 uDNS_Execute(m);
4470 #endif
4471 mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
4472 return(m->NextScheduledEvent);
4473 }
4474
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)
4487 {
4488 AuthRecord *rr;
4489
4490 mDNS_Lock(m);
4491
4492 m->SleepState = sleepstate;
4493 LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow);
4494
4495 if (sleepstate)
4496 {
4497 #ifndef UNICAST_DISABLED
4498 uDNS_Sleep(m);
4499 #endif
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;
4504 SendResponses(m);
4505 }
4506 else
4507 {
4508 DNSQuestion *q;
4509 mDNSu32 slot;
4510 CacheGroup *cg;
4511 CacheRecord *cr;
4512
4513 #ifndef UNICAST_DISABLED
4514 uDNS_Wake(m);
4515 #endif
4516 // 1. Retrigger all our questions
4517 for (q = m->Questions; q; q=q->next) // Scan our list of questions
4518 if (ActiveQuestion(q))
4519 {
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;
4526 }
4527
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);
4532
4533 // 3. Retrigger probing and announcing for all our authoritative records
4534 for (rr = m->ResourceRecords; rr; rr=rr->next)
4535 {
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);
4541 }
4542 }
4543
4544 mDNS_Unlock(m);
4545 }
4546
4547 // ***************************************************************************
4548 #if COMPILER_LIKES_PRAGMA_MARK
4549 #pragma mark -
4550 #pragma mark - Packet Reception Functions
4551 #endif
4552
4553 #define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
4554
4555 mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end,
4556 const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords)
4557 {
4558 mDNSu8 *responseptr = response->data;
4559 const mDNSu8 *const limit = response->data + sizeof(response->data);
4560 const mDNSu8 *ptr = query->data;
4561 AuthRecord *rr;
4562 mDNSu32 maxttl = 0x70000000;
4563 int i;
4564
4565 // Initialize the response fields so we can answer the questions
4566 InitializeDNSMessage(&response->h, query->h.id, ResponseFlags);
4567
4568 // ***
4569 // *** 1. Write out the list of questions we are actually going to answer with this packet
4570 // ***
4571 if (LegacyQuery)
4572 {
4573 maxttl = 10;
4574 for (i=0; i<query->h.numQuestions; i++) // For each question...
4575 {
4576 DNSQuestion q;
4577 ptr = getQuestion(query, ptr, end, InterfaceID, &q); // get the question...
4578 if (!ptr) return(mDNSNULL);
4579
4580 for (rr=ResponseRecords; rr; rr=rr->NextResponse) // and search our list of proposed answers
4581 {
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
4587 }
4588 }
4589 }
4590
4591 if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
4592 }
4593
4594 // ***
4595 // *** 2. Write Answers
4596 // ***
4597 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4598 if (rr->NR_AnswerTo)
4599 {
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; }
4603 }
4604
4605 // ***
4606 // *** 3. Write Additionals
4607 // ***
4608 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
4609 if (rr->NR_AdditionalTo && !rr->NR_AnswerTo)
4610 {
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");
4614 }
4615
4616 return(responseptr);
4617 }
4618
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)
4625 {
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); }
4630
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
4635
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
4640
4641 debugf("CompareRData: How did we get here?");
4642 return(-1);
4643 }
4644
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)
4655 {
4656 const AuthRecord *r1;
4657 for (r1 = m->ResourceRecords; r1; r1=r1->next)
4658 {
4659 if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
4660 {
4661 const AuthRecord *r2 = r1;
4662 while (r2->DependentOn) r2 = r2->DependentOn;
4663 if (r2 == master) return(mDNStrue);
4664 }
4665 }
4666 for (r1 = m->DuplicateRecords; r1; r1=r1->next)
4667 {
4668 if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
4669 {
4670 const AuthRecord *r2 = r1;
4671 while (r2->DependentOn) r2 = r2->DependentOn;
4672 if (r2 == master) return(mDNStrue);
4673 }
4674 }
4675 return(mDNSfalse);
4676 }
4677
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)
4683 {
4684 const AuthRecord *rr;
4685 for (rr = m->ResourceRecords; rr; rr=rr->next)
4686 {
4687 if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
4688 {
4689 while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet;
4690 return(rr);
4691 }
4692 }
4693 return(mDNSNULL);
4694 }
4695
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)
4706 {
4707 const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
4708
4709 // If not supposed to be unique, not a conflict
4710 if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
4711
4712 // If a dependent record, not a conflict
4713 if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse);
4714
4715 // If the pktrr matches a member of ourset, not a conflict
4716 if (FindRRSet(m, pktrr) == ourset) return(mDNSfalse);
4717
4718 // Okay, this is a conflict
4719 return(mDNStrue);
4720 }
4721
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)
4727 {
4728 int i;
4729 const mDNSu8 *ptr = LocateAuthorities(query, end);
4730 mDNSBool FoundUpdate = mDNSfalse;
4731
4732 for (i = 0; i < query->h.numAuthorities; i++)
4733 {
4734 ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
4735 if (!ptr) break;
4736 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
4737 {
4738 FoundUpdate = mDNStrue;
4739 if (PacketRRConflict(m, our, &m->rec.r))
4740 {
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);
4744 switch (result)
4745 {
4746 case 1: debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4747 break;
4748 case 0: break;
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);
4751 goto exit;
4752 }
4753 }
4754 }
4755 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
4756 }
4757 if (!FoundUpdate)
4758 debugf("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
4759 exit:
4760 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
4761 }
4762
4763 mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, ResourceRecord *pktrr)
4764 {
4765 mDNSu32 slot = HashSlot(pktrr->name);
4766 CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
4767 CacheRecord *rr;
4768 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
4769 if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break;
4770 return(rr);
4771 }
4772
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)
4777 {
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;
4789 AuthRecord *rr;
4790 int i;
4791
4792 // ***
4793 // *** 1. Parse Question Section and mark potential answers
4794 // ***
4795 for (i=0; i<query->h.numQuestions; i++) // For each question...
4796 {
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;
4802
4803 // The only queries that *need* a multicast response are:
4804 // * Queries sent via multicast
4805 // * from port 5353
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;
4815
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)
4824 {
4825 rr = m->CurrentRecord;
4826 m->CurrentRecord = rr->next;
4827 if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
4828 {
4829 if (rr->resrec.RecordType == kDNSRecordTypeUnique)
4830 ResolveSimultaneousProbe(m, query, end, &pktq, rr);
4831 else if (ResourceRecordIsValidAnswer(rr))
4832 {
4833 NumAnswersForThisQuestion++;
4834 // Notes:
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))
4842 {
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;
4849 }
4850 else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1;
4851 }
4852 }
4853 }
4854
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
4858
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)
4863 {
4864 const mDNSu32 slot = HashSlot(&pktq.qname);
4865 CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
4866 CacheRecord *rr;
4867
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)
4873 {
4874 *eap = rr;
4875 eap = &rr->NextInKAList;
4876 if (rr->MPUnansweredQ == 0 || m->timenow - rr->MPLastUnansweredQT >= mDNSPlatformOneSecond)
4877 {
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;
4884 }
4885 }
4886
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; }
4898 }
4899 }
4900
4901 // ***
4902 // *** 2. Now we can safely build the list of marked answers
4903 // ***
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
4907
4908 // ***
4909 // *** 3. Add additional records
4910 // ***
4911 AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
4912
4913 // ***
4914 // *** 4. Parse Answer Section and cancel any records disallowed by Known-Answer list
4915 // ***
4916 for (i=0; i<query->h.numAnswers; i++) // For each record in the query's answer section...
4917 {
4918 // Get the record...
4919 AuthRecord *rr;
4920 CacheRecord *ourcacherr;
4921 ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec);
4922 if (!ptr) goto exit;
4923
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; }
4928
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)
4931 {
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))
4934 {
4935 if (srcaddr->type == mDNSAddrType_IPv4)
4936 {
4937 if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr;
4938 }
4939 else if (srcaddr->type == mDNSAddrType_IPv6)
4940 {
4941 if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr;
4942 }
4943 if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
4944 {
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));
4949 #endif
4950 }
4951 }
4952 }
4953
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)
4958 {
4959 ourcacherr->MPUnansweredKA++;
4960 ourcacherr->MPExpectingKA = mDNSfalse;
4961 }
4962
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;
4967 while (*eap)
4968 {
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;
4973 }
4974
4975 // See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
4976 if (!ourcacherr)
4977 {
4978 dqp = &DupQuestions;
4979 while (*dqp)
4980 {
4981 DNSQuestion *q = *dqp;
4982 if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
4983 { *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; }
4984 else dqp = &q->NextInDQList;
4985 }
4986 }
4987 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
4988 }
4989
4990 // ***
4991 // *** 5. Cancel any additionals that were added because of now-deleted records
4992 // ***
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; }
4996
4997 // ***
4998 // *** 6. Mark the send flags on the records we plan to send
4999 // ***
5000 for (rr=ResponseRecords; rr; rr=rr->NextResponse)
5001 {
5002 if (rr->NR_AnswerTo)
5003 {
5004 mDNSBool SendMulticastResponse = mDNSfalse; // Send modern multicast response
5005 mDNSBool SendUnicastResponse = mDNSfalse; // Send modern unicast response (not legacy unicast response)
5006
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)
5009 {
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;
5015 }
5016
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;
5021
5022 if (SendMulticastResponse || SendUnicastResponse)
5023 {
5024 #if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
5025 rr->ImmedAnswerMarkTime = m->timenow;
5026 #endif
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;
5031 else
5032 {
5033 rr->ImmedAnswer = InterfaceID; // Record interface to send it on
5034 if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue;
5035 if (srcaddr->type == mDNSAddrType_IPv4)
5036 {
5037 if (mDNSIPv4AddressIsZero(rr->v4Requester)) rr->v4Requester = srcaddr->ip.v4;
5038 else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr;
5039 }
5040 else if (srcaddr->type == mDNSAddrType_IPv6)
5041 {
5042 if (mDNSIPv6AddressIsZero(rr->v6Requester)) rr->v6Requester = srcaddr->ip.v6;
5043 else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr;
5044 }
5045 }
5046 }
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
5053 }
5054 else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0)
5055 {
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
5062 }
5063 }
5064
5065 // ***
5066 // *** 7. If we think other machines are likely to answer these questions, set our packet suppression timer
5067 // ***
5068 if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50))
5069 {
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);
5074 #endif
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);
5093 #endif
5094 }
5095
5096 // ***
5097 // *** 8. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
5098 // ***
5099 if (SendLegacyResponse)
5100 responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords);
5101
5102 exit:
5103 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5104
5105 // ***
5106 // *** 9. Finally, clear our link chains ready for use next time
5107 // ***
5108 while (ResponseRecords)
5109 {
5110 rr = ResponseRecords;
5111 ResponseRecords = rr->NextResponse;
5112 rr->NextResponse = mDNSNULL;
5113 rr->NR_AnswerTo = mDNSNULL;
5114 rr->NR_AdditionalTo = mDNSNULL;
5115 }
5116
5117 while (ExpectedAnswers)
5118 {
5119 CacheRecord *rr;
5120 rr = ExpectedAnswers;
5121 ExpectedAnswers = rr->NextInKAList;
5122 rr->NextInKAList = mDNSNULL;
5123
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)
5128 {
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);
5135 }
5136
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)
5140 {
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);
5146 }
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)
5151 {
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;
5161
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));
5166
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;
5172
5173 if (remain < kDefaultReconfirmTimeForNoAnswer)
5174 remain = kDefaultReconfirmTimeForNoAnswer;
5175 mDNS_Reconfirm_internal(m, rr, remain);
5176 }
5177 }
5178
5179 while (DupQuestions)
5180 {
5181 int i;
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);
5188 }
5189
5190 return(responseptr);
5191 }
5192
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)
5196 {
5197 mDNSu8 *responseend = mDNSNULL;
5198 mDNSBool QueryWasLocalUnicast = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr);
5199
5200 if (!InterfaceID && mDNSAddrIsDNSMulticast(dstaddr))
5201 {
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");
5208 return;
5209 }
5210
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");
5217
5218 responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID,
5219 (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg);
5220
5221 if (responseend) // If responseend is non-null, that means we built a unicast response packet
5222 {
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);
5229 }
5230 }
5231
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)
5239 {
5240 int i;
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;
5244
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;
5249
5250 (void)srcaddr; // Currently used only for display in debugging message
5251 (void)srcport;
5252 (void)dstport;
5253
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");
5260
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))
5263 {
5264 if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2))
5265 return;
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)
5269 return;
5270 }
5271
5272 for (i = 0; i < totalrecords && ptr && ptr < end; i++)
5273 {
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
5277
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)
5282 {
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...
5286 {
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))
5289 {
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)
5292 {
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; }
5295 }
5296 else
5297 {
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; }
5300 }
5301 }
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))
5304 {
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));
5307
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;
5311
5312 // If we've just whacked this record's ProbeCount, don't need to do it again
5313 if (rr->ProbeCount <= DefaultProbeCountForTypeUnique)
5314 {
5315 // If we'd previously verified this record, put it back to probing state and try again
5316 if (rr->resrec.RecordType == kDNSRecordTypeVerified)
5317 {
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
5324 }
5325 // If we're probing for this record, we just failed
5326 else if (rr->resrec.RecordType == kDNSRecordTypeUnique)
5327 {
5328 debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5329 mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
5330 }
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)
5336 {
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);
5340 }
5341 else
5342 debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
5343 rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
5344 }
5345 }
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; }
5353 }
5354 }
5355
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
5358 {
5359 const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
5360 CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
5361 CacheRecord *rr;
5362 // 2a. Check if this packet resource record is already in our cache
5363 for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
5364 {
5365 // If we found this exact resource record, refresh its TTL
5366 if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
5367 {
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;
5372
5373 if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
5374 {
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; }
5378
5379 // If this packet record is marked unique, and our previous cached copy was not, then fix it
5380 if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
5381 {
5382 DNSQuestion *q;
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;
5385 }
5386 }
5387
5388 if (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength))
5389 {
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
5397 }
5398 else if (m->rec.r.resrec.rroriginalttl > 0)
5399 {
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);
5406 break;
5407 }
5408 else
5409 {
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);
5418 break;
5419 }
5420 }
5421 }
5422
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)
5426 {
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);
5431 else
5432 {
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
5449 else
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);
5454 }
5455 }
5456 }
5457 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5458 }
5459
5460 exit:
5461 m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
5462
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)
5466 {
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))
5474 {
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;
5479 else
5480 {
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);
5497 }
5498 }
5499 if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to
5500 {
5501 r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
5502 if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
5503 }
5504 }
5505 }
5506
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)
5510 {
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;
5514 mDNSu8 QR_OP;
5515 mDNSu8 *ptr = mDNSNULL;
5516 const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
5517
5518 #ifndef UNICAST_DISABLED
5519 mDNSIPPort NATPort = mDNSOpaque16fromIntVal(NATMAP_PORT);
5520
5521 if (srcport.NotAnInteger == NATPort.NotAnInteger)
5522 {
5523 mDNS_Lock(m);
5524 uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
5525 mDNS_Unlock(m);
5526 return;
5527 }
5528 #endif
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]);
5537
5538 if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
5539
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; }
5543
5544 mDNS_Lock(m);
5545 m->PktNum++;
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
5550 #endif
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);
5556
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
5562 mDNS_Unlock(m);
5563 }
5564
5565 // ***************************************************************************
5566 #if COMPILER_LIKES_PRAGMA_MARK
5567 #pragma mark -
5568 #pragma mark -
5569 #pragma mark - Searcher Functions
5570 #endif
5571
5572 #define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
5573
5574 mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
5575 {
5576 DNSQuestion *q;
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
5588 return(q);
5589 return(mDNSNULL);
5590 }
5591
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)
5595 {
5596 DNSQuestion *q;
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
5599 {
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);
5607 }
5608 }
5609
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))
5612
5613 mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
5614 {
5615 if (question->Target.type && !ValidQuestionTarget(question))
5616 {
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;
5620 }
5621
5622 if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID
5623 {
5624 question->TargetPort = zeroIPPort;
5625 question->TargetQID = zeroID;
5626 }
5627
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);
5634 #else
5635 question->uDNS_info.id = zeroID;
5636 #endif // UNICAST_DISABLED
5637
5638 //LogOperation("mDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
5639
5640 if (m->rrcache_size == 0) // Can't do queries if we have no cache space allocated
5641 return(mStatus_NoCache);
5642 else
5643 {
5644 int i;
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;
5649
5650 if (*q)
5651 {
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);
5655 }
5656
5657 // If this question is referencing a specific interface, make sure it exists
5658 if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly)
5659 {
5660 NetworkInterfaceInfo *intf;
5661 for (intf = m->HostInterfaces; intf; intf = intf->next)
5662 if (intf->InterfaceID == question->InterfaceID) break;
5663 if (!intf)
5664 {
5665 debugf("mDNS_StartQuery_internal: Question %##s InterfaceID %p not found", question->qname.c, question->InterfaceID);
5666 return(mStatus_BadInterfaceErr);
5667 }
5668 }
5669
5670 if (!ValidateDomainName(&question->qname))
5671 {
5672 LogMsg("Attempt to start query with invalid qname %##s %s", question->qname.c, DNSTypeName(question->qtype));
5673 return(mStatus_Invalid);
5674 }
5675
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);
5681
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;
5701
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);
5705 else
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);
5708
5709 *q = question;
5710 if (question->InterfaceID == mDNSInterface_LocalOnly)
5711 {
5712 if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
5713 }
5714 else
5715 {
5716 if (!m->NewQuestions) m->NewQuestions = question;
5717 SetNextQueryTime(m,question);
5718 }
5719
5720 return(mStatus_NoError);
5721 }
5722 }
5723
5724 mDNSlocal mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
5725 {
5726 const mDNSu32 slot = HashSlot(&question->qname);
5727 CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
5728 CacheRecord *rr;
5729 DNSQuestion **q = &m->Questions;
5730
5731 if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question);
5732
5733 if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
5734 while (*q && *q != question) q=&(*q)->next;
5735 if (*q) *q = (*q)->next;
5736 else
5737 {
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);
5742 }
5743
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;
5748
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)
5752 {
5753 if (rr->CRActiveQuestion == question)
5754 {
5755 DNSQuestion *q;
5756 for (q = m->Questions; q; q=q->next) // Scan our list of questions
5757 if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
5758 break;
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
5762 }
5763 }
5764
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)
5768 {
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;
5772 }
5773
5774 if (m->NewQuestions == question)
5775 {
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;
5779 }
5780
5781 if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
5782
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);
5786 }
5787
5788 mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
5789 {
5790 mStatus status;
5791 mDNS_Lock(m);
5792 status = mDNS_StartQuery_internal(m, question);
5793 mDNS_Unlock(m);
5794 return(status);
5795 }
5796
5797 mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
5798 {
5799 mStatus status;
5800 mDNS_Lock(m);
5801 status = mDNS_StopQuery_internal(m, question);
5802 mDNS_Unlock(m);
5803 return(status);
5804 }
5805
5806 mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr)
5807 {
5808 mStatus status;
5809 mDNS_Lock(m);
5810 status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
5811 mDNS_Unlock(m);
5812 return(status);
5813 }
5814
5815 mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr)
5816 {
5817 mStatus status = mStatus_BadReferenceErr;
5818 CacheRecord *cr;
5819 mDNS_Lock(m);
5820 cr = FindIdenticalRecordInCache(m, rr);
5821 if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
5822 mDNS_Unlock(m);
5823 return(status);
5824 }
5825
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)
5829 {
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);
5840
5841 #ifndef UNICAST_DISABLED
5842 if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
5843 {
5844 question->LongLived = mDNSfalse;
5845 question->uDNS_info.id = zeroID;
5846 return(mDNS_StartQuery(m, question));
5847 }
5848 else
5849 {
5850 mStatus status;
5851 // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
5852 mDNS_Lock(m);
5853 question->LongLived = mDNStrue;
5854 status = uDNS_StartQuery(m, question);
5855 mDNS_Unlock(m);
5856 return(status);
5857 }
5858 #else
5859 return(mDNS_StartQuery(m, question));
5860 #endif // UNICAST_DISABLED
5861 }
5862
5863 mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m)
5864 {
5865 NetworkInterfaceInfo *intf;
5866 for (intf = m->HostInterfaces; intf; intf = intf->next)
5867 if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue);
5868 return(mDNSfalse);
5869 }
5870
5871 mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
5872 {
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;
5877
5878 query->info->port = answer->rdata->u.srv.port;
5879
5880 // If this is our first answer, then set the GotSRV flag and start the address query
5881 if (!query->GotSRV)
5882 {
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);
5891 }
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))
5895 {
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)
5899 {
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;
5907 }
5908 else
5909 {
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);
5914 }
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);
5919 }
5920 else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged)
5921 {
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);
5927 }
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.
5930 }
5931
5932 mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
5933 {
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;
5938
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);
5943
5944 verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD);
5945
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)
5949 {
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);
5954 }
5955 }
5956
5957 mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
5958 {
5959 ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
5960 //LogOperation("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
5961 if (!AddRecord) return;
5962
5963 if (answer->rrtype == kDNSType_A)
5964 {
5965 query->info->ip.type = mDNSAddrType_IPv4;
5966 query->info->ip.ip.v4 = answer->rdata->u.ipv4;
5967 }
5968 else if (answer->rrtype == kDNSType_AAAA)
5969 {
5970 query->info->ip.type = mDNSAddrType_IPv6;
5971 query->info->ip.ip.v6 = answer->rdata->u.ipv6;
5972 }
5973 else
5974 {
5975 debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype));
5976 return;
5977 }
5978
5979 query->GotADD = mDNStrue;
5980 query->info->InterfaceID = answer->InterfaceID;
5981
5982 verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT);
5983
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)
5987 {
5988 if (++query->Answers >= 100)
5989 {
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);
5992 else
5993 debugf("**** WARNING **** have given %lu answers for %##s (AAAA) %.16a", query->Answers, query->qSRV.qname.c, &answer->rdata->u.ipv6);
5994 }
5995 query->ServiceInfoQueryCallback(m, query);
5996 }
5997 }
5998
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)
6005 {
6006 mStatus status;
6007 mDNS_Lock(m);
6008
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;
6020
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;
6032
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;
6044
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;
6056
6057 query->GotSRV = mDNSfalse;
6058 query->GotTXT = mDNSfalse;
6059 query->GotADD = mDNSfalse;
6060 query->Answers = 0;
6061
6062 query->info = info;
6063 query->ServiceInfoQueryCallback = Callback;
6064 query->ServiceInfoQueryContext = Context;
6065
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;
6070 info->TXTlen = 0;
6071
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);
6076
6077 mDNS_Unlock(m);
6078 return(status);
6079 }
6080
6081 mDNSexport void mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q)
6082 {
6083 mDNS_Lock(m);
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);
6089 mDNS_Unlock(m);
6090 }
6091
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)
6094 {
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));
6109 }
6110
6111 // ***************************************************************************
6112 #if COMPILER_LIKES_PRAGMA_MARK
6113 #pragma mark -
6114 #pragma mark - Responder Functions
6115 #endif
6116
6117 mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr)
6118 {
6119 mStatus status;
6120 mDNS_Lock(m);
6121 status = mDNS_Register_internal(m, rr);
6122 mDNS_Unlock(m);
6123 return(status);
6124 }
6125
6126 mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
6127 const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback)
6128 {
6129 #ifndef UNICAST_DISABLED
6130 mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name));
6131 #else
6132 mDNSBool unicast = mDNSfalse;
6133 #endif
6134
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); }
6137
6138 mDNS_Lock(m);
6139
6140 // If TTL is unspecified, leave TTL unchanged
6141 if (newttl == 0) newttl = rr->resrec.rroriginalttl;
6142
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)
6146 {
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
6151 }
6152
6153 rr->NewRData = newrdata;
6154 rr->newrdlength = newrdlength;
6155 rr->UpdateCallback = Callback;
6156
6157 if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); }
6158
6159 if (rr->resrec.rroriginalttl == newttl && rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
6160 CompleteRDataUpdate(m, rr);
6161 else
6162 {
6163 domainlabel name;
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)
6180 {
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" : "");
6186 }
6187 rr->resrec.rroriginalttl = newttl;
6188 }
6189
6190 mDNS_Unlock(m);
6191 return(mStatus_NoError);
6192 }
6193
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)
6198 {
6199 mStatus status;
6200 mDNS_Lock(m);
6201 status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
6202 mDNS_Unlock(m);
6203 return(status);
6204 }
6205
6206 mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
6207
6208 mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
6209 {
6210 NetworkInterfaceInfo *intf;
6211 for (intf = m->HostInterfaces; intf; intf = intf->next)
6212 if (intf->Advertise) break;
6213 return(intf);
6214 }
6215
6216 mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
6217 {
6218 char buffer[256];
6219 NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
6220 if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
6221
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);
6226
6227 #if ANSWER_REMOTE_HOSTNAME_QUERIES
6228 set->RR_A .AllowRemoteQuery = mDNStrue;
6229 set->RR_PTR .AllowRemoteQuery = mDNStrue;
6230 set->RR_HINFO.AllowRemoteQuery = mDNStrue;
6231 #endif
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)
6236 {
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]);
6242 }
6243 else if (set->ip.type == mDNSAddrType_IPv6)
6244 {
6245 int i;
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++)
6249 {
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] = '.';
6255 }
6256 mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
6257 }
6258
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
6262
6263 set->RR_A.RRSet = &primary->RR_A; // May refer to self
6264
6265 mDNS_Register_internal(m, &set->RR_A);
6266 mDNS_Register_internal(m, &set->RR_PTR);
6267
6268 if (m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254)
6269 {
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]);
6274 p += 1 + (int)p[0];
6275 mDNSPlatformMemCopy(&m->HISoftware, p, 1 + (mDNSu32)m->HISoftware.c[0]);
6276 mDNS_Register_internal(m, &set->RR_HINFO);
6277 }
6278 else
6279 {
6280 debugf("Not creating HINFO record: platform support layer provided no information");
6281 set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered;
6282 }
6283 }
6284
6285 mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
6286 {
6287 NetworkInterfaceInfo *intf;
6288
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;
6295
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);
6304 }
6305
6306 mDNSexport void mDNS_SetFQDN(mDNS *const m)
6307 {
6308 domainname newmname;
6309 NetworkInterfaceInfo *intf;
6310 AuthRecord *rr;
6311 newmname.c[0] = 0;
6312
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; }
6316
6317 mDNS_Lock(m);
6318 AssignDomainName(&m->MulticastHostname, &newmname);
6319
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);
6323
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);
6327
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);
6332
6333 mDNS_Unlock(m);
6334 }
6335
6336 mDNSexport void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6337 {
6338 (void)rr; // Unused parameter
6339
6340 #if MDNS_DEBUGMSGS
6341 {
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);
6346 }
6347 #endif
6348
6349 if (result == mStatus_NoError)
6350 {
6351 // Notify the client that the host name is successfully registered
6352 if (m->MainCallback)
6353 {
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
6357 }
6358 }
6359 else if (result == mStatus_NameConflict)
6360 {
6361 domainlabel oldlabel = m->hostlabel;
6362
6363 // 1. First give the client callback a chance to pick a new name
6364 if (m->MainCallback)
6365 {
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
6369 }
6370
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);
6374
6375 // 3. Generate the FQDNs from the hostlabel,
6376 // and make sure all SRV records, etc., are updated to reference our new hostname
6377 mDNS_SetFQDN(m);
6378 LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c);
6379 }
6380 else if (result == mStatus_MemFree)
6381 {
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)");
6385 }
6386 else
6387 LogMsg("mDNS_HostNameCallback: Unknown error %ld for registration of record %s", result, rr->resrec.name->c);
6388 }
6389
6390 mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active)
6391 {
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)
6397 {
6398 if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue;
6399 if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue;
6400 }
6401 }
6402
6403 mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSs32 delay)
6404 {
6405 mDNSBool FirstOfType = mDNStrue;
6406 NetworkInterfaceInfo **p = &m->HostInterfaces;
6407
6408 if (!set->InterfaceID)
6409 { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
6410
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); }
6413
6414 mDNS_Lock(m);
6415
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);
6420
6421 while (*p)
6422 {
6423 if (*p == set)
6424 {
6425 LogMsg("Error! Tried to register a NetworkInterfaceInfo that's already in the list");
6426 mDNS_Unlock(m);
6427 return(mStatus_AlreadyRegistered);
6428 }
6429
6430 // This InterfaceID is already in the list, so mark this interface inactive for now
6431 if ((*p)->InterfaceID == set->InterfaceID)
6432 {
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;
6437 }
6438
6439 p=&(*p)->next;
6440 }
6441
6442 set->next = mDNSNULL;
6443 *p = set;
6444
6445 if (set->Advertise)
6446 AdvertiseInterface(m, set);
6447
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");
6452
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))
6458 {
6459 DNSQuestion *q;
6460 AuthRecord *rr;
6461 mDNSs32 initial = InitialQuestionInterval;
6462
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);
6467
6468 if (delay)
6469 {
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);
6476 }
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);
6485 }
6486
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)
6491 {
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);
6497 }
6498 }
6499
6500 mDNS_Unlock(m);
6501 return(mStatus_NoError);
6502 }
6503
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)
6508 {
6509 NetworkInterfaceInfo **p = &m->HostInterfaces;
6510
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;
6516
6517 mDNS_Lock(m);
6518
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; }
6522
6523 // Unlink this record from our list
6524 *p = (*p)->next;
6525 set->next = mDNSNULL;
6526
6527 if (!set->InterfaceActive)
6528 {
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);
6534 }
6535 else
6536 {
6537 NetworkInterfaceInfo *intf;
6538 for (intf = m->HostInterfaces; intf; intf = intf->next)
6539 if (intf->InterfaceID == set->InterfaceID)
6540 break;
6541 if (intf)
6542 {
6543 debugf("mDNS_DeregisterInterface: Another representative of InterfaceID %p exists; making it active",
6544 set->InterfaceID);
6545 intf->InterfaceActive = mDNStrue;
6546 UpdateInterfaceProtocols(m, intf);
6547
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)
6552 break;
6553 if (!intf) revalidate = mDNStrue;
6554 }
6555 else
6556 {
6557 mDNSu32 slot;
6558 CacheGroup *cg;
6559 CacheRecord *rr;
6560 DNSQuestion *q;
6561 debugf("mDNS_DeregisterInterface: Last representative of InterfaceID %p deregistered; marking questions etc. dormant",
6562 set->InterfaceID);
6563
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;
6568
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);
6574 }
6575 }
6576
6577 // If we were advertising on this interface, deregister those address and reverse-lookup records now
6578 if (set->Advertise) DeadvertiseInterface(m, set);
6579
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)
6585 {
6586 mDNSu32 slot;
6587 CacheGroup *cg;
6588 CacheRecord *rr;
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);
6593 }
6594
6595 mDNS_Unlock(m);
6596 }
6597
6598 mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6599 {
6600 ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6601 (void)m; // Unused parameter
6602
6603 #if MDNS_DEBUGMSGS
6604 {
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);
6610 }
6611 #endif
6612
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;
6615
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)
6618 {
6619 sr->Conflict = mDNStrue; // Record that this service set had a conflict
6620 mDNS_DeregisterService(m, sr); // Unlink the records from our list
6621 return;
6622 }
6623
6624 if (result == mStatus_MemFree)
6625 {
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.
6628 mDNSu32 i;
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;
6631
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;
6635 }
6636
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);
6641 }
6642
6643 mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
6644 {
6645 ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
6646 if (sr->ServiceCallback)
6647 sr->ServiceCallback(m, sr, result);
6648 }
6649
6650 // Note:
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)
6663 {
6664 mStatus err;
6665 mDNSu32 i;
6666
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;
6675
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));
6678
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);
6684
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;
6689
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);
6698
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);
6701
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;
6707
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++)
6712 {
6713 domainname st;
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;
6722 }
6723
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;
6728
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'; }
6732
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)
6737 {
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);
6741 }
6742 sr->RR_TXT.DependentOn = &sr->RR_SRV;
6743
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)))
6748 {
6749 mStatus status;
6750 mDNS_Lock(m);
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);
6757 mDNS_Unlock(m);
6758 return(status);
6759 }
6760 #endif
6761 mDNS_Lock(m);
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);
6772
6773 mDNS_Unlock(m);
6774
6775 if (err) mDNS_DeregisterService(m, sr);
6776 return(err);
6777 }
6778
6779 mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
6780 ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl)
6781 {
6782 ExtraResourceRecord **e;
6783 mStatus status;
6784
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);
6788
6789 #ifndef UNICAST_DISABLED
6790 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6791 {
6792 mDNS_Lock(m);
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);
6800 mDNS_Unlock(m);
6801 return status;
6802 }
6803 #endif
6804
6805 mDNS_Lock(m);
6806 e = &sr->Extras;
6807 while (*e) e = &(*e)->next;
6808
6809 if (ttl == 0) ttl = kStandardTTL;
6810
6811 extra->r.DependentOn = &sr->RR_SRV;
6812
6813 debugf("mDNS_AddRecordToService adding record to %##s", extra->r.resrec.name->c);
6814
6815 status = mDNS_Register_internal(m, &extra->r);
6816 if (status == mStatus_NoError) *e = extra;
6817 mDNS_Unlock(m);
6818 return(status);
6819 }
6820
6821 mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context)
6822 {
6823 ExtraResourceRecord **e;
6824 mStatus status;
6825
6826 mDNS_Lock(m);
6827 e = &sr->Extras;
6828 while (*e && *e != extra) e = &(*e)->next;
6829 if (!*e)
6830 {
6831 debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c);
6832 status = mStatus_BadReferenceErr;
6833 }
6834 else
6835 {
6836 debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c);
6837 extra->r.RecordCallback = MemFreeCallback;
6838 extra->r.RecordContext = Context;
6839 *e = (*e)->next;
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);
6843 else
6844 #endif
6845 status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal);
6846 }
6847 mDNS_Unlock(m);
6848 return(status);
6849 }
6850
6851 mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname)
6852 {
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;
6859 mStatus err;
6860
6861 DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain);
6862 if (!newname)
6863 {
6864 name2 = name1;
6865 IncrementLabelSuffix(&name2, mDNStrue);
6866 newname = &name2;
6867 }
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;
6870
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);
6875
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)
6880 {
6881 ExtraResourceRecord *e = extras;
6882 extras = extras->next;
6883 err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl);
6884 }
6885
6886 return(err);
6887 }
6888
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)
6893 {
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));
6896
6897 #ifndef UNICAST_DISABLED
6898 if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
6899 {
6900 mStatus status;
6901 mDNS_Lock(m);
6902 status = uDNS_DeregisterService(m, sr);
6903 mDNS_Unlock(m);
6904 return(status);
6905 }
6906 #endif
6907 if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered)
6908 {
6909 debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c);
6910 return(mStatus_BadReferenceErr);
6911 }
6912 else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering)
6913 {
6914 debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c);
6915 return(mStatus_NoError);
6916 }
6917 else
6918 {
6919 mDNSu32 i;
6920 mStatus status;
6921 ExtraResourceRecord *e;
6922 mDNS_Lock(m);
6923 e = sr->Extras;
6924
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);
6929
6930 mDNS_Deregister_internal(m, &sr->RR_ADV, mDNS_Dereg_normal);
6931
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
6934 while (e)
6935 {
6936 mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat);
6937 e = e->next;
6938 }
6939
6940 for (i=0; i<sr->NumSubTypes; i++)
6941 mDNS_Deregister_internal(m, &sr->SubTypes[i], mDNS_Dereg_normal);
6942
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);
6949 mDNS_Unlock(m);
6950 return(status);
6951 }
6952 }
6953
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)
6964 {
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));
6973 }
6974
6975 mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
6976 mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
6977 {
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));
6982 }
6983
6984 // ***************************************************************************
6985 #if COMPILER_LIKES_PRAGMA_MARK
6986 #pragma mark -
6987 #pragma mark -
6988 #pragma mark - Startup and Shutdown
6989 #endif
6990
6991 mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
6992 {
6993 if (storage && numrecords)
6994 {
6995 mDNSu32 i;
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;
7001 }
7002 }
7003
7004 mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
7005 {
7006 mDNS_Lock(m);
7007 mDNS_GrowCache_internal(m, storage, numrecords);
7008 mDNS_Unlock(m);
7009 }
7010
7011 mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
7012 CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
7013 mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
7014 {
7015 mDNSu32 slot;
7016 mDNSs32 timenow, timenow_adjust;
7017 mStatus result;
7018
7019 if (!rrcachestorage) rrcachesize = 0;
7020
7021 m->p = p;
7022 m->KnownBugs = 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;
7031
7032 // For debugging: To catch and report locking failures
7033 m->mDNS_busy = 0;
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;
7039
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;
7045
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;
7057 m->PktNum = 0;
7058 m->SendDeregistrations = mDNSfalse;
7059 m->SendImmediateAnswers = mDNSfalse;
7060 m->SleepState = mDNSfalse;
7061
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;
7073
7074 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++) m->rrcache_hash[slot] = mDNSNULL;
7075
7076 mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
7077
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;
7092
7093 #ifndef UNICAST_DISABLED
7094 uDNS_Init(m);
7095 #endif
7096 result = mDNSPlatformInit(m);
7097
7098 return(result);
7099 }
7100
7101 mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
7102 {
7103 m->mDNSPlatformStatus = result;
7104 if (m->MainCallback)
7105 {
7106 mDNS_Lock(m);
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
7110 mDNS_Unlock(m);
7111 }
7112 }
7113
7114 mDNSexport void mDNS_Close(mDNS *const m)
7115 {
7116 mDNSu32 rrcache_active = 0;
7117 mDNSu32 rrcache_totalused = 0;
7118 mDNSu32 slot;
7119 NetworkInterfaceInfo *intf;
7120 mDNS_Lock(m);
7121
7122 m->mDNS_shutdown = mDNStrue;
7123
7124 #ifndef UNICAST_DISABLED
7125 uDNS_Close(m);
7126 #endif
7127 rrcache_totalused = m->rrcache_totalused;
7128 for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
7129 {
7130 while(m->rrcache_hash[slot])
7131 {
7132 CacheGroup *cg = m->rrcache_hash[slot];
7133 while (cg->members)
7134 {
7135 CacheRecord *rr = cg->members;
7136 cg->members = cg->members->next;
7137 if (rr->CRActiveQuestion) rrcache_active++;
7138 ReleaseCacheRecord(m, rr);
7139 }
7140 cg->rrcache_tail = &cg->members;
7141 ReleaseCacheGroup(m, &m->rrcache_hash[slot]);
7142 }
7143 }
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);
7147
7148 for (intf = m->HostInterfaces; intf; intf = intf->next)
7149 if (intf->Advertise)
7150 DeadvertiseInterface(m, intf);
7151
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)
7156 {
7157 AuthRecord *rr = m->CurrentRecord;
7158 if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
7159 {
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);
7162 }
7163 else
7164 m->CurrentRecord = rr->next;
7165 }
7166
7167 if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records");
7168 else debugf("mDNS_Close: No deregistering records remain");
7169
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));
7174
7175 mDNS_Unlock(m);
7176 debugf("mDNS_Close: mDNSPlatformClose");
7177 mDNSPlatformClose(m);
7178 debugf("mDNS_Close: done");
7179 }